LCOV - differential code coverage report
Current view: top level - src/backend/commands - tablecmds.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 91.2 % 6466 5899 271 171 344 12 146 3554 545 1654 390 3643 10 209
Current Date: 2023-04-08 15:15:32 Functions: 98.9 % 189 187 2 173 14 2 178 9
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  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-2023, PostgreSQL Global Development Group
       7                 :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :  *
       9                 :  *
      10                 :  * IDENTIFICATION
      11                 :  *    src/backend/commands/tablecmds.c
      12                 :  *
      13                 :  *-------------------------------------------------------------------------
      14                 :  */
      15                 : #include "postgres.h"
      16                 : 
      17                 : #include "access/attmap.h"
      18                 : #include "access/genam.h"
      19                 : #include "access/heapam.h"
      20                 : #include "access/heapam_xlog.h"
      21                 : #include "access/multixact.h"
      22                 : #include "access/reloptions.h"
      23                 : #include "access/relscan.h"
      24                 : #include "access/sysattr.h"
      25                 : #include "access/tableam.h"
      26                 : #include "access/toast_compression.h"
      27                 : #include "access/xact.h"
      28                 : #include "access/xlog.h"
      29                 : #include "access/xloginsert.h"
      30                 : #include "catalog/catalog.h"
      31                 : #include "catalog/heap.h"
      32                 : #include "catalog/index.h"
      33                 : #include "catalog/namespace.h"
      34                 : #include "catalog/objectaccess.h"
      35                 : #include "catalog/partition.h"
      36                 : #include "catalog/pg_am.h"
      37                 : #include "catalog/pg_attrdef.h"
      38                 : #include "catalog/pg_collation.h"
      39                 : #include "catalog/pg_constraint.h"
      40                 : #include "catalog/pg_depend.h"
      41                 : #include "catalog/pg_foreign_table.h"
      42                 : #include "catalog/pg_inherits.h"
      43                 : #include "catalog/pg_largeobject.h"
      44                 : #include "catalog/pg_namespace.h"
      45                 : #include "catalog/pg_opclass.h"
      46                 : #include "catalog/pg_statistic_ext.h"
      47                 : #include "catalog/pg_tablespace.h"
      48                 : #include "catalog/pg_trigger.h"
      49                 : #include "catalog/pg_type.h"
      50                 : #include "catalog/storage.h"
      51                 : #include "catalog/storage_xlog.h"
      52                 : #include "catalog/toasting.h"
      53                 : #include "commands/cluster.h"
      54                 : #include "commands/comment.h"
      55                 : #include "commands/defrem.h"
      56                 : #include "commands/event_trigger.h"
      57                 : #include "commands/policy.h"
      58                 : #include "commands/sequence.h"
      59                 : #include "commands/tablecmds.h"
      60                 : #include "commands/tablespace.h"
      61                 : #include "commands/trigger.h"
      62                 : #include "commands/typecmds.h"
      63                 : #include "commands/user.h"
      64                 : #include "executor/executor.h"
      65                 : #include "foreign/fdwapi.h"
      66                 : #include "foreign/foreign.h"
      67                 : #include "miscadmin.h"
      68                 : #include "nodes/makefuncs.h"
      69                 : #include "nodes/nodeFuncs.h"
      70                 : #include "nodes/parsenodes.h"
      71                 : #include "optimizer/optimizer.h"
      72                 : #include "parser/parse_clause.h"
      73                 : #include "parser/parse_coerce.h"
      74                 : #include "parser/parse_collate.h"
      75                 : #include "parser/parse_expr.h"
      76                 : #include "parser/parse_oper.h"
      77                 : #include "parser/parse_relation.h"
      78                 : #include "parser/parse_type.h"
      79                 : #include "parser/parse_utilcmd.h"
      80                 : #include "parser/parser.h"
      81                 : #include "partitioning/partbounds.h"
      82                 : #include "partitioning/partdesc.h"
      83                 : #include "pgstat.h"
      84                 : #include "rewrite/rewriteDefine.h"
      85                 : #include "rewrite/rewriteHandler.h"
      86                 : #include "rewrite/rewriteManip.h"
      87                 : #include "storage/bufmgr.h"
      88                 : #include "storage/lmgr.h"
      89                 : #include "storage/lock.h"
      90                 : #include "storage/predicate.h"
      91                 : #include "storage/smgr.h"
      92                 : #include "tcop/utility.h"
      93                 : #include "utils/acl.h"
      94                 : #include "utils/builtins.h"
      95                 : #include "utils/fmgroids.h"
      96                 : #include "utils/inval.h"
      97                 : #include "utils/lsyscache.h"
      98                 : #include "utils/memutils.h"
      99                 : #include "utils/partcache.h"
     100                 : #include "utils/relcache.h"
     101                 : #include "utils/ruleutils.h"
     102                 : #include "utils/snapmgr.h"
     103                 : #include "utils/syscache.h"
     104                 : #include "utils/timestamp.h"
     105                 : #include "utils/typcache.h"
     106                 : #include "utils/usercontext.h"
     107                 : 
     108                 : /*
     109                 :  * ON COMMIT action list
     110                 :  */
     111                 : typedef struct OnCommitItem
     112                 : {
     113                 :     Oid         relid;          /* relid of relation */
     114                 :     OnCommitAction oncommit;    /* what to do at end of xact */
     115                 : 
     116                 :     /*
     117                 :      * If this entry was created during the current transaction,
     118                 :      * creating_subid is the ID of the creating subxact; if created in a prior
     119                 :      * transaction, creating_subid is zero.  If deleted during the current
     120                 :      * transaction, deleting_subid is the ID of the deleting subxact; if no
     121                 :      * deletion request is pending, deleting_subid is zero.
     122                 :      */
     123                 :     SubTransactionId creating_subid;
     124                 :     SubTransactionId deleting_subid;
     125                 : } OnCommitItem;
     126                 : 
     127                 : static List *on_commits = NIL;
     128                 : 
     129                 : 
     130                 : /*
     131                 :  * State information for ALTER TABLE
     132                 :  *
     133                 :  * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
     134                 :  * structs, one for each table modified by the operation (the named table
     135                 :  * plus any child tables that are affected).  We save lists of subcommands
     136                 :  * to apply to this table (possibly modified by parse transformation steps);
     137                 :  * these lists will be executed in Phase 2.  If a Phase 3 step is needed,
     138                 :  * necessary information is stored in the constraints and newvals lists.
     139                 :  *
     140                 :  * Phase 2 is divided into multiple passes; subcommands are executed in
     141                 :  * a pass determined by subcommand type.
     142                 :  */
     143                 : 
     144                 : #define AT_PASS_UNSET           -1  /* UNSET will cause ERROR */
     145                 : #define AT_PASS_DROP            0   /* DROP (all flavors) */
     146                 : #define AT_PASS_ALTER_TYPE      1   /* ALTER COLUMN TYPE */
     147                 : #define AT_PASS_OLD_INDEX       2   /* re-add existing indexes */
     148                 : #define AT_PASS_OLD_CONSTR      3   /* re-add existing constraints */
     149                 : /* We could support a RENAME COLUMN pass here, but not currently used */
     150                 : #define AT_PASS_ADD_COL         4   /* ADD COLUMN */
     151                 : #define AT_PASS_ADD_CONSTR      5   /* ADD constraints (initial examination) */
     152                 : #define AT_PASS_COL_ATTRS       6   /* set column attributes, eg NOT NULL */
     153                 : #define AT_PASS_ADD_INDEXCONSTR 7   /* ADD index-based constraints */
     154                 : #define AT_PASS_ADD_INDEX       8   /* ADD indexes */
     155                 : #define AT_PASS_ADD_OTHERCONSTR 9   /* ADD other constraints, defaults */
     156                 : #define AT_PASS_MISC            10  /* other stuff */
     157                 : #define AT_NUM_PASSES           11
     158                 : 
     159                 : typedef struct AlteredTableInfo
     160                 : {
     161                 :     /* Information saved before any work commences: */
     162                 :     Oid         relid;          /* Relation to work on */
     163                 :     char        relkind;        /* Its relkind */
     164                 :     TupleDesc   oldDesc;        /* Pre-modification tuple descriptor */
     165                 : 
     166                 :     /*
     167                 :      * Transiently set during Phase 2, normally set to NULL.
     168                 :      *
     169                 :      * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
     170                 :      * returns control.  This can be exploited by ATExecCmd subroutines to
     171                 :      * close/reopen across transaction boundaries.
     172                 :      */
     173                 :     Relation    rel;
     174                 : 
     175                 :     /* Information saved by Phase 1 for Phase 2: */
     176                 :     List       *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
     177                 :     /* Information saved by Phases 1/2 for Phase 3: */
     178                 :     List       *constraints;    /* List of NewConstraint */
     179                 :     List       *newvals;        /* List of NewColumnValue */
     180                 :     List       *afterStmts;     /* List of utility command parsetrees */
     181                 :     bool        verify_new_notnull; /* T if we should recheck NOT NULL */
     182                 :     int         rewrite;        /* Reason for forced rewrite, if any */
     183                 :     Oid         newAccessMethod;    /* new access method; 0 means no change */
     184                 :     Oid         newTableSpace;  /* new tablespace; 0 means no change */
     185                 :     bool        chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
     186                 :     char        newrelpersistence;  /* if above is true */
     187                 :     Expr       *partition_constraint;   /* for attach partition validation */
     188                 :     /* true, if validating default due to some other attach/detach */
     189                 :     bool        validate_default;
     190                 :     /* Objects to rebuild after completing ALTER TYPE operations */
     191                 :     List       *changedConstraintOids;  /* OIDs of constraints to rebuild */
     192                 :     List       *changedConstraintDefs;  /* string definitions of same */
     193                 :     List       *changedIndexOids;   /* OIDs of indexes to rebuild */
     194                 :     List       *changedIndexDefs;   /* string definitions of same */
     195                 :     char       *replicaIdentityIndex;   /* index to reset as REPLICA IDENTITY */
     196                 :     char       *clusterOnIndex; /* index to use for CLUSTER */
     197                 :     List       *changedStatisticsOids;  /* OIDs of statistics to rebuild */
     198                 :     List       *changedStatisticsDefs;  /* string definitions of same */
     199                 : } AlteredTableInfo;
     200                 : 
     201                 : /* Struct describing one new constraint to check in Phase 3 scan */
     202                 : /* Note: new NOT NULL constraints are handled elsewhere */
     203                 : typedef struct NewConstraint
     204                 : {
     205                 :     char       *name;           /* Constraint name, or NULL if none */
     206                 :     ConstrType  contype;        /* CHECK, FOREIGN */
     207                 :     Oid         refrelid;       /* PK rel, if FOREIGN */
     208                 :     Oid         refindid;       /* OID of PK's index, if FOREIGN */
     209                 :     Oid         conid;          /* OID of pg_constraint entry, if FOREIGN */
     210                 :     Node       *qual;           /* Check expr or CONSTR_FOREIGN Constraint */
     211                 :     ExprState  *qualstate;      /* Execution state for CHECK expr */
     212                 : } NewConstraint;
     213                 : 
     214                 : /*
     215                 :  * Struct describing one new column value that needs to be computed during
     216                 :  * Phase 3 copy (this could be either a new column with a non-null default, or
     217                 :  * a column that we're changing the type of).  Columns without such an entry
     218                 :  * are just copied from the old table during ATRewriteTable.  Note that the
     219                 :  * expr is an expression over *old* table values, except when is_generated
     220                 :  * is true; then it is an expression over columns of the *new* tuple.
     221                 :  */
     222                 : typedef struct NewColumnValue
     223                 : {
     224                 :     AttrNumber  attnum;         /* which column */
     225                 :     Expr       *expr;           /* expression to compute */
     226                 :     ExprState  *exprstate;      /* execution state */
     227                 :     bool        is_generated;   /* is it a GENERATED expression? */
     228                 : } NewColumnValue;
     229                 : 
     230                 : /*
     231                 :  * Error-reporting support for RemoveRelations
     232                 :  */
     233                 : struct dropmsgstrings
     234                 : {
     235                 :     char        kind;
     236                 :     int         nonexistent_code;
     237                 :     const char *nonexistent_msg;
     238                 :     const char *skipping_msg;
     239                 :     const char *nota_msg;
     240                 :     const char *drophint_msg;
     241                 : };
     242                 : 
     243                 : static const struct dropmsgstrings dropmsgstringarray[] = {
     244                 :     {RELKIND_RELATION,
     245                 :         ERRCODE_UNDEFINED_TABLE,
     246                 :         gettext_noop("table \"%s\" does not exist"),
     247                 :         gettext_noop("table \"%s\" does not exist, skipping"),
     248                 :         gettext_noop("\"%s\" is not a table"),
     249                 :     gettext_noop("Use DROP TABLE to remove a table.")},
     250                 :     {RELKIND_SEQUENCE,
     251                 :         ERRCODE_UNDEFINED_TABLE,
     252                 :         gettext_noop("sequence \"%s\" does not exist"),
     253                 :         gettext_noop("sequence \"%s\" does not exist, skipping"),
     254                 :         gettext_noop("\"%s\" is not a sequence"),
     255                 :     gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
     256                 :     {RELKIND_VIEW,
     257                 :         ERRCODE_UNDEFINED_TABLE,
     258                 :         gettext_noop("view \"%s\" does not exist"),
     259                 :         gettext_noop("view \"%s\" does not exist, skipping"),
     260                 :         gettext_noop("\"%s\" is not a view"),
     261                 :     gettext_noop("Use DROP VIEW to remove a view.")},
     262                 :     {RELKIND_MATVIEW,
     263                 :         ERRCODE_UNDEFINED_TABLE,
     264                 :         gettext_noop("materialized view \"%s\" does not exist"),
     265                 :         gettext_noop("materialized view \"%s\" does not exist, skipping"),
     266                 :         gettext_noop("\"%s\" is not a materialized view"),
     267                 :     gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
     268                 :     {RELKIND_INDEX,
     269                 :         ERRCODE_UNDEFINED_OBJECT,
     270                 :         gettext_noop("index \"%s\" does not exist"),
     271                 :         gettext_noop("index \"%s\" does not exist, skipping"),
     272                 :         gettext_noop("\"%s\" is not an index"),
     273                 :     gettext_noop("Use DROP INDEX to remove an index.")},
     274                 :     {RELKIND_COMPOSITE_TYPE,
     275                 :         ERRCODE_UNDEFINED_OBJECT,
     276                 :         gettext_noop("type \"%s\" does not exist"),
     277                 :         gettext_noop("type \"%s\" does not exist, skipping"),
     278                 :         gettext_noop("\"%s\" is not a type"),
     279                 :     gettext_noop("Use DROP TYPE to remove a type.")},
     280                 :     {RELKIND_FOREIGN_TABLE,
     281                 :         ERRCODE_UNDEFINED_OBJECT,
     282                 :         gettext_noop("foreign table \"%s\" does not exist"),
     283                 :         gettext_noop("foreign table \"%s\" does not exist, skipping"),
     284                 :         gettext_noop("\"%s\" is not a foreign table"),
     285                 :     gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
     286                 :     {RELKIND_PARTITIONED_TABLE,
     287                 :         ERRCODE_UNDEFINED_TABLE,
     288                 :         gettext_noop("table \"%s\" does not exist"),
     289                 :         gettext_noop("table \"%s\" does not exist, skipping"),
     290                 :         gettext_noop("\"%s\" is not a table"),
     291                 :     gettext_noop("Use DROP TABLE to remove a table.")},
     292                 :     {RELKIND_PARTITIONED_INDEX,
     293                 :         ERRCODE_UNDEFINED_OBJECT,
     294                 :         gettext_noop("index \"%s\" does not exist"),
     295                 :         gettext_noop("index \"%s\" does not exist, skipping"),
     296                 :         gettext_noop("\"%s\" is not an index"),
     297                 :     gettext_noop("Use DROP INDEX to remove an index.")},
     298                 :     {'\0', 0, NULL, NULL, NULL, NULL}
     299                 : };
     300                 : 
     301                 : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
     302                 : struct DropRelationCallbackState
     303                 : {
     304                 :     /* These fields are set by RemoveRelations: */
     305                 :     char        expected_relkind;
     306                 :     LOCKMODE    heap_lockmode;
     307                 :     /* These fields are state to track which subsidiary locks are held: */
     308                 :     Oid         heapOid;
     309                 :     Oid         partParentOid;
     310                 :     /* These fields are passed back by RangeVarCallbackForDropRelation: */
     311                 :     char        actual_relkind;
     312                 :     char        actual_relpersistence;
     313                 : };
     314                 : 
     315                 : /* Alter table target-type flags for ATSimplePermissions */
     316                 : #define     ATT_TABLE               0x0001
     317                 : #define     ATT_VIEW                0x0002
     318                 : #define     ATT_MATVIEW             0x0004
     319                 : #define     ATT_INDEX               0x0008
     320                 : #define     ATT_COMPOSITE_TYPE      0x0010
     321                 : #define     ATT_FOREIGN_TABLE       0x0020
     322                 : #define     ATT_PARTITIONED_INDEX   0x0040
     323                 : #define     ATT_SEQUENCE            0x0080
     324                 : 
     325                 : /*
     326                 :  * ForeignTruncateInfo
     327                 :  *
     328                 :  * Information related to truncation of foreign tables.  This is used for
     329                 :  * the elements in a hash table. It uses the server OID as lookup key,
     330                 :  * and includes a per-server list of all foreign tables involved in the
     331                 :  * truncation.
     332                 :  */
     333                 : typedef struct ForeignTruncateInfo
     334                 : {
     335                 :     Oid         serverid;
     336                 :     List       *rels;
     337                 : } ForeignTruncateInfo;
     338                 : 
     339                 : /*
     340                 :  * Partition tables are expected to be dropped when the parent partitioned
     341                 :  * table gets dropped. Hence for partitioning we use AUTO dependency.
     342                 :  * Otherwise, for regular inheritance use NORMAL dependency.
     343                 :  */
     344                 : #define child_dependency_type(child_is_partition)   \
     345                 :     ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
     346                 : 
     347                 : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
     348                 : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
     349                 : static void truncate_check_activity(Relation rel);
     350                 : static void RangeVarCallbackForTruncate(const RangeVar *relation,
     351                 :                                         Oid relId, Oid oldRelId, void *arg);
     352                 : static List *MergeAttributes(List *schema, List *supers, char relpersistence,
     353                 :                              bool is_partition, List **supconstr,
     354                 :                              List **additional_notnulls);
     355                 : static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
     356                 : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
     357                 : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
     358                 : static void StoreCatalogInheritance(Oid relationId, List *supers,
     359                 :                                     bool child_is_partition);
     360                 : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
     361                 :                                      int32 seqNumber, Relation inhRelation,
     362                 :                                      bool child_is_partition);
     363                 : static int  findAttrByName(const char *attributeName, List *schema);
     364                 : static void AlterIndexNamespaces(Relation classRel, Relation rel,
     365                 :                                  Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
     366                 : static void AlterSeqNamespaces(Relation classRel, Relation rel,
     367                 :                                Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
     368                 :                                LOCKMODE lockmode);
     369                 : static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
     370                 :                                            bool recurse, bool recursing, LOCKMODE lockmode);
     371                 : static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
     372                 :                                      Relation rel, HeapTuple contuple, List **otherrelids,
     373                 :                                      LOCKMODE lockmode);
     374                 : static ObjectAddress ATExecValidateConstraint(List **wqueue,
     375                 :                                               Relation rel, char *constrName,
     376                 :                                               bool recurse, bool recursing, LOCKMODE lockmode);
     377                 : static int  transformColumnNameList(Oid relId, List *colList,
     378                 :                                     int16 *attnums, Oid *atttypids);
     379                 : static int  transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
     380                 :                                        List **attnamelist,
     381                 :                                        int16 *attnums, Oid *atttypids,
     382                 :                                        Oid *opclasses);
     383                 : static Oid  transformFkeyCheckAttrs(Relation pkrel,
     384                 :                                     int numattrs, int16 *attnums,
     385                 :                                     Oid *opclasses);
     386                 : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
     387                 : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
     388                 :                                      Oid *funcid);
     389                 : static void validateForeignKeyConstraint(char *conname,
     390                 :                                          Relation rel, Relation pkrel,
     391                 :                                          Oid pkindOid, Oid constraintOid);
     392                 : static void ATController(AlterTableStmt *parsetree,
     393                 :                          Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
     394                 :                          AlterTableUtilityContext *context);
     395                 : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
     396                 :                       bool recurse, bool recursing, LOCKMODE lockmode,
     397                 :                       AlterTableUtilityContext *context);
     398                 : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
     399                 :                               AlterTableUtilityContext *context);
     400                 : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
     401                 :                       AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,
     402                 :                       AlterTableUtilityContext *context);
     403                 : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
     404                 :                                           Relation rel, AlterTableCmd *cmd,
     405                 :                                           bool recurse, LOCKMODE lockmode,
     406                 :                                           int cur_pass,
     407                 :                                           AlterTableUtilityContext *context);
     408                 : static void ATRewriteTables(AlterTableStmt *parsetree,
     409                 :                             List **wqueue, LOCKMODE lockmode,
     410                 :                             AlterTableUtilityContext *context);
     411                 : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
     412                 : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
     413                 : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
     414                 : static void ATSimpleRecursion(List **wqueue, Relation rel,
     415                 :                               AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
     416                 :                               AlterTableUtilityContext *context);
     417                 : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
     418                 : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
     419                 :                                   LOCKMODE lockmode,
     420                 :                                   AlterTableUtilityContext *context);
     421                 : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
     422                 :                                            DropBehavior behavior);
     423                 : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     424                 :                             bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
     425                 :                             AlterTableUtilityContext *context);
     426                 : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
     427                 :                                      Relation rel, AlterTableCmd **cmd,
     428                 :                                      bool recurse, bool recursing,
     429                 :                                      LOCKMODE lockmode, int cur_pass,
     430                 :                                      AlterTableUtilityContext *context);
     431                 : static bool check_for_column_name_collision(Relation rel, const char *colname,
     432                 :                                             bool if_not_exists);
     433                 : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
     434                 : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
     435                 : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
     436                 :                                        LOCKMODE lockmode);
     437                 : static void set_attnotnull(List **wqueue, Relation rel,
     438                 :                            AttrNumber attnum, bool recurse, LOCKMODE lockmode);
     439                 : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
     440                 :                                       char *constrname, char *colName,
     441                 :                                       bool recurse, bool recursing,
     442                 :                                       List **readyRels, LOCKMODE lockmode);
     443                 : static void ATExecSetAttNotNull(List **wqueue, Relation rel,
     444                 :                                 const char *colName, LOCKMODE lockmode);
     445                 : static void ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
     446                 :                                const char *colName, LOCKMODE lockmode);
     447                 : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
     448                 : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
     449                 :                                              List *testConstraint, List *provenConstraint);
     450                 : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
     451                 :                                          Node *newDefault, LOCKMODE lockmode);
     452                 : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
     453                 :                                                Node *newDefault);
     454                 : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
     455                 :                                        Node *def, LOCKMODE lockmode);
     456                 : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
     457                 :                                        Node *def, LOCKMODE lockmode);
     458                 : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
     459                 : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
     460                 : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
     461                 : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
     462                 :                                          Node *newValue, LOCKMODE lockmode);
     463                 : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
     464                 :                                       Node *options, bool isReset, LOCKMODE lockmode);
     465                 : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
     466                 :                                       Node *newValue, LOCKMODE lockmode);
     467                 : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     468                 :                              AlterTableCmd *cmd, LOCKMODE lockmode,
     469                 :                              AlterTableUtilityContext *context);
     470                 : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
     471                 :                                       DropBehavior behavior,
     472                 :                                       bool recurse, bool recursing,
     473                 :                                       bool missing_ok, LOCKMODE lockmode,
     474                 :                                       ObjectAddresses *addrs);
     475                 : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
     476                 :                                     IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     477                 : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
     478                 :                                          CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     479                 : static ObjectAddress ATExecAddConstraint(List **wqueue,
     480                 :                                          AlteredTableInfo *tab, Relation rel,
     481                 :                                          Constraint *newConstraint, bool recurse, bool is_readd,
     482                 :                                          LOCKMODE lockmode);
     483                 : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
     484                 : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
     485                 :                                               IndexStmt *stmt, LOCKMODE lockmode);
     486                 : static ObjectAddress ATAddCheckConstraint(List **wqueue,
     487                 :                                           AlteredTableInfo *tab, Relation rel,
     488                 :                                           Constraint *constr,
     489                 :                                           bool recurse, bool recursing, bool is_readd,
     490                 :                                           LOCKMODE lockmode);
     491                 : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
     492                 :                                                Relation rel, Constraint *fkconstraint,
     493                 :                                                bool recurse, bool recursing,
     494                 :                                                LOCKMODE lockmode);
     495                 : static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
     496                 :                                             Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     497                 :                                             int numfks, int16 *pkattnum, int16 *fkattnum,
     498                 :                                             Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     499                 :                                             int numfkdelsetcols, int16 *fkdelsetcols,
     500                 :                                             bool old_check_ok,
     501                 :                                             Oid parentDelTrigger, Oid parentUpdTrigger);
     502                 : static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
     503                 :                                          int numfksetcols, const int16 *fksetcolsattnums,
     504                 :                                          List *fksetcols);
     505                 : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
     506                 :                                     Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     507                 :                                     int numfks, int16 *pkattnum, int16 *fkattnum,
     508                 :                                     Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     509                 :                                     int numfkdelsetcols, int16 *fkdelsetcols,
     510                 :                                     bool old_check_ok, LOCKMODE lockmode,
     511                 :                                     Oid parentInsTrigger, Oid parentUpdTrigger);
     512                 : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
     513                 :                                        Relation partitionRel);
     514                 : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
     515                 : static void CloneFkReferencing(List **wqueue, Relation parentRel,
     516                 :                                Relation partRel);
     517                 : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
     518                 :                                           Constraint *fkconstraint, Oid constraintOid,
     519                 :                                           Oid indexOid,
     520                 :                                           Oid parentInsTrigger, Oid parentUpdTrigger,
     521                 :                                           Oid *insertTrigOid, Oid *updateTrigOid);
     522                 : static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
     523                 :                                            Constraint *fkconstraint, Oid constraintOid,
     524                 :                                            Oid indexOid,
     525                 :                                            Oid parentDelTrigger, Oid parentUpdTrigger,
     526                 :                                            Oid *deleteTrigOid, Oid *updateTrigOid);
     527                 : static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
     528                 :                                          Oid partRelid,
     529                 :                                          Oid parentConstrOid, int numfks,
     530                 :                                          AttrNumber *mapped_conkey, AttrNumber *confkey,
     531                 :                                          Oid *conpfeqop,
     532                 :                                          Oid parentInsTrigger,
     533                 :                                          Oid parentUpdTrigger,
     534                 :                                          Relation trigrel);
     535                 : static void GetForeignKeyActionTriggers(Relation trigrel,
     536                 :                                         Oid conoid, Oid confrelid, Oid conrelid,
     537                 :                                         Oid *deleteTriggerOid,
     538                 :                                         Oid *updateTriggerOid);
     539                 : static void GetForeignKeyCheckTriggers(Relation trigrel,
     540                 :                                        Oid conoid, Oid confrelid, Oid conrelid,
     541                 :                                        Oid *insertTriggerOid,
     542                 :                                        Oid *updateTriggerOid);
     543                 : static void ATExecDropConstraint(Relation rel, const char *constrName,
     544                 :                                  DropBehavior behavior,
     545                 :                                  bool recurse, bool recursing,
     546                 :                                  bool missing_ok, LOCKMODE lockmode);
     547                 : static ObjectAddress dropconstraint_internal(Relation rel,
     548                 :                                              HeapTuple constraintTup, DropBehavior behavior,
     549                 :                                              bool recurse, bool recursing,
     550                 :                                              bool missing_ok, List **readyRels,
     551                 :                                              LOCKMODE lockmode);
     552                 : static void ATPrepAlterColumnType(List **wqueue,
     553                 :                                   AlteredTableInfo *tab, Relation rel,
     554                 :                                   bool recurse, bool recursing,
     555                 :                                   AlterTableCmd *cmd, LOCKMODE lockmode,
     556                 :                                   AlterTableUtilityContext *context);
     557                 : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
     558                 : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
     559                 :                                            AlterTableCmd *cmd, LOCKMODE lockmode);
     560                 : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
     561                 : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
     562                 : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
     563                 : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
     564                 :                                    LOCKMODE lockmode);
     565                 : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
     566                 :                                  char *cmd, List **wqueue, LOCKMODE lockmode,
     567                 :                                  bool rewrite);
     568                 : static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
     569                 :                                      Oid objid, Relation rel, List *domname,
     570                 :                                      const char *conname);
     571                 : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
     572                 : static void TryReuseForeignKey(Oid oldId, Constraint *con);
     573                 : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
     574                 :                                                      List *options, LOCKMODE lockmode);
     575                 : static void change_owner_fix_column_acls(Oid relationOid,
     576                 :                                          Oid oldOwnerId, Oid newOwnerId);
     577                 : static void change_owner_recurse_to_sequences(Oid relationOid,
     578                 :                                               Oid newOwnerId, LOCKMODE lockmode);
     579                 : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
     580                 :                                      LOCKMODE lockmode);
     581                 : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
     582                 : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
     583                 : static bool ATPrepChangePersistence(Relation rel, bool toLogged);
     584                 : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
     585                 :                                 const char *tablespacename, LOCKMODE lockmode);
     586                 : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
     587                 : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
     588                 : static void ATExecSetRelOptions(Relation rel, List *defList,
     589                 :                                 AlterTableType operation,
     590                 :                                 LOCKMODE lockmode);
     591                 : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
     592                 :                                        char fires_when, bool skip_system, bool recurse,
     593                 :                                        LOCKMODE lockmode);
     594                 : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
     595                 :                                     char fires_when, LOCKMODE lockmode);
     596                 : static void ATPrepAddInherit(Relation child_rel);
     597                 : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
     598                 : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
     599                 : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
     600                 :                                    DependencyType deptype);
     601                 : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
     602                 : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
     603                 : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
     604                 : static void ATExecGenericOptions(Relation rel, List *options);
     605                 : static void ATExecSetRowSecurity(Relation rel, bool rls);
     606                 : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
     607                 : static ObjectAddress ATExecSetCompression(Relation rel,
     608                 :                                           const char *column, Node *newValue, LOCKMODE lockmode);
     609                 : 
     610                 : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
     611                 : static const char *storage_name(char c);
     612                 : 
     613                 : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
     614                 :                                             Oid oldRelOid, void *arg);
     615                 : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
     616                 :                                              Oid oldrelid, void *arg);
     617                 : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
     618                 : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
     619                 :                                   List **partexprs, Oid *partopclass, Oid *partcollation,
     620                 :                                   PartitionStrategy strategy);
     621                 : static void CreateInheritance(Relation child_rel, Relation parent_rel);
     622                 : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
     623                 :                               bool expect_detached);
     624                 : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
     625                 :                                            PartitionCmd *cmd,
     626                 :                                            AlterTableUtilityContext *context);
     627                 : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
     628                 : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
     629                 :                                                List *partConstraint,
     630                 :                                                bool validate_default);
     631                 : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
     632                 : static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
     633                 : static void DropClonedTriggersFromPartition(Oid partitionId);
     634                 : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
     635                 :                                            Relation rel, RangeVar *name,
     636                 :                                            bool concurrent);
     637                 : static void DetachPartitionFinalize(Relation rel, Relation partRel,
     638                 :                                     bool concurrent, Oid defaultPartOid);
     639                 : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
     640                 : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
     641                 :                                               RangeVar *name);
     642                 : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
     643                 : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
     644                 :                                   Relation partitionTbl);
     645                 : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
     646                 : static List *GetParentedForeignKeyRefs(Relation partition);
     647                 : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
     648                 : static char GetAttributeCompression(Oid atttypid, char *compression);
     649                 : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
     650                 : 
     651                 : 
     652                 : /* ----------------------------------------------------------------
     653                 :  *      DefineRelation
     654                 :  *              Creates a new relation.
     655                 :  *
     656                 :  * stmt carries parsetree information from an ordinary CREATE TABLE statement.
     657                 :  * The other arguments are used to extend the behavior for other cases:
     658                 :  * relkind: relkind to assign to the new relation
     659                 :  * ownerId: if not InvalidOid, use this as the new relation's owner.
     660                 :  * typaddress: if not null, it's set to the pg_type entry's address.
     661                 :  * queryString: for error reporting
     662                 :  *
     663                 :  * Note that permissions checks are done against current user regardless of
     664                 :  * ownerId.  A nonzero ownerId is used when someone is creating a relation
     665                 :  * "on behalf of" someone else, so we still want to see that the current user
     666                 :  * has permissions to do it.
     667                 :  *
     668                 :  * If successful, returns the address of the new relation.
     669                 :  * ----------------------------------------------------------------
     670                 :  */
     671                 : ObjectAddress
     672 GIC       62952 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
     673                 :                ObjectAddress *typaddress, const char *queryString)
     674                 : {
     675                 :     char        relname[NAMEDATALEN];
     676                 :     Oid         namespaceId;
     677                 :     Oid         relationId;
     678                 :     Oid         tablespaceId;
     679                 :     Relation    rel;
     680                 :     TupleDesc   descriptor;
     681                 :     List       *inheritOids;
     682                 :     List       *old_constraints;
     683                 :     List       *old_notnulls;
     684                 :     List       *rawDefaults;
     685 ECB             :     List       *cookedDefaults;
     686                 :     List       *nncols;
     687                 :     Datum       reloptions;
     688                 :     ListCell   *listptr;
     689                 :     AttrNumber  attnum;
     690                 :     bool        partitioned;
     691                 :     static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
     692                 :     Oid         ofTypeId;
     693                 :     ObjectAddress address;
     694                 :     LOCKMODE    parentLockmode;
     695 GIC       62952 :     const char *accessMethod = NULL;
     696           62952 :     Oid         accessMethodId = InvalidOid;
     697                 : 
     698                 :     /*
     699                 :      * Truncate relname to appropriate length (probably a waste of time, as
     700                 :      * parser should have done this already).
     701                 :      */
     702           62952 :     strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
     703                 : 
     704                 :     /*
     705                 :      * Check consistency of arguments
     706                 :      */
     707           62952 :     if (stmt->oncommit != ONCOMMIT_NOOP
     708              86 :         && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
     709 CBC           6 :         ereport(ERROR,
     710 ECB             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     711                 :                  errmsg("ON COMMIT can only be used on temporary tables")));
     712                 : 
     713 GIC       62946 :     if (stmt->partspec != NULL)
     714                 :     {
     715            2178 :         if (relkind != RELKIND_RELATION)
     716 LBC           0 :             elog(ERROR, "unexpected relkind: %d", (int) relkind);
     717                 : 
     718 GIC        2178 :         relkind = RELKIND_PARTITIONED_TABLE;
     719            2178 :         partitioned = true;
     720                 :     }
     721 ECB             :     else
     722 CBC       60768 :         partitioned = false;
     723 ECB             : 
     724                 :     /*
     725                 :      * Look up the namespace in which we are supposed to create the relation,
     726                 :      * check we have permission to create there, lock it against concurrent
     727                 :      * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
     728                 :      * namespace is selected.
     729                 :      */
     730 EUB             :     namespaceId =
     731 GIC       62946 :         RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
     732 ECB             : 
     733                 :     /*
     734                 :      * Security check: disallow creating temp tables from security-restricted
     735                 :      * code.  This is needed because calling code might not expect untrusted
     736                 :      * tables to appear in pg_temp at the front of its search path.
     737                 :      */
     738 GIC       62946 :     if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
     739            1439 :         && InSecurityRestrictedOperation())
     740 UIC           0 :         ereport(ERROR,
     741                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     742                 :                  errmsg("cannot create temporary table within security-restricted operation")));
     743                 : 
     744                 :     /*
     745 ECB             :      * Determine the lockmode to use when scanning parents.  A self-exclusive
     746                 :      * lock is needed here.
     747                 :      *
     748                 :      * For regular inheritance, if two backends attempt to add children to the
     749                 :      * same parent simultaneously, and that parent has no pre-existing
     750                 :      * children, then both will attempt to update the parent's relhassubclass
     751                 :      * field, leading to a "tuple concurrently updated" error.  Also, this
     752                 :      * interlocks against a concurrent ANALYZE on the parent table, which
     753                 :      * might otherwise be attempting to clear the parent's relhassubclass
     754 EUB             :      * field, if its previous children were recently dropped.
     755                 :      *
     756                 :      * If the child table is a partition, then we instead grab an exclusive
     757                 :      * lock on the parent because its partition descriptor will be changed by
     758                 :      * addition of the new partition.
     759                 :      */
     760 GIC       62946 :     parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
     761                 :                       ShareUpdateExclusiveLock);
     762                 : 
     763                 :     /* Determine the list of OIDs of the parents. */
     764           62946 :     inheritOids = NIL;
     765           67430 :     foreach(listptr, stmt->inhRelations)
     766                 :     {
     767            4484 :         RangeVar   *rv = (RangeVar *) lfirst(listptr);
     768                 :         Oid         parentOid;
     769                 : 
     770            4484 :         parentOid = RangeVarGetRelid(rv, parentLockmode, false);
     771                 : 
     772                 :         /*
     773                 :          * Reject duplications in the list of parents.
     774 ECB             :          */
     775 GIC        4484 :         if (list_member_oid(inheritOids, parentOid))
     776 UIC           0 :             ereport(ERROR,
     777                 :                     (errcode(ERRCODE_DUPLICATE_TABLE),
     778 ECB             :                      errmsg("relation \"%s\" would be inherited from more than once",
     779                 :                             get_rel_name(parentOid))));
     780                 : 
     781 CBC        4484 :         inheritOids = lappend_oid(inheritOids, parentOid);
     782                 :     }
     783                 : 
     784 ECB             :     /*
     785                 :      * Select tablespace to use: an explicitly indicated one, or (in the case
     786                 :      * of a partitioned table) the parent's, if it has one.
     787                 :      */
     788 GIC       62946 :     if (stmt->tablespacename)
     789 ECB             :     {
     790 GBC          52 :         tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
     791                 : 
     792 GIC          49 :         if (partitioned && tablespaceId == MyDatabaseTableSpace)
     793               3 :             ereport(ERROR,
     794                 :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     795 ECB             :                      errmsg("cannot specify default tablespace for partitioned relations")));
     796                 :     }
     797 GIC       62894 :     else if (stmt->partbound)
     798                 :     {
     799                 :         /*
     800                 :          * For partitions, when no other tablespace is specified, we default
     801                 :          * the tablespace to the parent partitioned table's.
     802 ECB             :          */
     803 GIC        3608 :         Assert(list_length(inheritOids) == 1);
     804 CBC        3608 :         tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
     805                 :     }
     806 ECB             :     else
     807 CBC       59286 :         tablespaceId = InvalidOid;
     808                 : 
     809                 :     /* still nothing? use the default */
     810 GIC       62940 :     if (!OidIsValid(tablespaceId))
     811 CBC       62883 :         tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
     812                 :                                             partitioned);
     813                 : 
     814                 :     /* Check permissions except when using database's default */
     815 GIC       62937 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
     816                 :     {
     817 ECB             :         AclResult   aclresult;
     818                 : 
     819 GNC          66 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
     820                 :                                            ACL_CREATE);
     821 CBC          66 :         if (aclresult != ACLCHECK_OK)
     822 GIC           3 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
     823               3 :                            get_tablespace_name(tablespaceId));
     824 ECB             :     }
     825                 : 
     826                 :     /* In all cases disallow placing user relations in pg_global */
     827 GIC       62934 :     if (tablespaceId == GLOBALTABLESPACE_OID)
     828               9 :         ereport(ERROR,
     829 ECB             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     830                 :                  errmsg("only shared relations can be placed in pg_global tablespace")));
     831                 : 
     832                 :     /* Identify user ID that will own the table */
     833 CBC       62925 :     if (!OidIsValid(ownerId))
     834 GIC       62843 :         ownerId = GetUserId();
     835 ECB             : 
     836                 :     /*
     837                 :      * Parse and validate reloptions, if any.
     838                 :      */
     839 GIC       62925 :     reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
     840                 :                                      true, false);
     841 ECB             : 
     842 CBC       62916 :     switch (relkind)
     843                 :     {
     844 GIC       43940 :         case RELKIND_VIEW:
     845           43940 :             (void) view_reloptions(reloptions, true);
     846           43931 :             break;
     847 CBC        2169 :         case RELKIND_PARTITIONED_TABLE:
     848            2169 :             (void) partitioned_table_reloptions(reloptions, true);
     849 GIC        2166 :             break;
     850           16807 :         default:
     851           16807 :             (void) heap_reloptions(relkind, reloptions, true);
     852                 :     }
     853 ECB             : 
     854 GIC       62856 :     if (stmt->ofTypename)
     855                 :     {
     856 ECB             :         AclResult   aclresult;
     857                 : 
     858 CBC          43 :         ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
     859 ECB             : 
     860 GNC          43 :         aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
     861 CBC          43 :         if (aclresult != ACLCHECK_OK)
     862               3 :             aclcheck_error_type(aclresult, ofTypeId);
     863 ECB             :     }
     864                 :     else
     865 CBC       62813 :         ofTypeId = InvalidOid;
     866                 : 
     867                 :     /*
     868 ECB             :      * Look up inheritance ancestors and generate relation schema, including
     869                 :      * inherited attributes.  (Note that stmt->tableElts is destructively
     870                 :      * modified by MergeAttributes.)
     871                 :      */
     872 CBC       62769 :     stmt->tableElts =
     873 GIC       62853 :         MergeAttributes(stmt->tableElts, inheritOids,
     874 CBC       62853 :                         stmt->relation->relpersistence,
     875           62853 :                         stmt->partbound != NULL,
     876                 :                         &old_constraints, &old_notnulls);
     877                 : 
     878                 :     /*
     879 ECB             :      * Create a tuple descriptor from the relation schema.  Note that this
     880                 :      * deals with column names, types, and in-descriptor NOT NULL flags, but
     881                 :      * not default values, NOT NULL or CHECK constraints; we handle those
     882                 :      * below.
     883                 :      */
     884 GIC       62769 :     descriptor = BuildDescForRelation(stmt->tableElts);
     885                 : 
     886                 :     /*
     887 ECB             :      * Find columns with default values and prepare for insertion of the
     888                 :      * defaults.  Pre-cooked (that is, inherited) defaults go into a list of
     889                 :      * CookedConstraint structs that we'll pass to heap_create_with_catalog,
     890                 :      * while raw defaults go into a list of RawColumnDefault structs that will
     891                 :      * be processed by AddRelationNewConstraints.  (We can't deal with raw
     892                 :      * expressions until we can do transformExpr.)
     893                 :      *
     894                 :      * We can set the atthasdef flags now in the tuple descriptor; this just
     895                 :      * saves StoreAttrDefault from having to do an immediate update of the
     896                 :      * pg_attribute rows.
     897                 :      */
     898 GIC       62757 :     rawDefaults = NIL;
     899 CBC       62757 :     cookedDefaults = NIL;
     900 GIC       62757 :     attnum = 0;
     901                 : 
     902          549553 :     foreach(listptr, stmt->tableElts)
     903                 :     {
     904          486805 :         ColumnDef  *colDef = lfirst(listptr);
     905                 :         Form_pg_attribute attr;
     906                 : 
     907          486805 :         attnum++;
     908          486805 :         attr = TupleDescAttr(descriptor, attnum - 1);
     909                 : 
     910          486805 :         if (colDef->raw_default != NULL)
     911                 :         {
     912                 :             RawColumnDefault *rawEnt;
     913 ECB             : 
     914 CBC        1126 :             Assert(colDef->cooked_default == NULL);
     915 ECB             : 
     916 GIC        1126 :             rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
     917 CBC        1126 :             rawEnt->attnum = attnum;
     918 GIC        1126 :             rawEnt->raw_default = colDef->raw_default;
     919 CBC        1126 :             rawEnt->missingMode = false;
     920 GIC        1126 :             rawEnt->generated = colDef->generated;
     921            1126 :             rawDefaults = lappend(rawDefaults, rawEnt);
     922 CBC        1126 :             attr->atthasdef = true;
     923 ECB             :         }
     924 GIC      485679 :         else if (colDef->cooked_default != NULL)
     925 ECB             :         {
     926                 :             CookedConstraint *cooked;
     927                 : 
     928 GIC         160 :             cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
     929 CBC         160 :             cooked->contype = CONSTR_DEFAULT;
     930 GIC         160 :             cooked->conoid = InvalidOid; /* until created */
     931 CBC         160 :             cooked->name = NULL;
     932             160 :             cooked->attnum = attnum;
     933             160 :             cooked->expr = colDef->cooked_default;
     934             160 :             cooked->skip_validation = false;
     935             160 :             cooked->is_local = true; /* not used for defaults */
     936             160 :             cooked->inhcount = 0;    /* ditto */
     937             160 :             cooked->is_no_inherit = false;
     938 GIC         160 :             cookedDefaults = lappend(cookedDefaults, cooked);
     939 CBC         160 :             attr->atthasdef = true;
     940                 :         }
     941                 : 
     942 GIC      486805 :         if (colDef->identity)
     943 CBC          66 :             attr->attidentity = colDef->identity;
     944 ECB             : 
     945 CBC      486805 :         if (colDef->generated)
     946             445 :             attr->attgenerated = colDef->generated;
     947 ECB             : 
     948 CBC      486805 :         if (colDef->compression)
     949              41 :             attr->attcompression = GetAttributeCompression(attr->atttypid,
     950 ECB             :                                                            colDef->compression);
     951                 : 
     952 GNC      486799 :         if (colDef->storage_name)
     953               6 :             attr->attstorage = GetAttributeStorage(attr->atttypid, colDef->storage_name);
     954 ECB             :     }
     955                 : 
     956                 :     /*
     957                 :      * If the statement hasn't specified an access method, but we're defining
     958                 :      * a type of relation that needs one, use the default.
     959                 :      */
     960 CBC       62748 :     if (stmt->accessMethod != NULL)
     961 ECB             :     {
     962 GIC          57 :         accessMethod = stmt->accessMethod;
     963 ECB             : 
     964 CBC          57 :         if (partitioned)
     965 GIC           3 :             ereport(ERROR,
     966 ECB             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     967                 :                      errmsg("specifying a table access method is not supported on a partitioned table")));
     968                 :     }
     969 GIC       62691 :     else if (RELKIND_HAS_TABLE_AM(relkind))
     970 CBC       15335 :         accessMethod = default_table_access_method;
     971 ECB             : 
     972                 :     /* look up the access method, verify it is for a table */
     973 GIC       62745 :     if (accessMethod != NULL)
     974           15389 :         accessMethodId = get_table_am_oid(accessMethod, false);
     975                 : 
     976                 :     /*
     977                 :      * Create the relation.  Inherited defaults and constraints are passed in
     978 ECB             :      * for immediate handling --- since they don't need parsing, they can be
     979                 :      * stored immediately.
     980                 :      */
     981 GIC       62736 :     relationId = heap_create_with_catalog(relname,
     982 ECB             :                                           namespaceId,
     983                 :                                           tablespaceId,
     984                 :                                           InvalidOid,
     985                 :                                           InvalidOid,
     986                 :                                           ofTypeId,
     987                 :                                           ownerId,
     988                 :                                           accessMethodId,
     989                 :                                           descriptor,
     990                 :                                           list_concat(cookedDefaults,
     991                 :                                                       old_constraints),
     992                 :                                           relkind,
     993 GIC       62736 :                                           stmt->relation->relpersistence,
     994                 :                                           false,
     995                 :                                           false,
     996                 :                                           stmt->oncommit,
     997                 :                                           reloptions,
     998                 :                                           true,
     999 ECB             :                                           allowSystemTableMods,
    1000                 :                                           false,
    1001                 :                                           InvalidOid,
    1002                 :                                           typaddress);
    1003                 : 
    1004                 :     /*
    1005                 :      * We must bump the command counter to make the newly-created relation
    1006                 :      * tuple visible for opening.
    1007                 :      */
    1008 GIC       62724 :     CommandCounterIncrement();
    1009                 : 
    1010                 :     /*
    1011 ECB             :      * Open the new relation and acquire exclusive lock on it.  This isn't
    1012                 :      * really necessary for locking out other backends (since they can't see
    1013                 :      * the new rel anyway until we commit), but it keeps the lock manager from
    1014                 :      * complaining about deadlock risks.
    1015                 :      */
    1016 GIC       62724 :     rel = relation_open(relationId, AccessExclusiveLock);
    1017                 : 
    1018                 :     /*
    1019                 :      * Now add any newly specified column default and generation expressions
    1020                 :      * to the new relation.  These are passed to us in the form of raw
    1021                 :      * parsetrees; we need to transform them to executable expression trees
    1022                 :      * before they can be added. The most convenient way to do that is to
    1023                 :      * apply the parser's transformExpr routine, but transformExpr doesn't
    1024                 :      * work unless we have a pre-existing relation. So, the transformation has
    1025                 :      * to be postponed to this final step of CREATE TABLE.
    1026 ECB             :      *
    1027                 :      * This needs to be before processing the partitioning clauses because
    1028                 :      * those could refer to generated columns.
    1029                 :      */
    1030 GIC       62724 :     if (rawDefaults)
    1031             950 :         AddRelationNewConstraints(rel, rawDefaults, NIL,
    1032                 :                                   true, true, false, queryString);
    1033                 : 
    1034 ECB             :     /*
    1035                 :      * Make column generation expressions visible for use by partitioning.
    1036                 :      */
    1037 GIC       62673 :     CommandCounterIncrement();
    1038                 : 
    1039                 :     /* Process and store partition bound, if any. */
    1040           62673 :     if (stmt->partbound)
    1041                 :     {
    1042                 :         PartitionBoundSpec *bound;
    1043                 :         ParseState *pstate;
    1044            3581 :         Oid         parentId = linitial_oid(inheritOids),
    1045                 :                     defaultPartOid;
    1046                 :         Relation    parent,
    1047            3581 :                     defaultRel = NULL;
    1048 ECB             :         ParseNamespaceItem *nsitem;
    1049                 : 
    1050                 :         /* Already have strong enough lock on the parent */
    1051 GIC        3581 :         parent = table_open(parentId, NoLock);
    1052                 : 
    1053                 :         /*
    1054                 :          * We are going to try to validate the partition bound specification
    1055 ECB             :          * against the partition key of parentRel, so it better have one.
    1056                 :          */
    1057 GIC        3581 :         if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    1058 CBC           6 :             ereport(ERROR,
    1059                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1060                 :                      errmsg("\"%s\" is not partitioned",
    1061                 :                             RelationGetRelationName(parent))));
    1062 ECB             : 
    1063                 :         /*
    1064                 :          * The partition constraint of the default partition depends on the
    1065                 :          * partition bounds of every other partition. It is possible that
    1066                 :          * another backend might be about to execute a query on the default
    1067                 :          * partition table, and that the query relies on previously cached
    1068                 :          * default partition constraints. We must therefore take a table lock
    1069                 :          * strong enough to prevent all queries on the default partition from
    1070                 :          * proceeding until we commit and send out a shared-cache-inval notice
    1071                 :          * that will make them update their index lists.
    1072                 :          *
    1073                 :          * Order of locking: The relation being added won't be visible to
    1074                 :          * other backends until it is committed, hence here in
    1075                 :          * DefineRelation() the order of locking the default partition and the
    1076                 :          * relation being added does not matter. But at all other places we
    1077                 :          * need to lock the default relation before we lock the relation being
    1078                 :          * added or removed i.e. we should take the lock in same order at all
    1079                 :          * the places such that lock parent, lock default partition and then
    1080                 :          * lock the partition so as to avoid a deadlock.
    1081                 :          */
    1082                 :         defaultPartOid =
    1083 GIC        3575 :             get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
    1084                 :                                                                    true));
    1085            3575 :         if (OidIsValid(defaultPartOid))
    1086             177 :             defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
    1087                 : 
    1088                 :         /* Transform the bound values */
    1089            3575 :         pstate = make_parsestate(NULL);
    1090            3575 :         pstate->p_sourcetext = queryString;
    1091                 : 
    1092                 :         /*
    1093                 :          * Add an nsitem containing this relation, so that transformExpr
    1094                 :          * called on partition bound expressions is able to report errors
    1095                 :          * using a proper context.
    1096                 :          */
    1097            3575 :         nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
    1098                 :                                                NULL, false, false);
    1099            3575 :         addNSItemToQuery(pstate, nsitem, false, true, true);
    1100                 : 
    1101 CBC        3575 :         bound = transformPartitionBound(pstate, parent, stmt->partbound);
    1102                 : 
    1103 ECB             :         /*
    1104                 :          * Check first that the new partition's bound is valid and does not
    1105                 :          * overlap with any of existing partitions of the parent.
    1106                 :          */
    1107 CBC        3473 :         check_new_partition_bound(relname, parent, bound, pstate);
    1108 ECB             : 
    1109                 :         /*
    1110                 :          * If the default partition exists, its partition constraints will
    1111                 :          * change after the addition of this new partition such that it won't
    1112                 :          * allow any row that qualifies for this new partition. So, check that
    1113                 :          * the existing data in the default partition satisfies the constraint
    1114                 :          * as it will exist after adding this partition.
    1115                 :          */
    1116 GIC        3416 :         if (OidIsValid(defaultPartOid))
    1117 ECB             :         {
    1118 GIC         162 :             check_default_partition_contents(parent, defaultRel, bound);
    1119 ECB             :             /* Keep the lock until commit. */
    1120 GIC         153 :             table_close(defaultRel, NoLock);
    1121                 :         }
    1122                 : 
    1123                 :         /* Update the pg_class entry. */
    1124            3407 :         StorePartitionBound(rel, parent, bound);
    1125 ECB             : 
    1126 GIC        3407 :         table_close(parent, NoLock);
    1127                 :     }
    1128                 : 
    1129                 :     /* Store inheritance information for new rel. */
    1130           62499 :     StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
    1131                 : 
    1132                 :     /*
    1133                 :      * Process the partitioning specification (if any) and store the partition
    1134 ECB             :      * key information into the catalog.
    1135                 :      */
    1136 CBC       62499 :     if (partitioned)
    1137                 :     {
    1138 ECB             :         ParseState *pstate;
    1139                 :         int         partnatts;
    1140                 :         AttrNumber  partattrs[PARTITION_MAX_KEYS];
    1141                 :         Oid         partopclass[PARTITION_MAX_KEYS];
    1142                 :         Oid         partcollation[PARTITION_MAX_KEYS];
    1143 CBC        2163 :         List       *partexprs = NIL;
    1144                 : 
    1145 GIC        2163 :         pstate = make_parsestate(NULL);
    1146            2163 :         pstate->p_sourcetext = queryString;
    1147 ECB             : 
    1148 GIC        2163 :         partnatts = list_length(stmt->partspec->partParams);
    1149                 : 
    1150                 :         /* Protect fixed-size arrays here and in executor */
    1151            2163 :         if (partnatts > PARTITION_MAX_KEYS)
    1152 UIC           0 :             ereport(ERROR,
    1153 ECB             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    1154                 :                      errmsg("cannot partition using more than %d columns",
    1155                 :                             PARTITION_MAX_KEYS)));
    1156                 : 
    1157                 :         /*
    1158                 :          * We need to transform the raw parsetrees corresponding to partition
    1159                 :          * expressions into executable expression trees.  Like column defaults
    1160                 :          * and CHECK constraints, we could not have done the transformation
    1161                 :          * earlier.
    1162                 :          */
    1163 GNC        2163 :         stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
    1164 ECB             : 
    1165 GIC        2148 :         ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
    1166                 :                               partattrs, &partexprs, partopclass,
    1167 GNC        2148 :                               partcollation, stmt->partspec->strategy);
    1168 EUB             : 
    1169 GNC        2106 :         StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
    1170                 :                           partexprs,
    1171                 :                           partopclass, partcollation);
    1172                 : 
    1173                 :         /* make it all visible */
    1174 GIC        2106 :         CommandCounterIncrement();
    1175                 :     }
    1176                 : 
    1177                 :     /*
    1178                 :      * If we're creating a partition, create now all the indexes, triggers,
    1179                 :      * FKs defined in the parent.
    1180 ECB             :      *
    1181                 :      * We can't do it earlier, because DefineIndex wants to know the partition
    1182                 :      * key which we just stored.
    1183                 :      */
    1184 CBC       62442 :     if (stmt->partbound)
    1185                 :     {
    1186            3404 :         Oid         parentId = linitial_oid(inheritOids);
    1187                 :         Relation    parent;
    1188                 :         List       *idxlist;
    1189                 :         ListCell   *cell;
    1190                 : 
    1191 ECB             :         /* Already have strong enough lock on the parent */
    1192 GIC        3404 :         parent = table_open(parentId, NoLock);
    1193            3404 :         idxlist = RelationGetIndexList(parent);
    1194                 : 
    1195                 :         /*
    1196                 :          * For each index in the parent table, create one in the partition
    1197                 :          */
    1198            4006 :         foreach(cell, idxlist)
    1199                 :         {
    1200             611 :             Relation    idxRel = index_open(lfirst_oid(cell), AccessShareLock);
    1201 ECB             :             AttrMap    *attmap;
    1202                 :             IndexStmt  *idxstmt;
    1203                 :             Oid         constraintOid;
    1204                 : 
    1205 GIC         611 :             if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    1206                 :             {
    1207              18 :                 if (idxRel->rd_index->indisunique)
    1208               6 :                     ereport(ERROR,
    1209 ECB             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1210                 :                              errmsg("cannot create foreign partition of partitioned table \"%s\"",
    1211                 :                                     RelationGetRelationName(parent)),
    1212                 :                              errdetail("Table \"%s\" contains indexes that are unique.",
    1213                 :                                        RelationGetRelationName(parent))));
    1214                 :                 else
    1215                 :                 {
    1216 GIC          12 :                     index_close(idxRel, AccessShareLock);
    1217 CBC          12 :                     continue;
    1218                 :                 }
    1219                 :             }
    1220                 : 
    1221 GIC         593 :             attmap = build_attrmap_by_name(RelationGetDescr(rel),
    1222                 :                                            RelationGetDescr(parent),
    1223                 :                                            false);
    1224                 :             idxstmt =
    1225 CBC         593 :                 generateClonedIndexStmt(NULL, idxRel,
    1226 ECB             :                                         attmap, &constraintOid);
    1227 GIC         593 :             DefineIndex(RelationGetRelid(rel),
    1228                 :                         idxstmt,
    1229                 :                         InvalidOid,
    1230                 :                         RelationGetRelid(idxRel),
    1231                 :                         constraintOid,
    1232                 :                         -1,
    1233                 :                         false, false, false, false, false);
    1234                 : 
    1235 CBC         590 :             index_close(idxRel, AccessShareLock);
    1236 ECB             :         }
    1237                 : 
    1238 GIC        3395 :         list_free(idxlist);
    1239                 : 
    1240 ECB             :         /*
    1241                 :          * If there are any row-level triggers, clone them to the new
    1242                 :          * partition.
    1243                 :          */
    1244 CBC        3395 :         if (parent->trigdesc != NULL)
    1245 GIC         188 :             CloneRowTriggersToPartition(parent, rel);
    1246 ECB             : 
    1247                 :         /*
    1248                 :          * And foreign keys too.  Note that because we're freshly creating the
    1249                 :          * table, there is no need to verify these new constraints.
    1250                 :          */
    1251 GIC        3395 :         CloneForeignKeyConstraints(NULL, parent, rel);
    1252                 : 
    1253            3395 :         table_close(parent, NoLock);
    1254 ECB             :     }
    1255                 : 
    1256                 :     /*
    1257                 :      * Now add any newly specified CHECK constraints to the new relation. Same
    1258                 :      * as for defaults above, but these need to come after partitioning is set
    1259                 :      * up.
    1260                 :      */
    1261 GIC       62433 :     if (stmt->constraints)
    1262             281 :         AddRelationNewConstraints(rel, NIL, stmt->constraints,
    1263 ECB             :                                   true, true, false, queryString);
    1264                 : 
    1265                 :     /*
    1266                 :      * Finally, merge the NOT NULL constraints that are directly declared with
    1267                 :      * those that come from parent relations (making sure to count inheritance
    1268                 :      * appropriately for each), create them, and set the attnotnull flag on
    1269                 :      * columns that don't yet have it.
    1270                 :      */
    1271 GNC       62424 :     nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
    1272                 :                                            old_notnulls);
    1273           65677 :     foreach(listptr, nncols)
    1274            3253 :         set_attnotnull(NULL, rel, lfirst_int(listptr), false, NoLock);
    1275                 : 
    1276 GIC       62424 :     ObjectAddressSet(address, RelationRelationId, relationId);
    1277                 : 
    1278                 :     /*
    1279                 :      * Clean up.  We keep lock on new relation (although it shouldn't be
    1280                 :      * visible to anyone else anyway, until commit).
    1281 ECB             :      */
    1282 GIC       62424 :     relation_close(rel, NoLock);
    1283 ECB             : 
    1284 GIC       62424 :     return address;
    1285                 : }
    1286                 : 
    1287                 : /*
    1288                 :  * Emit the right error or warning message for a "DROP" command issued on a
    1289                 :  * non-existent relation
    1290                 :  */
    1291 ECB             : static void
    1292 CBC         516 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
    1293                 : {
    1294                 :     const struct dropmsgstrings *rentry;
    1295                 : 
    1296 GIC         573 :     if (rel->schemaname != NULL &&
    1297              57 :         !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
    1298                 :     {
    1299              21 :         if (!missing_ok)
    1300                 :         {
    1301 LBC           0 :             ereport(ERROR,
    1302                 :                     (errcode(ERRCODE_UNDEFINED_SCHEMA),
    1303 ECB             :                      errmsg("schema \"%s\" does not exist", rel->schemaname)));
    1304                 :         }
    1305                 :         else
    1306                 :         {
    1307 GIC          21 :             ereport(NOTICE,
    1308                 :                     (errmsg("schema \"%s\" does not exist, skipping",
    1309                 :                             rel->schemaname)));
    1310                 :         }
    1311              21 :         return;
    1312 ECB             :     }
    1313                 : 
    1314 CBC         643 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1315                 :     {
    1316 GIC         643 :         if (rentry->kind == rightkind)
    1317                 :         {
    1318             495 :             if (!missing_ok)
    1319                 :             {
    1320              65 :                 ereport(ERROR,
    1321                 :                         (errcode(rentry->nonexistent_code),
    1322 ECB             :                          errmsg(rentry->nonexistent_msg, rel->relname)));
    1323                 :             }
    1324                 :             else
    1325                 :             {
    1326 CBC         430 :                 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
    1327             430 :                 break;
    1328                 :             }
    1329 ECB             :         }
    1330                 :     }
    1331 EUB             : 
    1332 GIC         430 :     Assert(rentry->kind != '\0');    /* Should be impossible */
    1333                 : }
    1334                 : 
    1335                 : /*
    1336                 :  * Emit the right error message for a "DROP" command issued on a
    1337 ECB             :  * relation of the wrong type
    1338                 :  */
    1339                 : static void
    1340 UIC           0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
    1341 ECB             : {
    1342                 :     const struct dropmsgstrings *rentry;
    1343                 :     const struct dropmsgstrings *wentry;
    1344                 : 
    1345 UIC           0 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1346 LBC           0 :         if (rentry->kind == rightkind)
    1347 UIC           0 :             break;
    1348 LBC           0 :     Assert(rentry->kind != '\0');
    1349                 : 
    1350               0 :     for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
    1351 UIC           0 :         if (wentry->kind == wrongkind)
    1352               0 :             break;
    1353                 :     /* wrongkind could be something we don't have in our table... */
    1354                 : 
    1355               0 :     ereport(ERROR,
    1356 ECB             :             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1357                 :              errmsg(rentry->nota_msg, relname),
    1358                 :              (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
    1359                 : }
    1360                 : 
    1361                 : /*
    1362                 :  * RemoveRelations
    1363                 :  *      Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
    1364                 :  *      DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
    1365                 :  */
    1366                 : void
    1367 GIC        7479 : RemoveRelations(DropStmt *drop)
    1368                 : {
    1369                 :     ObjectAddresses *objects;
    1370 EUB             :     char        relkind;
    1371                 :     ListCell   *cell;
    1372 GIC        7479 :     int         flags = 0;
    1373            7479 :     LOCKMODE    lockmode = AccessExclusiveLock;
    1374                 : 
    1375 EUB             :     /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
    1376 GBC        7479 :     if (drop->concurrent)
    1377 EUB             :     {
    1378                 :         /*
    1379                 :          * Note that for temporary relations this lock may get upgraded later
    1380                 :          * on, but as no other session can access a temporary relation, this
    1381                 :          * is actually fine.
    1382                 :          */
    1383 GIC          70 :         lockmode = ShareUpdateExclusiveLock;
    1384              70 :         Assert(drop->removeType == OBJECT_INDEX);
    1385 GBC          70 :         if (list_length(drop->objects) != 1)
    1386 GIC           3 :             ereport(ERROR,
    1387                 :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1388                 :                      errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
    1389              67 :         if (drop->behavior == DROP_CASCADE)
    1390 UIC           0 :             ereport(ERROR,
    1391                 :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1392                 :                      errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
    1393                 :     }
    1394                 : 
    1395                 :     /*
    1396                 :      * First we identify all the relations, then we delete them in a single
    1397 ECB             :      * performMultipleDeletions() call.  This is to avoid unwanted DROP
    1398                 :      * RESTRICT errors if one of the relations depends on another.
    1399                 :      */
    1400                 : 
    1401                 :     /* Determine required relkind */
    1402 CBC        7476 :     switch (drop->removeType)
    1403 ECB             :     {
    1404 GIC        6494 :         case OBJECT_TABLE:
    1405            6494 :             relkind = RELKIND_RELATION;
    1406 CBC        6494 :             break;
    1407                 : 
    1408 GIC         363 :         case OBJECT_INDEX:
    1409             363 :             relkind = RELKIND_INDEX;
    1410             363 :             break;
    1411                 : 
    1412              86 :         case OBJECT_SEQUENCE:
    1413 CBC          86 :             relkind = RELKIND_SEQUENCE;
    1414              86 :             break;
    1415 ECB             : 
    1416 CBC         402 :         case OBJECT_VIEW:
    1417 GIC         402 :             relkind = RELKIND_VIEW;
    1418             402 :             break;
    1419 ECB             : 
    1420 GBC          57 :         case OBJECT_MATVIEW:
    1421 GIC          57 :             relkind = RELKIND_MATVIEW;
    1422              57 :             break;
    1423                 : 
    1424              74 :         case OBJECT_FOREIGN_TABLE:
    1425              74 :             relkind = RELKIND_FOREIGN_TABLE;
    1426              74 :             break;
    1427                 : 
    1428 UIC           0 :         default:
    1429               0 :             elog(ERROR, "unrecognized drop object type: %d",
    1430                 :                  (int) drop->removeType);
    1431                 :             relkind = 0;        /* keep compiler quiet */
    1432 ECB             :             break;
    1433                 :     }
    1434                 : 
    1435                 :     /* Lock and validate each relation; build a list of object addresses */
    1436 CBC        7476 :     objects = new_object_addresses();
    1437                 : 
    1438           16602 :     foreach(cell, drop->objects)
    1439 ECB             :     {
    1440 CBC        9204 :         RangeVar   *rel = makeRangeVarFromNameList((List *) lfirst(cell));
    1441                 :         Oid         relOid;
    1442 ECB             :         ObjectAddress obj;
    1443                 :         struct DropRelationCallbackState state;
    1444                 : 
    1445                 :         /*
    1446                 :          * These next few steps are a great deal like relation_openrv, but we
    1447                 :          * don't bother building a relcache entry since we don't need it.
    1448                 :          *
    1449                 :          * Check for shared-cache-inval messages before trying to access the
    1450                 :          * relation.  This is needed to cover the case where the name
    1451                 :          * identifies a rel that has been dropped and recreated since the
    1452                 :          * start of our transaction: if we don't flush the old syscache entry,
    1453                 :          * then we'll latch onto that entry and suffer an error later.
    1454                 :          */
    1455 CBC        9204 :         AcceptInvalidationMessages();
    1456 ECB             : 
    1457                 :         /* Look up the appropriate relation using namespace search. */
    1458 GBC        9204 :         state.expected_relkind = relkind;
    1459           18408 :         state.heap_lockmode = drop->concurrent ?
    1460 GIC        9204 :             ShareUpdateExclusiveLock : AccessExclusiveLock;
    1461                 :         /* We must initialize these fields to show that no locks are held: */
    1462            9204 :         state.heapOid = InvalidOid;
    1463            9204 :         state.partParentOid = InvalidOid;
    1464                 : 
    1465            9204 :         relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
    1466 ECB             :                                           RangeVarCallbackForDropRelation,
    1467                 :                                           (void *) &state);
    1468                 : 
    1469                 :         /* Not there? */
    1470 CBC        9194 :         if (!OidIsValid(relOid))
    1471                 :         {
    1472 GIC         516 :             DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
    1473             451 :             continue;
    1474                 :         }
    1475                 : 
    1476                 :         /*
    1477                 :          * Decide if concurrent mode needs to be used here or not.  The
    1478                 :          * callback retrieved the rel's persistence for us.
    1479                 :          */
    1480            8678 :         if (drop->concurrent &&
    1481              64 :             state.actual_relpersistence != RELPERSISTENCE_TEMP)
    1482                 :         {
    1483              55 :             Assert(list_length(drop->objects) == 1 &&
    1484                 :                    drop->removeType == OBJECT_INDEX);
    1485 CBC          55 :             flags |= PERFORM_DELETION_CONCURRENTLY;
    1486                 :         }
    1487                 : 
    1488 ECB             :         /*
    1489                 :          * Concurrent index drop cannot be used with partitioned indexes,
    1490                 :          * either.
    1491                 :          */
    1492 CBC        8678 :         if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
    1493              55 :             state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1494 GIC           3 :             ereport(ERROR,
    1495 ECB             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1496                 :                      errmsg("cannot drop partitioned index \"%s\" concurrently",
    1497                 :                             rel->relname)));
    1498                 : 
    1499                 :         /*
    1500                 :          * If we're told to drop a partitioned index, we must acquire lock on
    1501                 :          * all the children of its parent partitioned table before proceeding.
    1502                 :          * Otherwise we'd try to lock the child index partitions before their
    1503                 :          * tables, leading to potential deadlock against other sessions that
    1504                 :          * will lock those objects in the other order.
    1505                 :          */
    1506 GIC        8675 :         if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1507              26 :             (void) find_all_inheritors(state.heapOid,
    1508                 :                                        state.heap_lockmode,
    1509                 :                                        NULL);
    1510 ECB             : 
    1511                 :         /* OK, we're ready to delete this one */
    1512 GIC        8675 :         obj.classId = RelationRelationId;
    1513 CBC        8675 :         obj.objectId = relOid;
    1514 GIC        8675 :         obj.objectSubId = 0;
    1515 ECB             : 
    1516 GIC        8675 :         add_exact_object_address(&obj, objects);
    1517                 :     }
    1518                 : 
    1519            7398 :     performMultipleDeletions(objects, drop->behavior, flags);
    1520                 : 
    1521            7330 :     free_object_addresses(objects);
    1522 CBC        7330 : }
    1523 ECB             : 
    1524                 : /*
    1525                 :  * Before acquiring a table lock, check whether we have sufficient rights.
    1526                 :  * In the case of DROP INDEX, also try to lock the table before the index.
    1527                 :  * Also, if the table to be dropped is a partition, we try to lock the parent
    1528                 :  * first.
    1529                 :  */
    1530                 : static void
    1531 GIC        9317 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
    1532                 :                                 void *arg)
    1533                 : {
    1534                 :     HeapTuple   tuple;
    1535                 :     struct DropRelationCallbackState *state;
    1536 ECB             :     char        expected_relkind;
    1537                 :     bool        is_partition;
    1538                 :     Form_pg_class classform;
    1539                 :     LOCKMODE    heap_lockmode;
    1540 GIC        9317 :     bool        invalid_system_index = false;
    1541                 : 
    1542 CBC        9317 :     state = (struct DropRelationCallbackState *) arg;
    1543            9317 :     heap_lockmode = state->heap_lockmode;
    1544 ECB             : 
    1545                 :     /*
    1546                 :      * If we previously locked some other index's heap, and the name we're
    1547                 :      * looking up no longer refers to that relation, release the now-useless
    1548                 :      * lock.
    1549                 :      */
    1550 GIC        9317 :     if (relOid != oldRelOid && OidIsValid(state->heapOid))
    1551 ECB             :     {
    1552 LBC           0 :         UnlockRelationOid(state->heapOid, heap_lockmode);
    1553 UIC           0 :         state->heapOid = InvalidOid;
    1554                 :     }
    1555                 : 
    1556                 :     /*
    1557                 :      * Similarly, if we previously locked some other partition's heap, and the
    1558                 :      * name we're looking up no longer refers to that relation, release the
    1559                 :      * now-useless lock.
    1560                 :      */
    1561 CBC        9317 :     if (relOid != oldRelOid && OidIsValid(state->partParentOid))
    1562                 :     {
    1563 UIC           0 :         UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
    1564               0 :         state->partParentOid = InvalidOid;
    1565                 :     }
    1566                 : 
    1567                 :     /* Didn't find a relation, so no need for locking or permission checks. */
    1568 GIC        9317 :     if (!OidIsValid(relOid))
    1569             521 :         return;
    1570 ECB             : 
    1571 GIC        8796 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
    1572 CBC        8796 :     if (!HeapTupleIsValid(tuple))
    1573 LBC           0 :         return;                 /* concurrently dropped, so nothing to do */
    1574 GIC        8796 :     classform = (Form_pg_class) GETSTRUCT(tuple);
    1575            8796 :     is_partition = classform->relispartition;
    1576                 : 
    1577                 :     /* Pass back some data to save lookups in RemoveRelations */
    1578            8796 :     state->actual_relkind = classform->relkind;
    1579            8796 :     state->actual_relpersistence = classform->relpersistence;
    1580 ECB             : 
    1581                 :     /*
    1582 EUB             :      * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
    1583                 :      * but RemoveRelations() can only pass one relkind for a given relation.
    1584                 :      * It chooses RELKIND_RELATION for both regular and partitioned tables.
    1585                 :      * That means we must be careful before giving the wrong type error when
    1586                 :      * the relation is RELKIND_PARTITIONED_TABLE.  An equivalent problem
    1587                 :      * exists with indexes.
    1588                 :      */
    1589 GIC        8796 :     if (classform->relkind == RELKIND_PARTITIONED_TABLE)
    1590            1262 :         expected_relkind = RELKIND_RELATION;
    1591 CBC        7534 :     else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
    1592 GIC          31 :         expected_relkind = RELKIND_INDEX;
    1593 EUB             :     else
    1594 GBC        7503 :         expected_relkind = classform->relkind;
    1595                 : 
    1596 GIC        8796 :     if (state->expected_relkind != expected_relkind)
    1597 UIC           0 :         DropErrorMsgWrongType(rel->relname, classform->relkind,
    1598 LBC           0 :                               state->expected_relkind);
    1599 ECB             : 
    1600                 :     /* Allow DROP to either table owner or schema owner */
    1601 GNC        8796 :     if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
    1602               9 :         !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
    1603 GBC           9 :         aclcheck_error(ACLCHECK_NOT_OWNER,
    1604 CBC           9 :                        get_relkind_objtype(classform->relkind),
    1605               9 :                        rel->relname);
    1606                 : 
    1607                 :     /*
    1608 ECB             :      * Check the case of a system index that might have been invalidated by a
    1609                 :      * failed concurrent process and allow its drop. For the time being, this
    1610                 :      * only concerns indexes of toast relations that became invalid during a
    1611                 :      * REINDEX CONCURRENTLY process.
    1612                 :      */
    1613 GIC        8787 :     if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
    1614                 :     {
    1615                 :         HeapTuple   locTuple;
    1616                 :         Form_pg_index indexform;
    1617                 :         bool        indisvalid;
    1618                 : 
    1619 LBC           0 :         locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
    1620               0 :         if (!HeapTupleIsValid(locTuple))
    1621 ECB             :         {
    1622 LBC           0 :             ReleaseSysCache(tuple);
    1623 UIC           0 :             return;
    1624 ECB             :         }
    1625                 : 
    1626 LBC           0 :         indexform = (Form_pg_index) GETSTRUCT(locTuple);
    1627 UBC           0 :         indisvalid = indexform->indisvalid;
    1628               0 :         ReleaseSysCache(locTuple);
    1629                 : 
    1630                 :         /* Mark object as being an invalid index of system catalogs */
    1631 LBC           0 :         if (!indisvalid)
    1632               0 :             invalid_system_index = true;
    1633 ECB             :     }
    1634                 : 
    1635                 :     /* In the case of an invalid index, it is fine to bypass this check */
    1636 GIC        8787 :     if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
    1637               1 :         ereport(ERROR,
    1638                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1639                 :                  errmsg("permission denied: \"%s\" is a system catalog",
    1640                 :                         rel->relname)));
    1641                 : 
    1642            8786 :     ReleaseSysCache(tuple);
    1643 ECB             : 
    1644                 :     /*
    1645                 :      * In DROP INDEX, attempt to acquire lock on the parent table before
    1646                 :      * locking the index.  index_drop() will need this anyway, and since
    1647                 :      * regular queries lock tables before their indexes, we risk deadlock if
    1648                 :      * we do it the other way around.  No error if we don't find a pg_index
    1649 EUB             :      * entry, though --- the relation may have been dropped.  Note that this
    1650                 :      * code will execute for either plain or partitioned indexes.
    1651                 :      */
    1652 GBC        8786 :     if (expected_relkind == RELKIND_INDEX &&
    1653 EUB             :         relOid != oldRelOid)
    1654                 :     {
    1655 GIC         354 :         state->heapOid = IndexGetRelation(relOid, true);
    1656 GBC         354 :         if (OidIsValid(state->heapOid))
    1657             354 :             LockRelationOid(state->heapOid, heap_lockmode);
    1658 EUB             :     }
    1659                 : 
    1660                 :     /*
    1661                 :      * Similarly, if the relation is a partition, we must acquire lock on its
    1662                 :      * parent before locking the partition.  That's because queries lock the
    1663                 :      * parent before its partitions, so we risk deadlock if we do it the other
    1664                 :      * way around.
    1665                 :      */
    1666 CBC        8786 :     if (is_partition && relOid != oldRelOid)
    1667 ECB             :     {
    1668 GIC         293 :         state->partParentOid = get_partition_parent(relOid, true);
    1669             293 :         if (OidIsValid(state->partParentOid))
    1670             293 :             LockRelationOid(state->partParentOid, AccessExclusiveLock);
    1671                 :     }
    1672 ECB             : }
    1673                 : 
    1674                 : /*
    1675                 :  * ExecuteTruncate
    1676                 :  *      Executes a TRUNCATE command.
    1677                 :  *
    1678                 :  * This is a multi-relation truncate.  We first open and grab exclusive
    1679                 :  * lock on all relations involved, checking permissions and otherwise
    1680                 :  * verifying that the relation is OK for truncation.  Note that if relations
    1681                 :  * are foreign tables, at this stage, we have not yet checked that their
    1682                 :  * foreign data in external data sources are OK for truncation.  These are
    1683                 :  * checked when foreign data are actually truncated later.  In CASCADE mode,
    1684                 :  * relations having FK references to the targeted relations are automatically
    1685                 :  * added to the group; in RESTRICT mode, we check that all FK references are
    1686                 :  * internal to the group that's being truncated.  Finally all the relations
    1687                 :  * are truncated and reindexed.
    1688                 :  */
    1689                 : void
    1690 GIC         641 : ExecuteTruncate(TruncateStmt *stmt)
    1691                 : {
    1692             641 :     List       *rels = NIL;
    1693             641 :     List       *relids = NIL;
    1694             641 :     List       *relids_logged = NIL;
    1695                 :     ListCell   *cell;
    1696 ECB             : 
    1697                 :     /*
    1698                 :      * Open, exclusive-lock, and check all the explicitly-specified relations
    1699                 :      */
    1700 CBC        1359 :     foreach(cell, stmt->relations)
    1701                 :     {
    1702 GIC         742 :         RangeVar   *rv = lfirst(cell);
    1703                 :         Relation    rel;
    1704             742 :         bool        recurse = rv->inh;
    1705                 :         Oid         myrelid;
    1706             742 :         LOCKMODE    lockmode = AccessExclusiveLock;
    1707                 : 
    1708             742 :         myrelid = RangeVarGetRelidExtended(rv, lockmode,
    1709                 :                                            0, RangeVarCallbackForTruncate,
    1710                 :                                            NULL);
    1711                 : 
    1712                 :         /* don't throw error for "TRUNCATE foo, foo" */
    1713             724 :         if (list_member_oid(relids, myrelid))
    1714               1 :             continue;
    1715                 : 
    1716                 :         /* open the relation, we already hold a lock on it */
    1717             723 :         rel = table_open(myrelid, NoLock);
    1718                 : 
    1719                 :         /*
    1720 ECB             :          * RangeVarGetRelidExtended() has done most checks with its callback,
    1721                 :          * but other checks with the now-opened Relation remain.
    1722                 :          */
    1723 CBC         723 :         truncate_check_activity(rel);
    1724 ECB             : 
    1725 GIC         723 :         rels = lappend(rels, rel);
    1726             723 :         relids = lappend_oid(relids, myrelid);
    1727                 : 
    1728                 :         /* Log this relation only if needed for logical decoding */
    1729             723 :         if (RelationIsLogicallyLogged(rel))
    1730 CBC          37 :             relids_logged = lappend_oid(relids_logged, myrelid);
    1731                 : 
    1732             723 :         if (recurse)
    1733                 :         {
    1734 ECB             :             ListCell   *child;
    1735                 :             List       *children;
    1736                 : 
    1737 GIC         692 :             children = find_all_inheritors(myrelid, lockmode, NULL);
    1738 ECB             : 
    1739 GIC        2202 :             foreach(child, children)
    1740                 :             {
    1741            1510 :                 Oid         childrelid = lfirst_oid(child);
    1742                 : 
    1743 CBC        1510 :                 if (list_member_oid(relids, childrelid))
    1744             692 :                     continue;
    1745                 : 
    1746                 :                 /* find_all_inheritors already got lock */
    1747             818 :                 rel = table_open(childrelid, NoLock);
    1748                 : 
    1749                 :                 /*
    1750                 :                  * It is possible that the parent table has children that are
    1751                 :                  * temp tables of other backends.  We cannot safely access
    1752                 :                  * such tables (because of buffering issues), and the best
    1753 ECB             :                  * thing to do is to silently ignore them.  Note that this
    1754                 :                  * check is the same as one of the checks done in
    1755                 :                  * truncate_check_activity() called below, still it is kept
    1756                 :                  * here for simplicity.
    1757                 :                  */
    1758 GIC         818 :                 if (RELATION_IS_OTHER_TEMP(rel))
    1759 ECB             :                 {
    1760 CBC           4 :                     table_close(rel, lockmode);
    1761 GIC           4 :                     continue;
    1762 ECB             :                 }
    1763                 : 
    1764                 :                 /*
    1765                 :                  * Inherited TRUNCATE commands perform access permission
    1766                 :                  * checks on the parent table only. So we skip checking the
    1767                 :                  * children's permissions and don't call
    1768                 :                  * truncate_check_perms() here.
    1769                 :                  */
    1770 GIC         814 :                 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
    1771 CBC         814 :                 truncate_check_activity(rel);
    1772                 : 
    1773             814 :                 rels = lappend(rels, rel);
    1774             814 :                 relids = lappend_oid(relids, childrelid);
    1775                 : 
    1776                 :                 /* Log this relation only if needed for logical decoding */
    1777             814 :                 if (RelationIsLogicallyLogged(rel))
    1778 GIC          16 :                     relids_logged = lappend_oid(relids_logged, childrelid);
    1779                 :             }
    1780                 :         }
    1781              31 :         else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    1782               6 :             ereport(ERROR,
    1783                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1784                 :                      errmsg("cannot truncate only a partitioned table"),
    1785                 :                      errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
    1786                 :     }
    1787                 : 
    1788 CBC         617 :     ExecuteTruncateGuts(rels, relids, relids_logged,
    1789 GNC         617 :                         stmt->behavior, stmt->restart_seqs, false);
    1790 ECB             : 
    1791                 :     /* And close the rels */
    1792 GIC        2030 :     foreach(cell, rels)
    1793                 :     {
    1794            1454 :         Relation    rel = (Relation) lfirst(cell);
    1795                 : 
    1796            1454 :         table_close(rel, NoLock);
    1797                 :     }
    1798             576 : }
    1799                 : 
    1800 ECB             : /*
    1801                 :  * ExecuteTruncateGuts
    1802                 :  *
    1803                 :  * Internal implementation of TRUNCATE.  This is called by the actual TRUNCATE
    1804                 :  * command (see above) as well as replication subscribers that execute a
    1805                 :  * replicated TRUNCATE action.
    1806                 :  *
    1807                 :  * explicit_rels is the list of Relations to truncate that the command
    1808                 :  * specified.  relids is the list of Oids corresponding to explicit_rels.
    1809                 :  * relids_logged is the list of Oids (a subset of relids) that require
    1810                 :  * WAL-logging.  This is all a bit redundant, but the existing callers have
    1811                 :  * this information handy in this form.
    1812                 :  */
    1813                 : void
    1814 GIC         634 : ExecuteTruncateGuts(List *explicit_rels,
    1815                 :                     List *relids,
    1816                 :                     List *relids_logged,
    1817                 :                     DropBehavior behavior, bool restart_seqs,
    1818                 :                     bool run_as_table_owner)
    1819 ECB             : {
    1820                 :     List       *rels;
    1821 GIC         634 :     List       *seq_relids = NIL;
    1822             634 :     HTAB       *ft_htab = NULL;
    1823 ECB             :     EState     *estate;
    1824                 :     ResultRelInfo *resultRelInfos;
    1825                 :     ResultRelInfo *resultRelInfo;
    1826                 :     SubTransactionId mySubid;
    1827                 :     ListCell   *cell;
    1828                 :     Oid        *logrelids;
    1829                 : 
    1830                 :     /*
    1831                 :      * Check the explicitly-specified relations.
    1832                 :      *
    1833                 :      * In CASCADE mode, suck in all referencing relations as well.  This
    1834                 :      * requires multiple iterations to find indirectly-dependent relations. At
    1835                 :      * each phase, we need to exclusive-lock new rels before looking for their
    1836                 :      * dependencies, else we might miss something.  Also, we check each rel as
    1837                 :      * soon as we open it, to avoid a faux pas such as holding lock for a long
    1838                 :      * time on a rel we have no permissions for.
    1839                 :      */
    1840 GIC         634 :     rels = list_copy(explicit_rels);
    1841             634 :     if (behavior == DROP_CASCADE)
    1842                 :     {
    1843                 :         for (;;)
    1844              20 :         {
    1845 ECB             :             List       *newrelids;
    1846                 : 
    1847 GIC          40 :             newrelids = heap_truncate_find_FKs(relids);
    1848              40 :             if (newrelids == NIL)
    1849              20 :                 break;          /* nothing else to add */
    1850                 : 
    1851              67 :             foreach(cell, newrelids)
    1852 ECB             :             {
    1853 CBC          47 :                 Oid         relid = lfirst_oid(cell);
    1854                 :                 Relation    rel;
    1855                 : 
    1856 GIC          47 :                 rel = table_open(relid, AccessExclusiveLock);
    1857              47 :                 ereport(NOTICE,
    1858                 :                         (errmsg("truncate cascades to table \"%s\"",
    1859                 :                                 RelationGetRelationName(rel))));
    1860              47 :                 truncate_check_rel(relid, rel->rd_rel);
    1861              47 :                 truncate_check_perms(relid, rel->rd_rel);
    1862              47 :                 truncate_check_activity(rel);
    1863              47 :                 rels = lappend(rels, rel);
    1864              47 :                 relids = lappend_oid(relids, relid);
    1865                 : 
    1866                 :                 /* Log this relation only if needed for logical decoding */
    1867              47 :                 if (RelationIsLogicallyLogged(rel))
    1868 UIC           0 :                     relids_logged = lappend_oid(relids_logged, relid);
    1869                 :             }
    1870                 :         }
    1871 ECB             :     }
    1872                 : 
    1873                 :     /*
    1874                 :      * Check foreign key references.  In CASCADE mode, this should be
    1875                 :      * unnecessary since we just pulled in all the references; but as a
    1876                 :      * cross-check, do it anyway if in an Assert-enabled build.
    1877                 :      */
    1878                 : #ifdef USE_ASSERT_CHECKING
    1879 CBC         634 :     heap_truncate_check_FKs(rels, false);
    1880 ECB             : #else
    1881                 :     if (behavior == DROP_RESTRICT)
    1882                 :         heap_truncate_check_FKs(rels, false);
    1883                 : #endif
    1884                 : 
    1885                 :     /*
    1886                 :      * If we are asked to restart sequences, find all the sequences, lock them
    1887                 :      * (we need AccessExclusiveLock for ResetSequence), and check permissions.
    1888                 :      * We want to do this early since it's pointless to do all the truncation
    1889                 :      * work only to fail on sequence permissions.
    1890                 :      */
    1891 CBC         597 :     if (restart_seqs)
    1892 ECB             :     {
    1893 CBC          26 :         foreach(cell, rels)
    1894 ECB             :         {
    1895 CBC          13 :             Relation    rel = (Relation) lfirst(cell);
    1896 GIC          13 :             List       *seqlist = getOwnedSequences(RelationGetRelid(rel));
    1897                 :             ListCell   *seqcell;
    1898 ECB             : 
    1899 GBC          31 :             foreach(seqcell, seqlist)
    1900                 :             {
    1901 GIC          18 :                 Oid         seq_relid = lfirst_oid(seqcell);
    1902                 :                 Relation    seq_rel;
    1903                 : 
    1904              18 :                 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
    1905                 : 
    1906                 :                 /* This check must match AlterSequence! */
    1907 GNC          18 :                 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
    1908 UIC           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
    1909               0 :                                    RelationGetRelationName(seq_rel));
    1910 ECB             : 
    1911 GIC          18 :                 seq_relids = lappend_oid(seq_relids, seq_relid);
    1912                 : 
    1913              18 :                 relation_close(seq_rel, NoLock);
    1914                 :             }
    1915                 :         }
    1916                 :     }
    1917                 : 
    1918                 :     /* Prepare to catch AFTER triggers. */
    1919             597 :     AfterTriggerBeginQuery();
    1920                 : 
    1921                 :     /*
    1922 ECB             :      * To fire triggers, we'll need an EState as well as a ResultRelInfo for
    1923                 :      * each relation.  We don't need to call ExecOpenIndices, though.
    1924                 :      *
    1925                 :      * We put the ResultRelInfos in the es_opened_result_relations list, even
    1926                 :      * though we don't have a range table and don't populate the
    1927                 :      * es_result_relations array.  That's a bit bogus, but it's enough to make
    1928                 :      * ExecGetTriggerResultRel() find them.
    1929                 :      */
    1930 CBC         597 :     estate = CreateExecutorState();
    1931                 :     resultRelInfos = (ResultRelInfo *)
    1932             597 :         palloc(list_length(rels) * sizeof(ResultRelInfo));
    1933 GIC         597 :     resultRelInfo = resultRelInfos;
    1934            2135 :     foreach(cell, rels)
    1935 ECB             :     {
    1936 GIC        1538 :         Relation    rel = (Relation) lfirst(cell);
    1937                 : 
    1938 CBC        1538 :         InitResultRelInfo(resultRelInfo,
    1939 EUB             :                           rel,
    1940                 :                           0,    /* dummy rangetable index */
    1941                 :                           NULL,
    1942 ECB             :                           0);
    1943 GIC        1538 :         estate->es_opened_result_relations =
    1944 CBC        1538 :             lappend(estate->es_opened_result_relations, resultRelInfo);
    1945 GIC        1538 :         resultRelInfo++;
    1946                 :     }
    1947                 : 
    1948                 :     /*
    1949                 :      * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
    1950 ECB             :      * truncating (this is because one of them might throw an error). Also, if
    1951                 :      * we were to allow them to prevent statement execution, that would need
    1952                 :      * to be handled here.
    1953                 :      */
    1954 GIC         597 :     resultRelInfo = resultRelInfos;
    1955            2135 :     foreach(cell, rels)
    1956                 :     {
    1957                 :         UserContext ucxt;
    1958                 : 
    1959 GNC        1538 :         if (run_as_table_owner)
    1960              33 :             SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    1961                 :                                   &ucxt);
    1962 GIC        1538 :         ExecBSTruncateTriggers(estate, resultRelInfo);
    1963 GNC        1538 :         if (run_as_table_owner)
    1964              33 :             RestoreUserContext(&ucxt);
    1965 GIC        1538 :         resultRelInfo++;
    1966                 :     }
    1967                 : 
    1968 ECB             :     /*
    1969                 :      * OK, truncate each table.
    1970                 :      */
    1971 CBC         597 :     mySubid = GetCurrentSubTransactionId();
    1972 ECB             : 
    1973 GIC        2135 :     foreach(cell, rels)
    1974 ECB             :     {
    1975 GIC        1538 :         Relation    rel = (Relation) lfirst(cell);
    1976 ECB             : 
    1977                 :         /* Skip partitioned tables as there is nothing to do */
    1978 GIC        1538 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    1979             322 :             continue;
    1980                 : 
    1981 ECB             :         /*
    1982                 :          * Build the lists of foreign tables belonging to each foreign server
    1983                 :          * and pass each list to the foreign data wrapper's callback function,
    1984                 :          * so that each server can truncate its all foreign tables in bulk.
    1985                 :          * Each list is saved as a single entry in a hash table that uses the
    1986                 :          * server OID as lookup key.
    1987                 :          */
    1988 GIC        1216 :         if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    1989              17 :         {
    1990              17 :             Oid         serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
    1991                 :             bool        found;
    1992 ECB             :             ForeignTruncateInfo *ft_info;
    1993                 : 
    1994                 :             /* First time through, initialize hashtable for foreign tables */
    1995 GIC          17 :             if (!ft_htab)
    1996                 :             {
    1997 ECB             :                 HASHCTL     hctl;
    1998                 : 
    1999 GIC          15 :                 memset(&hctl, 0, sizeof(HASHCTL));
    2000 CBC          15 :                 hctl.keysize = sizeof(Oid);
    2001              15 :                 hctl.entrysize = sizeof(ForeignTruncateInfo);
    2002              15 :                 hctl.hcxt = CurrentMemoryContext;
    2003 ECB             : 
    2004 GIC          15 :                 ft_htab = hash_create("TRUNCATE for Foreign Tables",
    2005                 :                                       32,   /* start small and extend */
    2006                 :                                       &hctl,
    2007                 :                                       HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
    2008                 :             }
    2009 ECB             : 
    2010                 :             /* Find or create cached entry for the foreign table */
    2011 CBC          17 :             ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
    2012 GIC          17 :             if (!found)
    2013 ECB             :             {
    2014 GIC          15 :                 ft_info->serverid = serverid;
    2015              15 :                 ft_info->rels = NIL;
    2016 ECB             :             }
    2017                 : 
    2018                 :             /*
    2019                 :              * Save the foreign table in the entry of the server that the
    2020                 :              * foreign table belongs to.
    2021                 :              */
    2022 GIC          17 :             ft_info->rels = lappend(ft_info->rels, rel);
    2023              17 :             continue;
    2024                 :         }
    2025                 : 
    2026 ECB             :         /*
    2027                 :          * Normally, we need a transaction-safe truncation here.  However, if
    2028                 :          * the table was either created in the current (sub)transaction or has
    2029                 :          * a new relfilenumber in the current (sub)transaction, then we can
    2030                 :          * just truncate it in-place, because a rollback would cause the whole
    2031                 :          * table or the current physical file to be thrown away anyway.
    2032                 :          */
    2033 CBC        1199 :         if (rel->rd_createSubid == mySubid ||
    2034 GNC        1191 :             rel->rd_newRelfilelocatorSubid == mySubid)
    2035                 :         {
    2036                 :             /* Immediate, non-rollbackable truncation is OK */
    2037 CBC          24 :             heap_truncate_one_rel(rel);
    2038 ECB             :         }
    2039                 :         else
    2040                 :         {
    2041                 :             Oid         heap_relid;
    2042                 :             Oid         toast_relid;
    2043 GIC        1175 :             ReindexParams reindex_params = {0};
    2044                 : 
    2045                 :             /*
    2046                 :              * This effectively deletes all rows in the table, and may be done
    2047                 :              * in a serializable transaction.  In that case we must record a
    2048                 :              * rw-conflict in to this transaction from each transaction
    2049 ECB             :              * holding a predicate lock on the table.
    2050                 :              */
    2051 GIC        1175 :             CheckTableForSerializableConflictIn(rel);
    2052 ECB             : 
    2053                 :             /*
    2054                 :              * Need the full transaction-safe pushups.
    2055                 :              *
    2056                 :              * Create a new empty storage file for the relation, and assign it
    2057                 :              * as the relfilenumber value. The old storage file is scheduled
    2058                 :              * for deletion at commit.
    2059                 :              */
    2060 GNC        1175 :             RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
    2061 ECB             : 
    2062 GIC        1175 :             heap_relid = RelationGetRelid(rel);
    2063                 : 
    2064                 :             /*
    2065                 :              * The same for the toast table, if any.
    2066                 :              */
    2067            1175 :             toast_relid = rel->rd_rel->reltoastrelid;
    2068            1175 :             if (OidIsValid(toast_relid))
    2069                 :             {
    2070             700 :                 Relation    toastrel = relation_open(toast_relid,
    2071 ECB             :                                                      AccessExclusiveLock);
    2072                 : 
    2073 GNC         700 :                 RelationSetNewRelfilenumber(toastrel,
    2074             700 :                                             toastrel->rd_rel->relpersistence);
    2075 CBC         700 :                 table_close(toastrel, NoLock);
    2076                 :             }
    2077                 : 
    2078                 :             /*
    2079                 :              * Reconstruct the indexes to match, and we're done.
    2080                 :              */
    2081            1175 :             reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST,
    2082                 :                              &reindex_params);
    2083                 :         }
    2084                 : 
    2085 GIC        1199 :         pgstat_count_truncate(rel);
    2086                 :     }
    2087                 : 
    2088                 :     /* Now go through the hash table, and truncate foreign tables */
    2089 CBC         597 :     if (ft_htab)
    2090                 :     {
    2091                 :         ForeignTruncateInfo *ft_info;
    2092                 :         HASH_SEQ_STATUS seq;
    2093                 : 
    2094 GIC          15 :         hash_seq_init(&seq, ft_htab);
    2095                 : 
    2096              15 :         PG_TRY();
    2097                 :         {
    2098 CBC          26 :             while ((ft_info = hash_seq_search(&seq)) != NULL)
    2099                 :             {
    2100              15 :                 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
    2101                 : 
    2102                 :                 /* truncate_check_rel() has checked that already */
    2103 GIC          15 :                 Assert(routine->ExecForeignTruncate != NULL);
    2104                 : 
    2105 CBC          15 :                 routine->ExecForeignTruncate(ft_info->rels,
    2106 ECB             :                                              behavior,
    2107                 :                                              restart_seqs);
    2108                 :             }
    2109                 :         }
    2110 GIC           4 :         PG_FINALLY();
    2111 ECB             :         {
    2112 CBC          15 :             hash_destroy(ft_htab);
    2113 ECB             :         }
    2114 GIC          15 :         PG_END_TRY();
    2115                 :     }
    2116                 : 
    2117                 :     /*
    2118                 :      * Restart owned sequences if we were asked to.
    2119 ECB             :      */
    2120 GIC         611 :     foreach(cell, seq_relids)
    2121                 :     {
    2122              18 :         Oid         seq_relid = lfirst_oid(cell);
    2123 ECB             : 
    2124 GIC          18 :         ResetSequence(seq_relid);
    2125                 :     }
    2126                 : 
    2127 ECB             :     /*
    2128                 :      * Write a WAL record to allow this set of actions to be logically
    2129                 :      * decoded.
    2130                 :      *
    2131                 :      * Assemble an array of relids so we can write a single WAL record for the
    2132                 :      * whole action.
    2133                 :      */
    2134 GNC         593 :     if (relids_logged != NIL)
    2135                 :     {
    2136 ECB             :         xl_heap_truncate xlrec;
    2137 GIC          47 :         int         i = 0;
    2138 ECB             : 
    2139                 :         /* should only get here if wal_level >= logical */
    2140 GIC          47 :         Assert(XLogLogicalInfoActive());
    2141 ECB             : 
    2142 GIC          47 :         logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
    2143 CBC         133 :         foreach(cell, relids_logged)
    2144 GIC          86 :             logrelids[i++] = lfirst_oid(cell);
    2145                 : 
    2146              47 :         xlrec.dbId = MyDatabaseId;
    2147              47 :         xlrec.nrelids = list_length(relids_logged);
    2148 CBC          47 :         xlrec.flags = 0;
    2149 GIC          47 :         if (behavior == DROP_CASCADE)
    2150 CBC           1 :             xlrec.flags |= XLH_TRUNCATE_CASCADE;
    2151 GIC          47 :         if (restart_seqs)
    2152 CBC           3 :             xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
    2153                 : 
    2154 GIC          47 :         XLogBeginInsert();
    2155              47 :         XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
    2156              47 :         XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
    2157                 : 
    2158 CBC          47 :         XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
    2159                 : 
    2160              47 :         (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
    2161                 :     }
    2162 ECB             : 
    2163                 :     /*
    2164                 :      * Process all AFTER STATEMENT TRUNCATE triggers.
    2165                 :      */
    2166 GIC         593 :     resultRelInfo = resultRelInfos;
    2167            2127 :     foreach(cell, rels)
    2168                 :     {
    2169                 :         UserContext ucxt;
    2170                 : 
    2171 GNC        1534 :         if (run_as_table_owner)
    2172              33 :             SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2173                 :                                   &ucxt);
    2174 GIC        1534 :         ExecASTruncateTriggers(estate, resultRelInfo);
    2175 GNC        1534 :         if (run_as_table_owner)
    2176              33 :             RestoreUserContext(&ucxt);
    2177 GIC        1534 :         resultRelInfo++;
    2178                 :     }
    2179 ECB             : 
    2180                 :     /* Handle queued AFTER triggers */
    2181 GIC         593 :     AfterTriggerEndQuery(estate);
    2182 ECB             : 
    2183                 :     /* We can clean up the EState now */
    2184 GIC         593 :     FreeExecutorState(estate);
    2185 ECB             : 
    2186                 :     /*
    2187                 :      * Close any rels opened by CASCADE (can't do this while EState still
    2188                 :      * holds refs)
    2189                 :      */
    2190 GIC         593 :     rels = list_difference_ptr(rels, explicit_rels);
    2191 CBC         640 :     foreach(cell, rels)
    2192 ECB             :     {
    2193 CBC          47 :         Relation    rel = (Relation) lfirst(cell);
    2194 ECB             : 
    2195 CBC          47 :         table_close(rel, NoLock);
    2196 ECB             :     }
    2197 CBC         593 : }
    2198                 : 
    2199 ECB             : /*
    2200                 :  * Check that a given relation is safe to truncate.  Subroutine for
    2201                 :  * ExecuteTruncate() and RangeVarCallbackForTruncate().
    2202                 :  */
    2203                 : static void
    2204 GIC        1670 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
    2205 ECB             : {
    2206 GIC        1670 :     char       *relname = NameStr(reltuple->relname);
    2207                 : 
    2208                 :     /*
    2209                 :      * Only allow truncate on regular tables, foreign tables using foreign
    2210                 :      * data wrappers supporting TRUNCATE and partitioned tables (although, the
    2211 ECB             :      * latter are only being included here for the following checks; no
    2212                 :      * physical truncation will occur in their case.).
    2213                 :      */
    2214 GIC        1670 :     if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
    2215                 :     {
    2216 CBC          18 :         Oid         serverid = GetForeignServerIdByRelId(relid);
    2217              18 :         FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
    2218                 : 
    2219              18 :         if (!fdwroutine->ExecForeignTruncate)
    2220               1 :             ereport(ERROR,
    2221 ECB             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2222                 :                      errmsg("cannot truncate foreign table \"%s\"",
    2223                 :                             relname)));
    2224                 :     }
    2225 GIC        1652 :     else if (reltuple->relkind != RELKIND_RELATION &&
    2226 CBC         330 :              reltuple->relkind != RELKIND_PARTITIONED_TABLE)
    2227 UIC           0 :         ereport(ERROR,
    2228                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2229 ECB             :                  errmsg("\"%s\" is not a table", relname)));
    2230                 : 
    2231                 :     /*
    2232                 :      * Most system catalogs can't be truncated at all, or at least not unless
    2233                 :      * allow_system_table_mods=on. As an exception, however, we allow
    2234                 :      * pg_largeobject to be truncated as part of pg_upgrade, because we need
    2235                 :      * to change its relfilenode to match the old cluster, and allowing a
    2236                 :      * TRUNCATE command to be executed is the easiest way of doing that.
    2237                 :      */
    2238 CBC        1669 :     if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
    2239 GIC           7 :         && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
    2240 CBC           1 :         ereport(ERROR,
    2241                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2242 ECB             :                  errmsg("permission denied: \"%s\" is a system catalog",
    2243                 :                         relname)));
    2244                 : 
    2245 GIC        1668 :     InvokeObjectTruncateHook(relid);
    2246            1668 : }
    2247                 : 
    2248                 : /*
    2249 ECB             :  * Check that current user has the permission to truncate given relation.
    2250                 :  */
    2251                 : static void
    2252 GIC         854 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
    2253                 : {
    2254             854 :     char       *relname = NameStr(reltuple->relname);
    2255                 :     AclResult   aclresult;
    2256                 : 
    2257                 :     /* Permissions checks */
    2258             854 :     aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
    2259 CBC         854 :     if (aclresult != ACLCHECK_OK)
    2260 GIC          16 :         aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
    2261 ECB             :                        relname);
    2262 CBC         838 : }
    2263                 : 
    2264 ECB             : /*
    2265                 :  * Set of extra sanity checks to check if a given relation is safe to
    2266                 :  * truncate.  This is split with truncate_check_rel() as
    2267                 :  * RangeVarCallbackForTruncate() cannot open a Relation yet.
    2268                 :  */
    2269                 : static void
    2270 CBC        1584 : truncate_check_activity(Relation rel)
    2271 ECB             : {
    2272 EUB             :     /*
    2273                 :      * Don't allow truncate on temp tables of other backends ... their local
    2274                 :      * buffer manager is not going to cope.
    2275                 :      */
    2276 GIC        1584 :     if (RELATION_IS_OTHER_TEMP(rel))
    2277 UIC           0 :         ereport(ERROR,
    2278                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2279                 :                  errmsg("cannot truncate temporary tables of other sessions")));
    2280                 : 
    2281                 :     /*
    2282                 :      * Also check for active uses of the relation in the current transaction,
    2283 ECB             :      * including open scans and pending AFTER trigger events.
    2284                 :      */
    2285 CBC        1584 :     CheckTableNotInUse(rel, "TRUNCATE");
    2286 GIC        1584 : }
    2287                 : 
    2288                 : /*
    2289                 :  * storage_name
    2290 ECB             :  *    returns the name corresponding to a typstorage/attstorage enum value
    2291                 :  */
    2292                 : static const char *
    2293 GIC          12 : storage_name(char c)
    2294                 : {
    2295              12 :     switch (c)
    2296                 :     {
    2297 LBC           0 :         case TYPSTORAGE_PLAIN:
    2298 UIC           0 :             return "PLAIN";
    2299 LBC           0 :         case TYPSTORAGE_EXTERNAL:
    2300 UIC           0 :             return "EXTERNAL";
    2301 GIC           6 :         case TYPSTORAGE_EXTENDED:
    2302               6 :             return "EXTENDED";
    2303 CBC           6 :         case TYPSTORAGE_MAIN:
    2304               6 :             return "MAIN";
    2305 LBC           0 :         default:
    2306 UIC           0 :             return "???";
    2307 ECB             :     }
    2308                 : }
    2309                 : 
    2310                 : /*----------
    2311                 :  * MergeAttributes
    2312                 :  *      Returns new schema given initial schema and superclasses.
    2313                 :  *
    2314                 :  * Input arguments:
    2315                 :  * 'schema' is the column/attribute definition for the table. (It's a list
    2316                 :  *      of ColumnDef's.) It is destructively changed.
    2317                 :  * 'supers' is a list of OIDs of parent relations, already locked by caller.
    2318                 :  * 'relpersistence' is the persistence type of the table.
    2319                 :  * 'is_partition' tells if the table is a partition.
    2320                 :  *
    2321                 :  * Output arguments:
    2322 EUB             :  * 'supconstr' receives a list of constraints belonging to the parents,
    2323                 :  *      updated as necessary to be valid for the child.
    2324                 :  * 'nnconstraints' receives a list of CookedConstraints that corresponds to
    2325                 :  *      constraints coming from inheritance parents.
    2326                 :  *
    2327                 :  * Return value:
    2328                 :  * Completed schema list.
    2329                 :  *
    2330                 :  * Notes:
    2331                 :  *    The order in which the attributes are inherited is very important.
    2332 ECB             :  *    Intuitively, the inherited attributes should come first. If a table
    2333                 :  *    inherits from multiple parents, the order of those attributes are
    2334                 :  *    according to the order of the parents specified in CREATE TABLE.
    2335                 :  *
    2336                 :  *    Here's an example:
    2337                 :  *
    2338                 :  *      create table person (name text, age int4, location point);
    2339                 :  *      create table emp (salary int4, manager text) inherits(person);
    2340                 :  *      create table student (gpa float8) inherits (person);
    2341                 :  *      create table stud_emp (percent int4) inherits (emp, student);
    2342                 :  *
    2343                 :  *    The order of the attributes of stud_emp is:
    2344 EUB             :  *
    2345                 :  *                          person {1:name, 2:age, 3:location}
    2346                 :  *                          /    \
    2347                 :  *             {6:gpa}  student   emp {4:salary, 5:manager}
    2348 ECB             :  *                          \    /
    2349                 :  *                         stud_emp {7:percent}
    2350                 :  *
    2351                 :  *     If the same attribute name appears multiple times, then it appears
    2352 EUB             :  *     in the result table in the proper location for its first appearance.
    2353                 :  *
    2354                 :  *     Constraints (including NOT NULL constraints) for the child table
    2355                 :  *     are the union of all relevant constraints, from both the child schema
    2356                 :  *     and parent tables.  In addition, in legacy inheritance, each column that
    2357                 :  *     appears in a primary key in any of the parents also gets a NOT NULL
    2358                 :  *     constraint (partitioning doesn't need this, because the PK itself gets
    2359                 :  *     inherited.)
    2360                 :  *
    2361                 :  *     The default value for a child column is defined as:
    2362                 :  *      (1) If the child schema specifies a default, that value is used.
    2363                 :  *      (2) If neither the child nor any parent specifies a default, then
    2364                 :  *          the column will not have a default.
    2365                 :  *      (3) If conflicting defaults are inherited from different parents
    2366                 :  *          (and not overridden by the child), an error is raised.
    2367                 :  *      (4) Otherwise the inherited default is used.
    2368                 :  *
    2369                 :  *      Note that the default-value infrastructure is used for generated
    2370                 :  *      columns' expressions too, so most of the preceding paragraph applies
    2371                 :  *      to generation expressions too.  We insist that a child column be
    2372                 :  *      generated if and only if its parent(s) are, but it need not have
    2373                 :  *      the same generation expression.
    2374                 :  *----------
    2375                 :  */
    2376                 : static List *
    2377 GIC       62853 : MergeAttributes(List *schema, List *supers, char relpersistence,
    2378                 :                 bool is_partition, List **supconstr, List **supnotnulls)
    2379                 : {
    2380           62853 :     List       *inhSchema = NIL;
    2381           62853 :     List       *constraints = NIL;
    2382 GNC       62853 :     List       *nnconstraints = NIL;
    2383 GIC       62853 :     bool        have_bogus_defaults = false;
    2384                 :     int         child_attno;
    2385                 :     static Node bogus_marker = {0}; /* marks conflicting defaults */
    2386           62853 :     List       *saved_schema = NIL;
    2387                 :     ListCell   *entry;
    2388                 : 
    2389                 :     /*
    2390                 :      * Check for and reject tables with too many columns. We perform this
    2391                 :      * check relatively early for two reasons: (a) we don't run the risk of
    2392                 :      * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
    2393                 :      * okay if we're processing <= 1600 columns, but could take minutes to
    2394                 :      * execute if the user attempts to create a table with hundreds of
    2395                 :      * thousands of columns.
    2396                 :      *
    2397                 :      * Note that we also need to check that we do not exceed this figure after
    2398                 :      * including columns from inherited relations.
    2399                 :      */
    2400           62853 :     if (list_length(schema) > MaxHeapAttributeNumber)
    2401 UIC           0 :         ereport(ERROR,
    2402                 :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    2403                 :                  errmsg("tables can have at most %d columns",
    2404                 :                         MaxHeapAttributeNumber)));
    2405                 : 
    2406                 :     /*
    2407                 :      * Check for duplicate names in the explicit list of attributes.
    2408                 :      *
    2409                 :      * Although we might consider merging such entries in the same way that we
    2410                 :      * handle name conflicts for inherited attributes, it seems to make more
    2411                 :      * sense to assume such conflicts are errors.
    2412                 :      *
    2413                 :      * We don't use foreach() here because we have two nested loops over the
    2414                 :      * schema list, with possible element deletions in the inner one.  If we
    2415                 :      * used foreach_delete_current() it could only fix up the state of one of
    2416                 :      * the loops, so it seems cleaner to use looping over list indexes for
    2417                 :      * both loops.  Note that any deletion will happen beyond where the outer
    2418                 :      * loop is, so its index never needs adjustment.
    2419                 :      */
    2420 GIC      541245 :     for (int coldefpos = 0; coldefpos < list_length(schema); coldefpos++)
    2421                 :     {
    2422          478404 :         ColumnDef  *coldef = list_nth_node(ColumnDef, schema, coldefpos);
    2423                 : 
    2424          478404 :         if (!is_partition && coldef->typeName == NULL)
    2425                 :         {
    2426                 :             /*
    2427                 :              * Typed table column option that does not belong to a column from
    2428                 :              * the type.  This works because the columns from the type come
    2429                 :              * first in the list.  (We omit this check for partition column
    2430                 :              * lists; those are processed separately below.)
    2431                 :              */
    2432 CBC           3 :             ereport(ERROR,
    2433                 :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    2434                 :                      errmsg("column \"%s\" does not exist",
    2435 ECB             :                             coldef->colname)));
    2436                 :         }
    2437                 : 
    2438                 :         /* restpos scans all entries beyond coldef; incr is in loop body */
    2439 GIC     6750509 :         for (int restpos = coldefpos + 1; restpos < list_length(schema);)
    2440                 :         {
    2441 CBC     6272117 :             ColumnDef  *restdef = list_nth_node(ColumnDef, schema, restpos);
    2442                 : 
    2443 GIC     6272117 :             if (strcmp(coldef->colname, restdef->colname) == 0)
    2444                 :             {
    2445              25 :                 if (coldef->is_from_type)
    2446                 :                 {
    2447                 :                     /*
    2448                 :                      * merge the column options into the column from the type
    2449                 :                      */
    2450              16 :                     coldef->is_not_null = restdef->is_not_null;
    2451              16 :                     coldef->raw_default = restdef->raw_default;
    2452              16 :                     coldef->cooked_default = restdef->cooked_default;
    2453              16 :                     coldef->constraints = restdef->constraints;
    2454              16 :                     coldef->is_from_type = false;
    2455 CBC          16 :                     schema = list_delete_nth_cell(schema, restpos);
    2456 EUB             :                 }
    2457                 :                 else
    2458 GIC           9 :                     ereport(ERROR,
    2459                 :                             (errcode(ERRCODE_DUPLICATE_COLUMN),
    2460                 :                              errmsg("column \"%s\" specified more than once",
    2461                 :                                     coldef->colname)));
    2462                 :             }
    2463                 :             else
    2464         6272092 :                 restpos++;
    2465                 :         }
    2466                 :     }
    2467                 : 
    2468                 :     /*
    2469                 :      * In case of a partition, there are no new column definitions, only dummy
    2470                 :      * ColumnDefs created for column constraints.  Set them aside for now and
    2471                 :      * process them at the end.
    2472                 :      */
    2473           62841 :     if (is_partition)
    2474                 :     {
    2475 CBC        3602 :         saved_schema = schema;
    2476 GIC        3602 :         schema = NIL;
    2477 ECB             :     }
    2478                 : 
    2479                 :     /*
    2480                 :      * Scan the parents left-to-right, and merge their attributes to form a
    2481                 :      * list of inherited attributes (inhSchema).  Also check to see if we need
    2482                 :      * to inherit an OID column.
    2483                 :      */
    2484 GIC       62841 :     child_attno = 0;
    2485           67277 :     foreach(entry, supers)
    2486                 :     {
    2487 CBC        4472 :         Oid         parent = lfirst_oid(entry);
    2488                 :         Relation    relation;
    2489                 :         TupleDesc   tupleDesc;
    2490                 :         TupleConstr *constr;
    2491                 :         AttrMap    *newattmap;
    2492                 :         List       *inherited_defaults;
    2493                 :         List       *cols_with_defaults;
    2494                 :         List       *nnconstrs;
    2495 ECB             :         AttrNumber  parent_attno;
    2496                 :         ListCell   *lc1;
    2497                 :         ListCell   *lc2;
    2498                 :         Bitmapset  *pkattrs;
    2499 GNC        4472 :         Bitmapset  *nncols = NULL;
    2500                 : 
    2501                 : 
    2502 ECB             :         /* caller already got lock */
    2503 GIC        4472 :         relation = table_open(parent, NoLock);
    2504 ECB             : 
    2505                 :         /*
    2506                 :          * Check for active uses of the parent partitioned table in the
    2507                 :          * current transaction, such as being used in some manner by an
    2508                 :          * enclosing command.
    2509                 :          */
    2510 CBC        4472 :         if (is_partition)
    2511            3602 :             CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
    2512 ECB             : 
    2513                 :         /*
    2514                 :          * We do not allow partitioned tables and partitions to participate in
    2515                 :          * regular inheritance.
    2516                 :          */
    2517 CBC        4469 :         if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
    2518 GIC        3596 :             !is_partition)
    2519               3 :             ereport(ERROR,
    2520                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2521                 :                      errmsg("cannot inherit from partitioned table \"%s\"",
    2522                 :                             RelationGetRelationName(relation))));
    2523 CBC        4466 :         if (relation->rd_rel->relispartition && !is_partition)
    2524 GIC           3 :             ereport(ERROR,
    2525                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2526                 :                      errmsg("cannot inherit from partition \"%s\"",
    2527                 :                             RelationGetRelationName(relation))));
    2528                 : 
    2529            4463 :         if (relation->rd_rel->relkind != RELKIND_RELATION &&
    2530            3603 :             relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
    2531            3593 :             relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    2532 LBC           0 :             ereport(ERROR,
    2533                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2534 ECB             :                      errmsg("inherited relation \"%s\" is not a table or foreign table",
    2535                 :                             RelationGetRelationName(relation))));
    2536                 : 
    2537                 :         /*
    2538                 :          * If the parent is permanent, so must be all of its partitions.  Note
    2539                 :          * that inheritance allows that case.
    2540                 :          */
    2541 GIC        4463 :         if (is_partition &&
    2542            3599 :             relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
    2543 ECB             :             relpersistence == RELPERSISTENCE_TEMP)
    2544 CBC           3 :             ereport(ERROR,
    2545                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2546 ECB             :                      errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
    2547                 :                             RelationGetRelationName(relation))));
    2548                 : 
    2549                 :         /* Permanent rels cannot inherit from temporary ones */
    2550 GIC        4460 :         if (relpersistence != RELPERSISTENCE_TEMP &&
    2551            4289 :             relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
    2552              12 :             ereport(ERROR,
    2553                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2554                 :                      errmsg(!is_partition
    2555                 :                             ? "cannot inherit from temporary relation \"%s\""
    2556                 :                             : "cannot create a permanent relation as partition of temporary relation \"%s\"",
    2557                 :                             RelationGetRelationName(relation))));
    2558 ECB             : 
    2559                 :         /* If existing rel is temp, it must belong to this session */
    2560 GIC        4448 :         if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
    2561             147 :             !relation->rd_islocaltemp)
    2562 LBC           0 :             ereport(ERROR,
    2563                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2564                 :                      errmsg(!is_partition
    2565                 :                             ? "cannot inherit from temporary relation of another session"
    2566                 :                             : "cannot create as partition of temporary relation of another session")));
    2567                 : 
    2568                 :         /*
    2569 ECB             :          * We should have an UNDER permission flag for this, but for now,
    2570                 :          * demand that creator of a child table own the parent.
    2571                 :          */
    2572 GNC        4448 :         if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
    2573 UIC           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
    2574               0 :                            RelationGetRelationName(relation));
    2575                 : 
    2576 CBC        4448 :         tupleDesc = RelationGetDescr(relation);
    2577            4448 :         constr = tupleDesc->constr;
    2578 ECB             : 
    2579                 :         /*
    2580                 :          * newattmap->attnums[] will contain the child-table attribute numbers
    2581                 :          * for the attributes of this parent table.  (They are not the same
    2582                 :          * for parents after the first one, nor if we have dropped columns.)
    2583                 :          */
    2584 GIC        4448 :         newattmap = make_attrmap(tupleDesc->natts);
    2585                 : 
    2586                 :         /* We can't process inherited defaults until newattmap is complete. */
    2587            4448 :         inherited_defaults = cols_with_defaults = NIL;
    2588 ECB             : 
    2589                 :         /*
    2590                 :          * All columns that are part of the parent's primary key need to be
    2591                 :          * NOT NULL; if partition just the attnotnull bit, otherwise a full
    2592                 :          * constraint (if they don't have one already).  Also, we request
    2593                 :          * attnotnull on columns that have a NOT NULL constraint that's not
    2594                 :          * marked NO INHERIT.
    2595                 :          */
    2596 GNC        4448 :         pkattrs = RelationGetIndexAttrBitmap(relation,
    2597                 :                                              INDEX_ATTR_BITMAP_PRIMARY_KEY);
    2598            4448 :         nnconstrs = RelationGetNotNullConstraints(relation, true);
    2599            4749 :         foreach(lc1, nnconstrs)
    2600             301 :             nncols = bms_add_member(nncols,
    2601             301 :                                     ((CookedConstraint *) lfirst(lc1))->attnum);
    2602                 : 
    2603 CBC       13432 :         for (parent_attno = 1; parent_attno <= tupleDesc->natts;
    2604            8984 :              parent_attno++)
    2605 EUB             :         {
    2606 GIC        8996 :             Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
    2607                 :                                                         parent_attno - 1);
    2608            8996 :             char       *attributeName = NameStr(attribute->attname);
    2609                 :             int         exist_attno;
    2610                 :             ColumnDef  *def;
    2611                 : 
    2612                 :             /*
    2613                 :              * Ignore dropped columns in the parent.
    2614 ECB             :              */
    2615 CBC        8996 :             if (attribute->attisdropped)
    2616 GIC          96 :                 continue;       /* leave newattmap->attnums entry as zero */
    2617 ECB             : 
    2618                 :             /*
    2619                 :              * Does it conflict with some previously inherited column?
    2620                 :              */
    2621 GIC        8900 :             exist_attno = findAttrByName(attributeName, inhSchema);
    2622            8900 :             if (exist_attno > 0)
    2623 ECB             :             {
    2624                 :                 Oid         defTypeId;
    2625                 :                 int32       deftypmod;
    2626                 :                 Oid         defCollId;
    2627                 : 
    2628                 :                 /*
    2629                 :                  * Yes, try to merge the two column definitions.
    2630                 :                  */
    2631 GIC         112 :                 ereport(NOTICE,
    2632 ECB             :                         (errmsg("merging multiple inherited definitions of column \"%s\"",
    2633                 :                                 attributeName)));
    2634 GBC         112 :                 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
    2635                 : 
    2636                 :                 /*
    2637                 :                  * Must have the same type and typmod
    2638                 :                  */
    2639 GIC         112 :                 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
    2640             112 :                 if (defTypeId != attribute->atttypid ||
    2641             112 :                     deftypmod != attribute->atttypmod)
    2642 UIC           0 :                     ereport(ERROR,
    2643                 :                             (errcode(ERRCODE_DATATYPE_MISMATCH),
    2644                 :                              errmsg("inherited column \"%s\" has a type conflict",
    2645                 :                                     attributeName),
    2646                 :                              errdetail("%s versus %s",
    2647                 :                                        format_type_with_typemod(defTypeId,
    2648 ECB             :                                                                 deftypmod),
    2649 EUB             :                                        format_type_with_typemod(attribute->atttypid,
    2650                 :                                                                 attribute->atttypmod))));
    2651                 : 
    2652                 :                 /*
    2653                 :                  * Must have the same collation
    2654                 :                  */
    2655 GIC         112 :                 defCollId = GetColumnDefCollation(NULL, def, defTypeId);
    2656 CBC         112 :                 if (defCollId != attribute->attcollation)
    2657 LBC           0 :                     ereport(ERROR,
    2658                 :                             (errcode(ERRCODE_COLLATION_MISMATCH),
    2659                 :                              errmsg("inherited column \"%s\" has a collation conflict",
    2660                 :                                     attributeName),
    2661                 :                              errdetail("\"%s\" versus \"%s\"",
    2662                 :                                        get_collation_name(defCollId),
    2663                 :                                        get_collation_name(attribute->attcollation))));
    2664 ECB             : 
    2665                 :                 /*
    2666                 :                  * Copy/check storage parameter
    2667                 :                  */
    2668 GIC         112 :                 if (def->storage == 0)
    2669 LBC           0 :                     def->storage = attribute->attstorage;
    2670 GIC         112 :                 else if (def->storage != attribute->attstorage)
    2671               3 :                     ereport(ERROR,
    2672                 :                             (errcode(ERRCODE_DATATYPE_MISMATCH),
    2673                 :                              errmsg("inherited column \"%s\" has a storage parameter conflict",
    2674                 :                                     attributeName),
    2675                 :                              errdetail("%s versus %s",
    2676                 :                                        storage_name(def->storage),
    2677                 :                                        storage_name(attribute->attstorage))));
    2678 ECB             : 
    2679                 :                 /*
    2680                 :                  * Copy/check compression parameter
    2681                 :                  */
    2682 CBC         109 :                 if (CompressionMethodIsValid(attribute->attcompression))
    2683 ECB             :                 {
    2684                 :                     const char *compression =
    2685 CBC           3 :                     GetCompressionMethodName(attribute->attcompression);
    2686                 : 
    2687               3 :                     if (def->compression == NULL)
    2688 LBC           0 :                         def->compression = pstrdup(compression);
    2689 GIC           3 :                     else if (strcmp(def->compression, compression) != 0)
    2690 CBC           3 :                         ereport(ERROR,
    2691                 :                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    2692 ECB             :                                  errmsg("column \"%s\" has a compression method conflict",
    2693                 :                                         attributeName),
    2694                 :                                  errdetail("%s versus %s", def->compression, compression)));
    2695                 :                 }
    2696                 : 
    2697                 :                 /*
    2698                 :                  * In regular inheritance, columns in the parent's primary key
    2699                 :                  * get an extra CHECK (NOT NULL) constraint.  Partitioning
    2700                 :                  * doesn't need this, because the PK itself is going to be
    2701                 :                  * cloned to the partition.
    2702                 :                  */
    2703 GNC         212 :                 if (!is_partition &&
    2704             106 :                     bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
    2705                 :                                   pkattrs))
    2706                 :                 {
    2707                 :                     CookedConstraint *nn;
    2708                 : 
    2709 UNC           0 :                     nn = palloc(sizeof(CookedConstraint));
    2710               0 :                     nn->contype = CONSTR_NOTNULL;
    2711               0 :                     nn->conoid = InvalidOid;
    2712               0 :                     nn->name = NULL;
    2713               0 :                     nn->attnum = exist_attno;
    2714               0 :                     nn->expr = NULL;
    2715               0 :                     nn->skip_validation = false;
    2716               0 :                     nn->is_local = false;
    2717               0 :                     nn->inhcount = 1;
    2718               0 :                     nn->is_no_inherit = false;
    2719                 : 
    2720               0 :                     nnconstraints = lappend(nnconstraints, nn);
    2721                 :                 }
    2722                 : 
    2723                 :                 /*
    2724                 :                  * mark attnotnull if parent has it and it's not NO INHERIT
    2725                 :                  */
    2726 GNC         202 :                 if (bms_is_member(parent_attno, nncols) ||
    2727              96 :                     bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
    2728                 :                                   pkattrs))
    2729              10 :                     def->is_not_null = true;
    2730                 : 
    2731                 :                 /*
    2732                 :                  * Check for GENERATED conflicts
    2733                 :                  */
    2734 GIC         106 :                 if (def->generated != attribute->attgenerated)
    2735 CBC           6 :                     ereport(ERROR,
    2736 ECB             :                             (errcode(ERRCODE_DATATYPE_MISMATCH),
    2737                 :                              errmsg("inherited column \"%s\" has a generation conflict",
    2738                 :                                     attributeName)));
    2739                 : 
    2740                 :                 /*
    2741                 :                  * Default and other constraints are handled below
    2742                 :                  */
    2743                 : 
    2744 GNC         100 :                 def->inhcount++;
    2745             100 :                 if (def->inhcount < 0)
    2746 UNC           0 :                     ereport(ERROR,
    2747                 :                             errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    2748                 :                             errmsg("too many inheritance parents"));
    2749                 : 
    2750 GNC         100 :                 newattmap->attnums[parent_attno - 1] = exist_attno;
    2751                 :             }
    2752                 :             else
    2753                 :             {
    2754                 :                 /*
    2755                 :                  * No, create a new inherited column
    2756                 :                  */
    2757 CBC        8788 :                 def = makeNode(ColumnDef);
    2758 GIC        8788 :                 def->colname = pstrdup(attributeName);
    2759            8788 :                 def->typeName = makeTypeNameFromOid(attribute->atttypid,
    2760 ECB             :                                                     attribute->atttypmod);
    2761 GIC        8788 :                 def->inhcount = 1;
    2762            8788 :                 def->is_local = false;
    2763                 :                 /* mark attnotnull if parent has it and it's not NO INHERIT */
    2764 GNC       17285 :                 if (bms_is_member(parent_attno, nncols) ||
    2765            8497 :                     bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
    2766                 :                                   pkattrs))
    2767             831 :                     def->is_not_null = true;
    2768 GIC        8788 :                 def->is_from_type = false;
    2769 CBC        8788 :                 def->storage = attribute->attstorage;
    2770            8788 :                 def->raw_default = NULL;
    2771            8788 :                 def->cooked_default = NULL;
    2772 GBC        8788 :                 def->generated = attribute->attgenerated;
    2773 GIC        8788 :                 def->collClause = NULL;
    2774            8788 :                 def->collOid = attribute->attcollation;
    2775            8788 :                 def->constraints = NIL;
    2776            8788 :                 def->location = -1;
    2777            8788 :                 if (CompressionMethodIsValid(attribute->attcompression))
    2778               9 :                     def->compression =
    2779               9 :                         pstrdup(GetCompressionMethodName(attribute->attcompression));
    2780                 :                 else
    2781            8779 :                     def->compression = NULL;
    2782            8788 :                 inhSchema = lappend(inhSchema, def);
    2783            8788 :                 newattmap->attnums[parent_attno - 1] = ++child_attno;
    2784                 : 
    2785                 :                 /*
    2786                 :                  * In regular inheritance, columns in the parent's primary key
    2787                 :                  * get an extra NOT NULL constraint.  Partitioning doesn't
    2788                 :                  * need this, because the PK itself is going to be cloned to
    2789                 :                  * the partition.
    2790                 :                  */
    2791 GNC       10386 :                 if (!is_partition &&
    2792            1598 :                     bms_is_member(parent_attno -
    2793                 :                                   FirstLowInvalidHeapAttributeNumber,
    2794                 :                                   pkattrs))
    2795                 :                 {
    2796                 :                     CookedConstraint *nn;
    2797                 : 
    2798              95 :                     nn = palloc(sizeof(CookedConstraint));
    2799              95 :                     nn->contype = CONSTR_NOTNULL;
    2800              95 :                     nn->conoid = InvalidOid;
    2801              95 :                     nn->name = NULL;
    2802              95 :                     nn->attnum = newattmap->attnums[parent_attno - 1];
    2803              95 :                     nn->expr = NULL;
    2804              95 :                     nn->skip_validation = false;
    2805              95 :                     nn->is_local = false;
    2806              95 :                     nn->inhcount = 1;
    2807              95 :                     nn->is_no_inherit = false;
    2808                 : 
    2809              95 :                     nnconstraints = lappend(nnconstraints, nn);
    2810                 :                 }
    2811                 :             }
    2812 ECB             : 
    2813                 :             /*
    2814                 :              * Locate default/generation expression if any
    2815                 :              */
    2816 GIC        8888 :             if (attribute->atthasdef)
    2817                 :             {
    2818             260 :                 Node       *this_default = NULL;
    2819                 : 
    2820                 :                 /* Find default in constraint structure */
    2821             260 :                 if (constr != NULL)
    2822                 :                 {
    2823             260 :                     AttrDefault *attrdef = constr->defval;
    2824                 : 
    2825 CBC         284 :                     for (int i = 0; i < constr->num_defval; i++)
    2826 EUB             :                     {
    2827 CBC         284 :                         if (attrdef[i].adnum == parent_attno)
    2828 ECB             :                         {
    2829 GIC         260 :                             this_default = stringToNode(attrdef[i].adbin);
    2830             260 :                             break;
    2831                 :                         }
    2832                 :                     }
    2833                 :                 }
    2834             260 :                 if (this_default == NULL)
    2835 UIC           0 :                     elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
    2836                 :                          parent_attno, RelationGetRelationName(relation));
    2837                 : 
    2838                 :                 /*
    2839 ECB             :                  * If it's a GENERATED default, it might contain Vars that
    2840                 :                  * need to be mapped to the inherited column(s)' new numbers.
    2841                 :                  * We can't do that till newattmap is ready, so just remember
    2842                 :                  * all the inherited default expressions for the moment.
    2843                 :                  */
    2844 CBC         260 :                 inherited_defaults = lappend(inherited_defaults, this_default);
    2845 GBC         260 :                 cols_with_defaults = lappend(cols_with_defaults, def);
    2846 ECB             :             }
    2847                 :         }
    2848                 : 
    2849                 :         /*
    2850                 :          * Now process any inherited default expressions, adjusting attnos
    2851                 :          * using the completed newattmap map.
    2852                 :          */
    2853 GIC        4696 :         forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
    2854                 :         {
    2855             260 :             Node       *this_default = (Node *) lfirst(lc1);
    2856             260 :             ColumnDef  *def = (ColumnDef *) lfirst(lc2);
    2857                 :             bool        found_whole_row;
    2858                 : 
    2859                 :             /* Adjust Vars to match new table's column numbering */
    2860 CBC         260 :             this_default = map_variable_attnos(this_default,
    2861 ECB             :                                                1, 0,
    2862                 :                                                newattmap,
    2863                 :                                                InvalidOid, &found_whole_row);
    2864                 : 
    2865                 :             /*
    2866 EUB             :              * For the moment we have to reject whole-row variables.  We could
    2867                 :              * convert them, if we knew the new table's rowtype OID, but that
    2868                 :              * hasn't been assigned yet.  (A variable could only appear in a
    2869                 :              * generation expression, so the error message is correct.)
    2870                 :              */
    2871 GBC         260 :             if (found_whole_row)
    2872 UBC           0 :                 ereport(ERROR,
    2873 EUB             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2874                 :                          errmsg("cannot convert whole-row table reference"),
    2875                 :                          errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
    2876                 :                                    def->colname,
    2877                 :                                    RelationGetRelationName(relation))));
    2878                 : 
    2879                 :             /*
    2880                 :              * If we already had a default from some prior parent, check to
    2881                 :              * see if they are the same.  If so, no problem; if not, mark the
    2882                 :              * column as having a bogus default.  Below, we will complain if
    2883 ECB             :              * the bogus default isn't overridden by the child schema.
    2884                 :              */
    2885 GIC         260 :             Assert(def->raw_default == NULL);
    2886 CBC         260 :             if (def->cooked_default == NULL)
    2887 GIC         245 :                 def->cooked_default = this_default;
    2888              15 :             else if (!equal(def->cooked_default, this_default))
    2889                 :             {
    2890              12 :                 def->cooked_default = &bogus_marker;
    2891 CBC          12 :                 have_bogus_defaults = true;
    2892 ECB             :             }
    2893                 :         }
    2894                 : 
    2895                 :         /*
    2896                 :          * Now copy the CHECK constraints of this parent, adjusting attnos
    2897                 :          * using the completed newattmap map.  Identically named constraints
    2898                 :          * are merged if possible, else we throw error.
    2899                 :          */
    2900 GIC        4436 :         if (constr && constr->num_check > 0)
    2901 ECB             :         {
    2902 CBC          98 :             ConstrCheck *check = constr->check;
    2903 EUB             :             int         i;
    2904                 : 
    2905 GIC         211 :             for (i = 0; i < constr->num_check; i++)
    2906                 :             {
    2907 CBC         113 :                 char       *name = check[i].ccname;
    2908                 :                 Node       *expr;
    2909                 :                 bool        found_whole_row;
    2910                 : 
    2911                 :                 /* ignore if the constraint is non-inheritable */
    2912 GIC         113 :                 if (check[i].ccnoinherit)
    2913              21 :                     continue;
    2914 ECB             : 
    2915                 :                 /* Adjust Vars to match new table's column numbering */
    2916 CBC          92 :                 expr = map_variable_attnos(stringToNode(check[i].ccbin),
    2917                 :                                            1, 0,
    2918 ECB             :                                            newattmap,
    2919                 :                                            InvalidOid, &found_whole_row);
    2920                 : 
    2921                 :                 /*
    2922                 :                  * For the moment we have to reject whole-row variables. We
    2923                 :                  * could convert them, if we knew the new table's rowtype OID,
    2924                 :                  * but that hasn't been assigned yet.
    2925                 :                  */
    2926 CBC          92 :                 if (found_whole_row)
    2927 LBC           0 :                     ereport(ERROR,
    2928 ECB             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2929                 :                              errmsg("cannot convert whole-row table reference"),
    2930                 :                              errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
    2931                 :                                        name,
    2932                 :                                        RelationGetRelationName(relation))));
    2933                 : 
    2934                 :                 /* check for duplicate */
    2935 CBC          92 :                 if (!MergeCheckConstraint(constraints, name, expr))
    2936 ECB             :                 {
    2937                 :                     /* nope, this is a new one */
    2938                 :                     CookedConstraint *cooked;
    2939                 : 
    2940 CBC          86 :                     cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
    2941 GIC          86 :                     cooked->contype = CONSTR_CHECK;
    2942              86 :                     cooked->conoid = InvalidOid; /* until created */
    2943              86 :                     cooked->name = pstrdup(name);
    2944              86 :                     cooked->attnum = 0; /* not used for constraints */
    2945              86 :                     cooked->expr = expr;
    2946              86 :                     cooked->skip_validation = false;
    2947              86 :                     cooked->is_local = false;
    2948 CBC          86 :                     cooked->inhcount = 1;
    2949              86 :                     cooked->is_no_inherit = false;
    2950 GIC          86 :                     constraints = lappend(constraints, cooked);
    2951                 :                 }
    2952                 :             }
    2953                 :         }
    2954                 : 
    2955                 :         /*
    2956                 :          * Also copy the NOT NULL constraints from this parent.  The
    2957                 :          * attnotnull markings were already installed above.
    2958                 :          */
    2959 GNC        4737 :         foreach(lc1, nnconstrs)
    2960                 :         {
    2961             301 :             CookedConstraint *nn = lfirst(lc1);
    2962                 : 
    2963             301 :             nn->attnum = newattmap->attnums[nn->attnum - 1];
    2964                 : 
    2965             301 :             nnconstraints = lappend(nnconstraints, nn);
    2966                 :         }
    2967                 : 
    2968 CBC        4436 :         free_attrmap(newattmap);
    2969 ECB             : 
    2970                 :         /*
    2971                 :          * Close the parent rel, but keep our lock on it until xact commit.
    2972                 :          * That will prevent someone else from deleting or ALTERing the parent
    2973                 :          * before the child is committed.
    2974                 :          */
    2975 CBC        4436 :         table_close(relation, NoLock);
    2976 ECB             :     }
    2977                 : 
    2978                 :     /*
    2979                 :      * If we had no inherited attributes, the result schema is just the
    2980                 :      * explicitly declared columns.  Otherwise, we need to merge the declared
    2981                 :      * columns into the inherited schema list.  Although, we never have any
    2982                 :      * explicitly declared columns if the table is a partition.
    2983                 :      */
    2984 GIC       62805 :     if (inhSchema != NIL)
    2985                 :     {
    2986 CBC        4318 :         int         schema_attno = 0;
    2987                 : 
    2988            4698 :         foreach(entry, schema)
    2989                 :         {
    2990 GIC         404 :             ColumnDef  *newdef = lfirst(entry);
    2991 CBC         404 :             char       *attributeName = newdef->colname;
    2992                 :             int         exist_attno;
    2993 ECB             : 
    2994 GIC         404 :             schema_attno++;
    2995 ECB             : 
    2996                 :             /*
    2997                 :              * Does it conflict with some previously inherited column?
    2998                 :              */
    2999 CBC         404 :             exist_attno = findAttrByName(attributeName, inhSchema);
    3000             404 :             if (exist_attno > 0)
    3001                 :             {
    3002                 :                 ColumnDef  *def;
    3003                 :                 Oid         defTypeId,
    3004 ECB             :                             newTypeId;
    3005 EUB             :                 int32       deftypmod,
    3006                 :                             newtypmod;
    3007                 :                 Oid         defcollid,
    3008                 :                             newcollid;
    3009                 : 
    3010                 :                 /*
    3011                 :                  * Partitions have only one parent and have no column
    3012                 :                  * definitions of their own, so conflict should never occur.
    3013                 :                  */
    3014 CBC         125 :                 Assert(!is_partition);
    3015 ECB             : 
    3016                 :                 /*
    3017                 :                  * Yes, try to merge the two column definitions.
    3018                 :                  */
    3019 GIC         125 :                 if (exist_attno == schema_attno)
    3020             114 :                     ereport(NOTICE,
    3021                 :                             (errmsg("merging column \"%s\" with inherited definition",
    3022 ECB             :                                     attributeName)));
    3023                 :                 else
    3024 CBC          11 :                     ereport(NOTICE,
    3025 ECB             :                             (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
    3026                 :                              errdetail("User-specified column moved to the position of the inherited column.")));
    3027 GIC         125 :                 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
    3028                 : 
    3029                 :                 /*
    3030                 :                  * Must have the same type and typmod
    3031                 :                  */
    3032             125 :                 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
    3033 CBC         125 :                 typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
    3034 GIC         125 :                 if (defTypeId != newTypeId || deftypmod != newtypmod)
    3035               6 :                     ereport(ERROR,
    3036                 :                             (errcode(ERRCODE_DATATYPE_MISMATCH),
    3037                 :                              errmsg("column \"%s\" has a type conflict",
    3038                 :                                     attributeName),
    3039                 :                              errdetail("%s versus %s",
    3040                 :                                        format_type_with_typemod(defTypeId,
    3041                 :                                                                 deftypmod),
    3042                 :                                        format_type_with_typemod(newTypeId,
    3043                 :                                                                 newtypmod))));
    3044                 : 
    3045                 :                 /*
    3046                 :                  * Must have the same collation
    3047                 :                  */
    3048 CBC         119 :                 defcollid = GetColumnDefCollation(NULL, def, defTypeId);
    3049 GBC         119 :                 newcollid = GetColumnDefCollation(NULL, newdef, newTypeId);
    3050 GIC         119 :                 if (defcollid != newcollid)
    3051               3 :                     ereport(ERROR,
    3052                 :                             (errcode(ERRCODE_COLLATION_MISMATCH),
    3053                 :                              errmsg("column \"%s\" has a collation conflict",
    3054                 :                                     attributeName),
    3055                 :                              errdetail("\"%s\" versus \"%s\"",
    3056                 :                                        get_collation_name(defcollid),
    3057                 :                                        get_collation_name(newcollid))));
    3058                 : 
    3059                 :                 /*
    3060                 :                  * Identity is never inherited.  The new column can have an
    3061                 :                  * identity definition, so we always just take that one.
    3062 ECB             :                  */
    3063 CBC         116 :                 def->identity = newdef->identity;
    3064 ECB             : 
    3065                 :                 /*
    3066                 :                  * Copy storage parameter
    3067                 :                  */
    3068 GIC         116 :                 if (def->storage == 0)
    3069 LBC           0 :                     def->storage = newdef->storage;
    3070 CBC         116 :                 else if (newdef->storage != 0 && def->storage != newdef->storage)
    3071 GIC           3 :                     ereport(ERROR,
    3072                 :                             (errcode(ERRCODE_DATATYPE_MISMATCH),
    3073                 :                              errmsg("column \"%s\" has a storage parameter conflict",
    3074                 :                                     attributeName),
    3075                 :                              errdetail("%s versus %s",
    3076                 :                                        storage_name(def->storage),
    3077                 :                                        storage_name(newdef->storage))));
    3078                 : 
    3079                 :                 /*
    3080                 :                  * Copy compression parameter
    3081                 :                  */
    3082             113 :                 if (def->compression == NULL)
    3083 CBC         110 :                     def->compression = newdef->compression;
    3084 GIC           3 :                 else if (newdef->compression != NULL)
    3085                 :                 {
    3086 CBC           3 :                     if (strcmp(def->compression, newdef->compression) != 0)
    3087 GIC           3 :                         ereport(ERROR,
    3088 ECB             :                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3089                 :                                  errmsg("column \"%s\" has a compression method conflict",
    3090                 :                                         attributeName),
    3091                 :                                  errdetail("%s versus %s", def->compression, newdef->compression)));
    3092                 :                 }
    3093                 : 
    3094                 :                 /*
    3095                 :                  * Merge of NOT NULL constraints = OR 'em together
    3096                 :                  */
    3097 CBC         110 :                 def->is_not_null |= newdef->is_not_null;
    3098                 : 
    3099                 :                 /*
    3100                 :                  * Check for conflicts related to generated columns.
    3101                 :                  *
    3102                 :                  * If the parent column is generated, the child column will be
    3103                 :                  * made a generated column if it isn't already.  If it is a
    3104                 :                  * generated column, we'll take its generation expression in
    3105                 :                  * preference to the parent's.  We must check that the child
    3106                 :                  * column doesn't specify a default value or identity, which
    3107                 :                  * matches the rules for a single column in parse_util.c.
    3108                 :                  *
    3109                 :                  * Conversely, if the parent column is not generated, the
    3110                 :                  * child column can't be either.  (We used to allow that, but
    3111                 :                  * it results in being able to override the generation
    3112                 :                  * expression via UPDATEs through the parent.)
    3113                 :                  */
    3114 GIC         110 :                 if (def->generated)
    3115                 :                 {
    3116              13 :                     if (newdef->raw_default && !newdef->generated)
    3117               3 :                         ereport(ERROR,
    3118 ECB             :                                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3119                 :                                  errmsg("column \"%s\" inherits from generated column but specifies default",
    3120                 :                                         def->colname)));
    3121 CBC          10 :                     if (newdef->identity)
    3122               3 :                         ereport(ERROR,
    3123 ECB             :                                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3124                 :                                  errmsg("column \"%s\" inherits from generated column but specifies identity",
    3125                 :                                         def->colname)));
    3126                 :                 }
    3127                 :                 else
    3128                 :                 {
    3129 GIC          97 :                     if (newdef->generated)
    3130 GNC           3 :                         ereport(ERROR,
    3131                 :                                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3132                 :                                  errmsg("child column \"%s\" specifies generation expression",
    3133                 :                                         def->colname),
    3134                 :                                  errhint("A child table column cannot be generated unless its parent column is.")));
    3135                 :                 }
    3136 ECB             : 
    3137                 :                 /*
    3138                 :                  * If new def has a default, override previous default
    3139                 :                  */
    3140 CBC         101 :                 if (newdef->raw_default != NULL)
    3141                 :                 {
    3142               9 :                     def->raw_default = newdef->raw_default;
    3143 GIC           9 :                     def->cooked_default = newdef->cooked_default;
    3144 ECB             :                 }
    3145                 : 
    3146                 :                 /* Mark the column as locally defined */
    3147 GNC         101 :                 def->is_local = true;
    3148                 :             }
    3149                 :             else
    3150 ECB             :             {
    3151                 :                 /*
    3152                 :                  * No, attach new column to result schema
    3153                 :                  */
    3154 GIC         279 :                 inhSchema = lappend(inhSchema, newdef);
    3155                 :             }
    3156                 :         }
    3157 ECB             : 
    3158 GIC        4294 :         schema = inhSchema;
    3159                 : 
    3160                 :         /*
    3161                 :          * Check that we haven't exceeded the legal # of columns after merging
    3162                 :          * in inherited columns.
    3163                 :          */
    3164            4294 :         if (list_length(schema) > MaxHeapAttributeNumber)
    3165 UIC           0 :             ereport(ERROR,
    3166 ECB             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    3167                 :                      errmsg("tables can have at most %d columns",
    3168                 :                             MaxHeapAttributeNumber)));
    3169                 :     }
    3170                 : 
    3171                 :     /*
    3172                 :      * Now that we have the column definition list for a partition, we can
    3173                 :      * check whether the columns referenced in the column constraint specs
    3174                 :      * actually exist.
    3175                 :      */
    3176 GIC       62781 :     if (is_partition)
    3177                 :     {
    3178            3681 :         foreach(entry, saved_schema)
    3179                 :         {
    3180 CBC         100 :             ColumnDef  *restdef = lfirst(entry);
    3181             100 :             bool        found = false;
    3182                 :             ListCell   *l;
    3183                 : 
    3184 GIC         372 :             foreach(l, schema)
    3185                 :             {
    3186             278 :                 ColumnDef  *coldef = lfirst(l);
    3187                 : 
    3188             278 :                 if (strcmp(coldef->colname, restdef->colname) == 0)
    3189                 :                 {
    3190             100 :                     found = true;
    3191             100 :                     coldef->is_not_null |= restdef->is_not_null;
    3192                 : 
    3193                 :                     /*
    3194                 :                      * Check for conflicts related to generated columns.
    3195                 :                      *
    3196                 :                      * Same rules as above: generated-ness has to match the
    3197                 :                      * parent, but the contents of the generation expression
    3198                 :                      * can be different.
    3199                 :                      */
    3200 GNC         100 :                     if (coldef->generated)
    3201                 :                     {
    3202              49 :                         if (restdef->raw_default && !restdef->generated)
    3203               3 :                             ereport(ERROR,
    3204                 :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3205                 :                                      errmsg("column \"%s\" inherits from generated column but specifies default",
    3206                 :                                             restdef->colname)));
    3207              46 :                         if (restdef->identity)
    3208 UNC           0 :                             ereport(ERROR,
    3209                 :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3210                 :                                      errmsg("column \"%s\" inherits from generated column but specifies identity",
    3211                 :                                             restdef->colname)));
    3212                 :                     }
    3213                 :                     else
    3214                 :                     {
    3215 GNC          51 :                         if (restdef->generated)
    3216               3 :                             ereport(ERROR,
    3217                 :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3218                 :                                      errmsg("child column \"%s\" specifies generation expression",
    3219                 :                                             restdef->colname),
    3220                 :                                      errhint("A child table column cannot be generated unless its parent column is.")));
    3221                 :                     }
    3222                 : 
    3223                 :                     /*
    3224                 :                      * Override the parent's default value for this column
    3225 ECB             :                      * (coldef->cooked_default) with the partition's local
    3226                 :                      * definition (restdef->raw_default), if there's one. It
    3227                 :                      * should be physically impossible to get a cooked default
    3228                 :                      * in the local definition or a raw default in the
    3229                 :                      * inherited definition, but make sure they're nulls, for
    3230                 :                      * future-proofing.
    3231                 :                      */
    3232 GIC          94 :                     Assert(restdef->cooked_default == NULL);
    3233              94 :                     Assert(coldef->raw_default == NULL);
    3234              94 :                     if (restdef->raw_default)
    3235 ECB             :                     {
    3236 GIC          58 :                         coldef->raw_default = restdef->raw_default;
    3237              58 :                         coldef->cooked_default = NULL;
    3238 ECB             :                     }
    3239                 :                 }
    3240                 :             }
    3241                 : 
    3242                 :             /* complain for constraints on columns not in parent */
    3243 CBC          94 :             if (!found)
    3244 LBC           0 :                 ereport(ERROR,
    3245 ECB             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    3246                 :                          errmsg("column \"%s\" does not exist",
    3247                 :                                 restdef->colname)));
    3248                 :         }
    3249                 :     }
    3250                 : 
    3251                 :     /*
    3252                 :      * If we found any conflicting parent default values, check to make sure
    3253                 :      * they were overridden by the child.
    3254                 :      */
    3255 GIC       62775 :     if (have_bogus_defaults)
    3256                 :     {
    3257              27 :         foreach(entry, schema)
    3258                 :         {
    3259 CBC          21 :             ColumnDef  *def = lfirst(entry);
    3260 ECB             : 
    3261 CBC          21 :             if (def->cooked_default == &bogus_marker)
    3262 ECB             :             {
    3263 GIC           6 :                 if (def->generated)
    3264               3 :                     ereport(ERROR,
    3265                 :                             (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3266                 :                              errmsg("column \"%s\" inherits conflicting generation expressions",
    3267                 :                                     def->colname),
    3268                 :                              errhint("To resolve the conflict, specify a generation expression explicitly.")));
    3269                 :                 else
    3270               3 :                     ereport(ERROR,
    3271                 :                             (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3272                 :                              errmsg("column \"%s\" inherits conflicting default values",
    3273                 :                                     def->colname),
    3274                 :                              errhint("To resolve the conflict, specify a default explicitly.")));
    3275 ECB             :             }
    3276                 :         }
    3277                 :     }
    3278                 : 
    3279 GIC       62769 :     *supconstr = constraints;
    3280 GNC       62769 :     *supnotnulls = nnconstraints;
    3281                 : 
    3282 CBC       62769 :     return schema;
    3283 EUB             : }
    3284 ECB             : 
    3285                 : 
    3286                 : /*
    3287                 :  * MergeCheckConstraint
    3288                 :  *      Try to merge an inherited CHECK constraint with previous ones
    3289                 :  *
    3290                 :  * If we inherit identically-named constraints from multiple parents, we must
    3291                 :  * merge them, or throw an error if they don't have identical definitions.
    3292                 :  *
    3293                 :  * constraints is a list of CookedConstraint structs for previous constraints.
    3294                 :  *
    3295                 :  * Returns true if merged (constraint is a duplicate), or false if it's
    3296                 :  * got a so-far-unique name, or throws error if conflict.
    3297                 :  */
    3298                 : static bool
    3299 GIC          92 : MergeCheckConstraint(List *constraints, char *name, Node *expr)
    3300 ECB             : {
    3301                 :     ListCell   *lc;
    3302                 : 
    3303 GIC         107 :     foreach(lc, constraints)
    3304                 :     {
    3305              21 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
    3306                 : 
    3307              21 :         Assert(ccon->contype == CONSTR_CHECK);
    3308                 : 
    3309                 :         /* Non-matching names never conflict */
    3310              21 :         if (strcmp(ccon->name, name) != 0)
    3311 CBC          15 :             continue;
    3312                 : 
    3313 GIC           6 :         if (equal(expr, ccon->expr))
    3314                 :         {
    3315                 :             /* OK to merge */
    3316               6 :             ccon->inhcount++;
    3317 GNC           6 :             if (ccon->inhcount < 0)
    3318 UNC           0 :                 ereport(ERROR,
    3319                 :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3320                 :                         errmsg("too many inheritance parents"));
    3321 GIC           6 :             return true;
    3322                 :         }
    3323                 : 
    3324 UIC           0 :         ereport(ERROR,
    3325                 :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    3326                 :                  errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
    3327                 :                         name)));
    3328                 :     }
    3329                 : 
    3330 GIC          86 :     return false;
    3331                 : }
    3332 ECB             : 
    3333                 : /*
    3334                 :  * RelationGetNotNullConstraints -- get list of NOT NULL constraints
    3335                 :  *
    3336                 :  * Caller can request cooked constraints, or raw.
    3337                 :  *
    3338                 :  * This is seldom needed, so we just scan pg_constraint each time.
    3339                 :  *
    3340                 :  * XXX This is only used to create derived tables, so NO INHERIT constraints
    3341                 :  * are always skipped.
    3342                 :  */
    3343                 : List *
    3344 GNC        4761 : RelationGetNotNullConstraints(Relation relation, bool cooked)
    3345                 : {
    3346            4761 :     List       *notnulls = NIL;
    3347                 :     Relation    constrRel;
    3348                 :     HeapTuple   htup;
    3349                 :     SysScanDesc conscan;
    3350                 :     ScanKeyData skey;
    3351                 : 
    3352            4761 :     constrRel = table_open(ConstraintRelationId, AccessShareLock);
    3353            4761 :     ScanKeyInit(&skey,
    3354                 :                 Anum_pg_constraint_conrelid,
    3355                 :                 BTEqualStrategyNumber, F_OIDEQ,
    3356                 :                 ObjectIdGetDatum(RelationGetRelid(relation)));
    3357            4761 :     conscan = systable_beginscan(constrRel, ConstraintRelidTypidNameIndexId, true,
    3358                 :                                  NULL, 1, &skey);
    3359                 : 
    3360            6297 :     while (HeapTupleIsValid(htup = systable_getnext(conscan)))
    3361                 :     {
    3362            1536 :         Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(htup);
    3363                 :         AttrNumber  colnum;
    3364                 : 
    3365            1536 :         if (conForm->contype != CONSTRAINT_NOTNULL)
    3366            1149 :             continue;
    3367             387 :         if (conForm->connoinherit)
    3368               9 :             continue;
    3369                 : 
    3370             378 :         colnum = extractNotNullColumn(htup);
    3371                 : 
    3372             378 :         if (cooked)
    3373                 :         {
    3374                 :             CookedConstraint *cooked;
    3375                 : 
    3376             301 :             cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
    3377                 : 
    3378             301 :             cooked->contype = CONSTR_NOTNULL;
    3379             301 :             cooked->name = pstrdup(NameStr(conForm->conname));
    3380             301 :             cooked->attnum = colnum;
    3381             301 :             cooked->expr = NULL;
    3382             301 :             cooked->skip_validation = false;
    3383             301 :             cooked->is_local = true;
    3384             301 :             cooked->inhcount = 0;
    3385             301 :             cooked->is_no_inherit = conForm->connoinherit;
    3386                 : 
    3387             301 :             notnulls = lappend(notnulls, cooked);
    3388                 :         }
    3389                 :         else
    3390                 :         {
    3391                 :             Constraint *constr;
    3392                 : 
    3393              77 :             constr = makeNode(Constraint);
    3394              77 :             constr->contype = CONSTR_NOTNULL;
    3395              77 :             constr->conname = pstrdup(NameStr(conForm->conname));
    3396              77 :             constr->deferrable = false;
    3397              77 :             constr->initdeferred = false;
    3398              77 :             constr->location = -1;
    3399              77 :             constr->colname = get_attname(RelationGetRelid(relation),
    3400                 :                                           colnum, false);
    3401              77 :             constr->skip_validation = false;
    3402              77 :             constr->initially_valid = true;
    3403              77 :             notnulls = lappend(notnulls, constr);
    3404                 :         }
    3405                 :     }
    3406                 : 
    3407            4761 :     systable_endscan(conscan);
    3408            4761 :     table_close(constrRel, AccessShareLock);
    3409                 : 
    3410            4761 :     return notnulls;
    3411                 : }
    3412                 : 
    3413 ECB             : /*
    3414                 :  * StoreCatalogInheritance
    3415                 :  *      Updates the system catalogs with proper inheritance information.
    3416                 :  *
    3417                 :  * supers is a list of the OIDs of the new relation's direct ancestors.
    3418                 :  */
    3419                 : static void
    3420 GIC       62499 : StoreCatalogInheritance(Oid relationId, List *supers,
    3421                 :                         bool child_is_partition)
    3422                 : {
    3423                 :     Relation    relation;
    3424                 :     int32       seqNumber;
    3425                 :     ListCell   *entry;
    3426 ECB             : 
    3427                 :     /*
    3428                 :      * sanity checks
    3429                 :      */
    3430 GNC       62499 :     Assert(OidIsValid(relationId));
    3431                 : 
    3432 GIC       62499 :     if (supers == NIL)
    3433           58385 :         return;
    3434                 : 
    3435                 :     /*
    3436                 :      * Store INHERITS information in pg_inherits using direct ancestors only.
    3437 ECB             :      * Also enter dependencies on the direct ancestors, and make sure they are
    3438                 :      * marked with relhassubclass = true.
    3439                 :      *
    3440                 :      * (Once upon a time, both direct and indirect ancestors were found here
    3441                 :      * and then entered into pg_ipl.  Since that catalog doesn't exist
    3442                 :      * anymore, there's no need to look for indirect ancestors.)
    3443                 :      */
    3444 CBC        4114 :     relation = table_open(InheritsRelationId, RowExclusiveLock);
    3445                 : 
    3446 GIC        4114 :     seqNumber = 1;
    3447            8322 :     foreach(entry, supers)
    3448                 :     {
    3449            4208 :         Oid         parentOid = lfirst_oid(entry);
    3450                 : 
    3451 CBC        4208 :         StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
    3452                 :                                  child_is_partition);
    3453 GIC        4208 :         seqNumber++;
    3454                 :     }
    3455 ECB             : 
    3456 GIC        4114 :     table_close(relation, RowExclusiveLock);
    3457                 : }
    3458                 : 
    3459                 : /*
    3460                 :  * Make catalog entries showing relationId as being an inheritance child
    3461 ECB             :  * of parentOid.  inhRelation is the already-opened pg_inherits catalog.
    3462 EUB             :  */
    3463                 : static void
    3464 GIC        5238 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
    3465                 :                          int32 seqNumber, Relation inhRelation,
    3466                 :                          bool child_is_partition)
    3467                 : {
    3468                 :     ObjectAddress childobject,
    3469                 :                 parentobject;
    3470                 : 
    3471                 :     /* store the pg_inherits row */
    3472            5238 :     StoreSingleInheritance(relationId, parentOid, seqNumber);
    3473 ECB             : 
    3474                 :     /*
    3475                 :      * Store a dependency too
    3476                 :      */
    3477 CBC        5238 :     parentobject.classId = RelationRelationId;
    3478            5238 :     parentobject.objectId = parentOid;
    3479 GIC        5238 :     parentobject.objectSubId = 0;
    3480            5238 :     childobject.classId = RelationRelationId;
    3481 CBC        5238 :     childobject.objectId = relationId;
    3482 GIC        5238 :     childobject.objectSubId = 0;
    3483 ECB             : 
    3484 GIC        5238 :     recordDependencyOn(&childobject, &parentobject,
    3485 ECB             :                        child_dependency_type(child_is_partition));
    3486                 : 
    3487                 :     /*
    3488                 :      * Post creation hook of this inheritance. Since object_access_hook
    3489                 :      * doesn't take multiple object identifiers, we relay oid of parent
    3490                 :      * relation using auxiliary_id argument.
    3491                 :      */
    3492 GIC        5238 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
    3493                 :                                  relationId, 0,
    3494                 :                                  parentOid, false);
    3495                 : 
    3496                 :     /*
    3497 ECB             :      * Mark the parent as having subclasses.
    3498                 :      */
    3499 CBC        5238 :     SetRelationHasSubclass(parentOid, true);
    3500            5238 : }
    3501                 : 
    3502                 : /*
    3503                 :  * Look for an existing schema entry with the given name.
    3504 ECB             :  *
    3505 EUB             :  * Returns the index (starting with 1) if attribute already exists in schema,
    3506                 :  * 0 if it doesn't.
    3507                 :  */
    3508                 : static int
    3509 GIC        9304 : findAttrByName(const char *attributeName, List *schema)
    3510                 : {
    3511                 :     ListCell   *s;
    3512 CBC        9304 :     int         i = 1;
    3513 ECB             : 
    3514 GIC       16605 :     foreach(s, schema)
    3515                 :     {
    3516            7538 :         ColumnDef  *def = lfirst(s);
    3517                 : 
    3518            7538 :         if (strcmp(attributeName, def->colname) == 0)
    3519             237 :             return i;
    3520                 : 
    3521            7301 :         i++;
    3522                 :     }
    3523            9067 :     return 0;
    3524                 : }
    3525                 : 
    3526                 : 
    3527                 : /*
    3528                 :  * SetRelationHasSubclass
    3529 ECB             :  *      Set the value of the relation's relhassubclass field in pg_class.
    3530                 :  *
    3531                 :  * NOTE: caller must be holding an appropriate lock on the relation.
    3532                 :  * ShareUpdateExclusiveLock is sufficient.
    3533                 :  *
    3534                 :  * NOTE: an important side-effect of this operation is that an SI invalidation
    3535                 :  * message is sent out to all backends --- including me --- causing plans
    3536                 :  * referencing the relation to be rebuilt with the new list of children.
    3537                 :  * This must happen even if we find that no change is needed in the pg_class
    3538                 :  * row.
    3539                 :  */
    3540                 : void
    3541 GBC        6544 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
    3542                 : {
    3543                 :     Relation    relationRelation;
    3544                 :     HeapTuple   tuple;
    3545                 :     Form_pg_class classtuple;
    3546                 : 
    3547                 :     /*
    3548                 :      * Fetch a modifiable copy of the tuple, modify it, update pg_class.
    3549                 :      */
    3550 GIC        6544 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
    3551            6544 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
    3552 CBC        6544 :     if (!HeapTupleIsValid(tuple))
    3553 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", relationId);
    3554 CBC        6544 :     classtuple = (Form_pg_class) GETSTRUCT(tuple);
    3555                 : 
    3556            6544 :     if (classtuple->relhassubclass != relhassubclass)
    3557                 :     {
    3558            3248 :         classtuple->relhassubclass = relhassubclass;
    3559 GIC        3248 :         CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
    3560 ECB             :     }
    3561                 :     else
    3562                 :     {
    3563                 :         /* no need to change tuple, but force relcache rebuild anyway */
    3564 GIC        3296 :         CacheInvalidateRelcacheByTuple(tuple);
    3565                 :     }
    3566                 : 
    3567 CBC        6544 :     heap_freetuple(tuple);
    3568 GIC        6544 :     table_close(relationRelation, RowExclusiveLock);
    3569            6544 : }
    3570                 : 
    3571                 : /*
    3572                 :  * CheckRelationTableSpaceMove
    3573                 :  *      Check if relation can be moved to new tablespace.
    3574                 :  *
    3575                 :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3576 ECB             :  *
    3577                 :  * Returns true if the relation can be moved to the new tablespace; raises
    3578                 :  * an error if it is not possible to do the move; returns false if the move
    3579                 :  * would have no effect.
    3580                 :  */
    3581                 : bool
    3582 GIC         215 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
    3583                 : {
    3584                 :     Oid         oldTableSpaceId;
    3585                 : 
    3586                 :     /*
    3587                 :      * No work if no change in tablespace.  Note that MyDatabaseTableSpace is
    3588                 :      * stored as 0.
    3589                 :      */
    3590             215 :     oldTableSpaceId = rel->rd_rel->reltablespace;
    3591             215 :     if (newTableSpaceId == oldTableSpaceId ||
    3592             211 :         (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
    3593               5 :         return false;
    3594                 : 
    3595                 :     /*
    3596 ECB             :      * We cannot support moving mapped relations into different tablespaces.
    3597                 :      * (In particular this eliminates all shared catalogs.)
    3598                 :      */
    3599 GIC         210 :     if (RelationIsMapped(rel))
    3600 LBC           0 :         ereport(ERROR,
    3601                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3602 ECB             :                  errmsg("cannot move system relation \"%s\"",
    3603                 :                         RelationGetRelationName(rel))));
    3604                 : 
    3605                 :     /* Cannot move a non-shared relation into pg_global */
    3606 GIC         210 :     if (newTableSpaceId == GLOBALTABLESPACE_OID)
    3607 CBC           6 :         ereport(ERROR,
    3608 ECB             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3609                 :                  errmsg("only shared relations can be placed in pg_global tablespace")));
    3610                 : 
    3611                 :     /*
    3612                 :      * Do not allow moving temp tables of other backends ... their local
    3613                 :      * buffer manager is not going to cope.
    3614                 :      */
    3615 GBC         204 :     if (RELATION_IS_OTHER_TEMP(rel))
    3616 UIC           0 :         ereport(ERROR,
    3617                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3618 ECB             :                  errmsg("cannot move temporary tables of other sessions")));
    3619                 : 
    3620 GIC         204 :     return true;
    3621 EUB             : }
    3622                 : 
    3623                 : /*
    3624                 :  * SetRelationTableSpace
    3625                 :  *      Set new reltablespace and relfilenumber in pg_class entry.
    3626                 :  *
    3627 ECB             :  * newTableSpaceId is the new tablespace for the relation, and
    3628                 :  * newRelFilenumber its new filenumber.  If newRelFilenumber is
    3629                 :  * InvalidRelFileNumber, this field is not updated.
    3630                 :  *
    3631                 :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3632                 :  *
    3633                 :  * The caller of this routine had better check if a relation can be
    3634                 :  * moved to this new tablespace by calling CheckRelationTableSpaceMove()
    3635                 :  * first, and is responsible for making the change visible with
    3636                 :  * CommandCounterIncrement().
    3637                 :  */
    3638                 : void
    3639 GIC         102 : SetRelationTableSpace(Relation rel,
    3640                 :                       Oid newTableSpaceId,
    3641                 :                       RelFileNumber newRelFilenumber)
    3642                 : {
    3643 ECB             :     Relation    pg_class;
    3644                 :     HeapTuple   tuple;
    3645                 :     Form_pg_class rd_rel;
    3646 GIC         102 :     Oid         reloid = RelationGetRelid(rel);
    3647                 : 
    3648             102 :     Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
    3649 ECB             : 
    3650                 :     /* Get a modifiable copy of the relation's pg_class row. */
    3651 GIC         102 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
    3652                 : 
    3653             102 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
    3654 CBC         102 :     if (!HeapTupleIsValid(tuple))
    3655 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", reloid);
    3656 GIC         102 :     rd_rel = (Form_pg_class) GETSTRUCT(tuple);
    3657 ECB             : 
    3658                 :     /* Update the pg_class row. */
    3659 CBC         204 :     rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
    3660 GIC         102 :         InvalidOid : newTableSpaceId;
    3661 GNC         102 :     if (RelFileNumberIsValid(newRelFilenumber))
    3662              80 :         rd_rel->relfilenode = newRelFilenumber;
    3663 CBC         102 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
    3664 ECB             : 
    3665                 :     /*
    3666                 :      * Record dependency on tablespace.  This is only required for relations
    3667                 :      * that have no physical storage.
    3668                 :      */
    3669 CBC         102 :     if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
    3670 GIC          15 :         changeDependencyOnTablespace(RelationRelationId, reloid,
    3671                 :                                      rd_rel->reltablespace);
    3672                 : 
    3673 CBC         102 :     heap_freetuple(tuple);
    3674 GIC         102 :     table_close(pg_class, RowExclusiveLock);
    3675 CBC         102 : }
    3676 ECB             : 
    3677                 : /*
    3678                 :  *      renameatt_check         - basic sanity checks before attribute rename
    3679                 :  */
    3680                 : static void
    3681 CBC         479 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
    3682 ECB             : {
    3683 GIC         479 :     char        relkind = classform->relkind;
    3684 ECB             : 
    3685 GIC         479 :     if (classform->reloftype && !recursing)
    3686               3 :         ereport(ERROR,
    3687                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3688                 :                  errmsg("cannot rename column of typed table")));
    3689                 : 
    3690 ECB             :     /*
    3691                 :      * Renaming the columns of sequences or toast tables doesn't actually
    3692                 :      * break anything from the system's point of view, since internal
    3693                 :      * references are by attnum.  But it doesn't seem right to allow users to
    3694                 :      * change names that are hardcoded into the system, hence the following
    3695                 :      * restriction.
    3696                 :      */
    3697 GIC         476 :     if (relkind != RELKIND_RELATION &&
    3698 CBC          42 :         relkind != RELKIND_VIEW &&
    3699              42 :         relkind != RELKIND_MATVIEW &&
    3700              18 :         relkind != RELKIND_COMPOSITE_TYPE &&
    3701 GIC          18 :         relkind != RELKIND_INDEX &&
    3702              18 :         relkind != RELKIND_PARTITIONED_INDEX &&
    3703 UIC           0 :         relkind != RELKIND_FOREIGN_TABLE &&
    3704 ECB             :         relkind != RELKIND_PARTITIONED_TABLE)
    3705 LBC           0 :         ereport(ERROR,
    3706                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3707 ECB             :                  errmsg("cannot rename columns of relation \"%s\"",
    3708                 :                         NameStr(classform->relname)),
    3709                 :                  errdetail_relkind_not_supported(relkind)));
    3710                 : 
    3711                 :     /*
    3712                 :      * permissions checking.  only the owner of a class can change its schema.
    3713                 :      */
    3714 GNC         476 :     if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
    3715 UIC           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
    3716               0 :                        NameStr(classform->relname));
    3717 CBC         476 :     if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
    3718 GIC           1 :         ereport(ERROR,
    3719                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    3720                 :                  errmsg("permission denied: \"%s\" is a system catalog",
    3721                 :                         NameStr(classform->relname))));
    3722             475 : }
    3723                 : 
    3724                 : /*
    3725                 :  *      renameatt_internal      - workhorse for renameatt
    3726                 :  *
    3727 ECB             :  * Return value is the attribute number in the 'myrelid' relation.
    3728                 :  */
    3729                 : static AttrNumber
    3730 CBC         267 : renameatt_internal(Oid myrelid,
    3731                 :                    const char *oldattname,
    3732                 :                    const char *newattname,
    3733                 :                    bool recurse,
    3734                 :                    bool recursing,
    3735                 :                    int expected_parents,
    3736                 :                    DropBehavior behavior)
    3737                 : {
    3738                 :     Relation    targetrelation;
    3739                 :     Relation    attrelation;
    3740                 :     HeapTuple   atttup;
    3741 ECB             :     Form_pg_attribute attform;
    3742                 :     AttrNumber  attnum;
    3743                 : 
    3744                 :     /*
    3745                 :      * Grab an exclusive lock on the target table, which we will NOT release
    3746                 :      * until end of transaction.
    3747                 :      */
    3748 CBC         267 :     targetrelation = relation_open(myrelid, AccessExclusiveLock);
    3749 GIC         267 :     renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
    3750 ECB             : 
    3751                 :     /*
    3752                 :      * if the 'recurse' flag is set then we are supposed to rename this
    3753                 :      * attribute in all classes that inherit from 'relname' (as well as in
    3754                 :      * 'relname').
    3755                 :      *
    3756                 :      * any permissions or problems with duplicate attributes will cause the
    3757                 :      * whole transaction to abort, which is what we want -- all or nothing.
    3758                 :      */
    3759 GIC         267 :     if (recurse)
    3760                 :     {
    3761 ECB             :         List       *child_oids,
    3762                 :                    *child_numparents;
    3763                 :         ListCell   *lo,
    3764                 :                    *li;
    3765                 : 
    3766                 :         /*
    3767                 :          * we need the number of parents for each child so that the recursive
    3768                 :          * calls to renameatt() can determine whether there are any parents
    3769                 :          * outside the inheritance hierarchy being processed.
    3770                 :          */
    3771 GIC         115 :         child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    3772                 :                                          &child_numparents);
    3773                 : 
    3774 ECB             :         /*
    3775                 :          * find_all_inheritors does the recursive search of the inheritance
    3776                 :          * hierarchy, so all we have to do is process all of the relids in the
    3777                 :          * list that it returns.
    3778                 :          */
    3779 CBC         349 :         forboth(lo, child_oids, li, child_numparents)
    3780                 :         {
    3781             249 :             Oid         childrelid = lfirst_oid(lo);
    3782 GIC         249 :             int         numparents = lfirst_int(li);
    3783                 : 
    3784             249 :             if (childrelid == myrelid)
    3785             115 :                 continue;
    3786                 :             /* note we need not recurse again */
    3787             134 :             renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
    3788                 :         }
    3789 ECB             :     }
    3790                 :     else
    3791                 :     {
    3792                 :         /*
    3793                 :          * If we are told not to recurse, there had better not be any child
    3794                 :          * tables; else the rename would put them out of step.
    3795                 :          *
    3796                 :          * expected_parents will only be 0 if we are not already recursing.
    3797                 :          */
    3798 GIC         170 :         if (expected_parents == 0 &&
    3799              18 :             find_inheritance_children(myrelid, NoLock) != NIL)
    3800               6 :             ereport(ERROR,
    3801                 :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3802                 :                      errmsg("inherited column \"%s\" must be renamed in child tables too",
    3803                 :                             oldattname)));
    3804                 :     }
    3805                 : 
    3806 ECB             :     /* rename attributes in typed tables of composite type */
    3807 GIC         246 :     if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    3808                 :     {
    3809 ECB             :         List       *child_oids;
    3810                 :         ListCell   *lo;
    3811                 : 
    3812 GIC          12 :         child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
    3813 CBC          12 :                                                    RelationGetRelationName(targetrelation),
    3814                 :                                                    behavior);
    3815 ECB             : 
    3816 CBC          12 :         foreach(lo, child_oids)
    3817 GIC           3 :             renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
    3818 ECB             :     }
    3819                 : 
    3820 CBC         243 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    3821                 : 
    3822 GIC         243 :     atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
    3823             243 :     if (!HeapTupleIsValid(atttup))
    3824              12 :         ereport(ERROR,
    3825                 :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    3826                 :                  errmsg("column \"%s\" does not exist",
    3827                 :                         oldattname)));
    3828             231 :     attform = (Form_pg_attribute) GETSTRUCT(atttup);
    3829                 : 
    3830             231 :     attnum = attform->attnum;
    3831             231 :     if (attnum <= 0)
    3832 UIC           0 :         ereport(ERROR,
    3833                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3834                 :                  errmsg("cannot rename system column \"%s\"",
    3835                 :                         oldattname)));
    3836                 : 
    3837                 :     /*
    3838 ECB             :      * if the attribute is inherited, forbid the renaming.  if this is a
    3839                 :      * top-level call to renameatt(), then expected_parents will be 0, so the
    3840                 :      * effect of this code will be to prohibit the renaming if the attribute
    3841                 :      * is inherited at all.  if this is a recursive call to renameatt(),
    3842                 :      * expected_parents will be the number of parents the current relation has
    3843                 :      * within the inheritance hierarchy being processed, so we'll prohibit the
    3844                 :      * renaming only if there are additional parents from elsewhere.
    3845                 :      */
    3846 GIC         231 :     if (attform->attinhcount > expected_parents)
    3847 CBC          15 :         ereport(ERROR,
    3848 ECB             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3849                 :                  errmsg("cannot rename inherited column \"%s\"",
    3850 EUB             :                         oldattname)));
    3851 ECB             : 
    3852                 :     /* new name should not already exist */
    3853 CBC         216 :     (void) check_for_column_name_collision(targetrelation, newattname, false);
    3854                 : 
    3855 ECB             :     /* apply the update */
    3856 CBC         210 :     namestrcpy(&(attform->attname), newattname);
    3857                 : 
    3858 GIC         210 :     CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
    3859                 : 
    3860             210 :     InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
    3861 ECB             : 
    3862 GIC         210 :     heap_freetuple(atttup);
    3863                 : 
    3864 CBC         210 :     table_close(attrelation, RowExclusiveLock);
    3865 ECB             : 
    3866 CBC         210 :     relation_close(targetrelation, NoLock); /* close rel but keep lock */
    3867                 : 
    3868 GIC         210 :     return attnum;
    3869                 : }
    3870                 : 
    3871                 : /*
    3872                 :  * Perform permissions and integrity checks before acquiring a relation lock.
    3873                 :  */
    3874                 : static void
    3875             191 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
    3876                 :                                    void *arg)
    3877                 : {
    3878                 :     HeapTuple   tuple;
    3879 ECB             :     Form_pg_class form;
    3880                 : 
    3881 GIC         191 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
    3882             191 :     if (!HeapTupleIsValid(tuple))
    3883              18 :         return;                 /* concurrently dropped */
    3884             173 :     form = (Form_pg_class) GETSTRUCT(tuple);
    3885             173 :     renameatt_check(relid, form, false);
    3886             169 :     ReleaseSysCache(tuple);
    3887 ECB             : }
    3888                 : 
    3889                 : /*
    3890                 :  *      renameatt       - changes the name of an attribute in a relation
    3891                 :  *
    3892                 :  * The returned ObjectAddress is that of the renamed column.
    3893                 :  */
    3894                 : ObjectAddress
    3895 GIC         149 : renameatt(RenameStmt *stmt)
    3896 ECB             : {
    3897 EUB             :     Oid         relid;
    3898                 :     AttrNumber  attnum;
    3899                 :     ObjectAddress address;
    3900                 : 
    3901                 :     /* lock level taken here should match renameatt_internal */
    3902 GIC         149 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    3903 CBC         149 :                                      stmt->missing_ok ? RVR_MISSING_OK : 0,
    3904 ECB             :                                      RangeVarCallbackForRenameAttribute,
    3905                 :                                      NULL);
    3906                 : 
    3907 GIC         142 :     if (!OidIsValid(relid))
    3908                 :     {
    3909              12 :         ereport(NOTICE,
    3910                 :                 (errmsg("relation \"%s\" does not exist, skipping",
    3911                 :                         stmt->relation->relname)));
    3912 CBC          12 :         return InvalidObjectAddress;
    3913 EUB             :     }
    3914                 : 
    3915                 :     attnum =
    3916 GIC         130 :         renameatt_internal(relid,
    3917 CBC         130 :                            stmt->subname,    /* old att name */
    3918 GIC         130 :                            stmt->newname,    /* new att name */
    3919             130 :                            stmt->relation->inh, /* recursive? */
    3920                 :                            false,   /* recursing? */
    3921                 :                            0,   /* expected inhcount */
    3922                 :                            stmt->behavior);
    3923                 : 
    3924              88 :     ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
    3925                 : 
    3926              88 :     return address;
    3927                 : }
    3928                 : 
    3929                 : /*
    3930                 :  * same logic as renameatt_internal
    3931                 :  */
    3932                 : static ObjectAddress
    3933              42 : rename_constraint_internal(Oid myrelid,
    3934                 :                            Oid mytypid,
    3935                 :                            const char *oldconname,
    3936 ECB             :                            const char *newconname,
    3937                 :                            bool recurse,
    3938                 :                            bool recursing,
    3939                 :                            int expected_parents)
    3940                 : {
    3941 GIC          42 :     Relation    targetrelation = NULL;
    3942                 :     Oid         constraintOid;
    3943 ECB             :     HeapTuple   tuple;
    3944                 :     Form_pg_constraint con;
    3945                 :     ObjectAddress address;
    3946                 : 
    3947 GNC          42 :     Assert(!myrelid || !mytypid);
    3948 ECB             : 
    3949 GIC          42 :     if (mytypid)
    3950 ECB             :     {
    3951 CBC           3 :         constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
    3952 EUB             :     }
    3953 ECB             :     else
    3954                 :     {
    3955 GIC          39 :         targetrelation = relation_open(myrelid, AccessExclusiveLock);
    3956 ECB             : 
    3957                 :         /*
    3958                 :          * don't tell it whether we're recursing; we allow changing typed
    3959                 :          * tables here
    3960                 :          */
    3961 GIC          39 :         renameatt_check(myrelid, RelationGetForm(targetrelation), false);
    3962                 : 
    3963              39 :         constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
    3964                 :     }
    3965                 : 
    3966 CBC          42 :     tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
    3967              42 :     if (!HeapTupleIsValid(tuple))
    3968 UIC           0 :         elog(ERROR, "cache lookup failed for constraint %u",
    3969                 :              constraintOid);
    3970 CBC          42 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
    3971 ECB             : 
    3972 GNC          42 :     if (myrelid &&
    3973              39 :         (con->contype == CONSTRAINT_CHECK ||
    3974               9 :          con->contype == CONSTRAINT_NOTNULL) &&
    3975              30 :         !con->connoinherit)
    3976                 :     {
    3977 GIC          24 :         if (recurse)
    3978                 :         {
    3979                 :             List       *child_oids,
    3980                 :                        *child_numparents;
    3981 ECB             :             ListCell   *lo,
    3982                 :                        *li;
    3983                 : 
    3984 GIC          15 :             child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    3985 ECB             :                                              &child_numparents);
    3986                 : 
    3987 GIC          36 :             forboth(lo, child_oids, li, child_numparents)
    3988                 :             {
    3989              21 :                 Oid         childrelid = lfirst_oid(lo);
    3990              21 :                 int         numparents = lfirst_int(li);
    3991                 : 
    3992              21 :                 if (childrelid == myrelid)
    3993              15 :                     continue;
    3994                 : 
    3995               6 :                 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
    3996                 :             }
    3997 ECB             :         }
    3998                 :         else
    3999                 :         {
    4000 CBC          12 :             if (expected_parents == 0 &&
    4001               3 :                 find_inheritance_children(myrelid, NoLock) != NIL)
    4002               3 :                 ereport(ERROR,
    4003 EUB             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4004                 :                          errmsg("inherited constraint \"%s\" must be renamed in child tables too",
    4005                 :                                 oldconname)));
    4006                 :         }
    4007                 : 
    4008 GIC          21 :         if (con->coninhcount > expected_parents)
    4009               3 :             ereport(ERROR,
    4010                 :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4011                 :                      errmsg("cannot rename inherited constraint \"%s\"",
    4012                 :                             oldconname)));
    4013                 :     }
    4014 ECB             : 
    4015 GBC          36 :     if (con->conindid
    4016               9 :         && (con->contype == CONSTRAINT_PRIMARY
    4017 CBC           3 :             || con->contype == CONSTRAINT_UNIQUE
    4018 LBC           0 :             || con->contype == CONSTRAINT_EXCLUSION))
    4019                 :         /* rename the index; this renames the constraint as well */
    4020 GIC           9 :         RenameRelationInternal(con->conindid, newconname, false, true);
    4021                 :     else
    4022 CBC          27 :         RenameConstraintById(constraintOid, newconname);
    4023                 : 
    4024 GIC          36 :     ObjectAddressSet(address, ConstraintRelationId, constraintOid);
    4025                 : 
    4026              36 :     ReleaseSysCache(tuple);
    4027                 : 
    4028              36 :     if (targetrelation)
    4029                 :     {
    4030 ECB             :         /*
    4031                 :          * Invalidate relcache so as others can see the new constraint name.
    4032                 :          */
    4033 GIC          33 :         CacheInvalidateRelcache(targetrelation);
    4034                 : 
    4035              33 :         relation_close(targetrelation, NoLock); /* close rel but keep lock */
    4036                 :     }
    4037                 : 
    4038              36 :     return address;
    4039                 : }
    4040                 : 
    4041                 : ObjectAddress
    4042              39 : RenameConstraint(RenameStmt *stmt)
    4043                 : {
    4044              39 :     Oid         relid = InvalidOid;
    4045              39 :     Oid         typid = InvalidOid;
    4046                 : 
    4047              39 :     if (stmt->renameType == OBJECT_DOMCONSTRAINT)
    4048 ECB             :     {
    4049                 :         Relation    rel;
    4050                 :         HeapTuple   tup;
    4051                 : 
    4052 GIC           3 :         typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
    4053               3 :         rel = table_open(TypeRelationId, RowExclusiveLock);
    4054               3 :         tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
    4055               3 :         if (!HeapTupleIsValid(tup))
    4056 UIC           0 :             elog(ERROR, "cache lookup failed for type %u", typid);
    4057 GIC           3 :         checkDomainOwner(tup);
    4058               3 :         ReleaseSysCache(tup);
    4059 CBC           3 :         table_close(rel, NoLock);
    4060                 :     }
    4061                 :     else
    4062                 :     {
    4063                 :         /* lock level taken here should match rename_constraint_internal */
    4064 GIC          36 :         relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    4065              36 :                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4066                 :                                          RangeVarCallbackForRenameAttribute,
    4067                 :                                          NULL);
    4068              36 :         if (!OidIsValid(relid))
    4069                 :         {
    4070               3 :             ereport(NOTICE,
    4071 ECB             :                     (errmsg("relation \"%s\" does not exist, skipping",
    4072                 :                             stmt->relation->relname)));
    4073 GIC           3 :             return InvalidObjectAddress;
    4074                 :         }
    4075                 :     }
    4076                 : 
    4077                 :     return
    4078              36 :         rename_constraint_internal(relid, typid,
    4079 CBC          36 :                                    stmt->subname,
    4080 GIC          36 :                                    stmt->newname,
    4081 CBC          69 :                                    (stmt->relation &&
    4082              33 :                                     stmt->relation->inh), /* recursive? */
    4083                 :                                    false,   /* recursing? */
    4084 ECB             :                                    0 /* expected inhcount */ );
    4085                 : }
    4086                 : 
    4087                 : /*
    4088                 :  * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
    4089                 :  * RENAME
    4090                 :  */
    4091                 : ObjectAddress
    4092 GIC         255 : RenameRelation(RenameStmt *stmt)
    4093                 : {
    4094             255 :     bool        is_index_stmt = stmt->renameType == OBJECT_INDEX;
    4095                 :     Oid         relid;
    4096                 :     ObjectAddress address;
    4097                 : 
    4098 ECB             :     /*
    4099                 :      * Grab an exclusive lock on the target table, index, sequence, view,
    4100                 :      * materialized view, or foreign table, which we will NOT release until
    4101                 :      * end of transaction.
    4102                 :      *
    4103                 :      * Lock level used here should match RenameRelationInternal, to avoid lock
    4104                 :      * escalation.  However, because ALTER INDEX can be used with any relation
    4105                 :      * type, we mustn't believe without verification.
    4106                 :      */
    4107                 :     for (;;)
    4108 GIC           6 :     {
    4109                 :         LOCKMODE    lockmode;
    4110                 :         char        relkind;
    4111                 :         bool        obj_is_index;
    4112 ECB             : 
    4113 CBC         261 :         lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
    4114                 : 
    4115 GIC         261 :         relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
    4116 CBC         261 :                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4117 ECB             :                                          RangeVarCallbackForAlterRelation,
    4118                 :                                          (void *) stmt);
    4119                 : 
    4120 CBC         236 :         if (!OidIsValid(relid))
    4121                 :         {
    4122               9 :             ereport(NOTICE,
    4123 ECB             :                     (errmsg("relation \"%s\" does not exist, skipping",
    4124                 :                             stmt->relation->relname)));
    4125 GIC           9 :             return InvalidObjectAddress;
    4126                 :         }
    4127                 : 
    4128 ECB             :         /*
    4129                 :          * We allow mismatched statement and object types (e.g., ALTER INDEX
    4130                 :          * to rename a table), but we might've used the wrong lock level.  If
    4131                 :          * that happens, retry with the correct lock level.  We don't bother
    4132 EUB             :          * if we already acquired AccessExclusiveLock with an index, however.
    4133                 :          */
    4134 GIC         227 :         relkind = get_rel_relkind(relid);
    4135             227 :         obj_is_index = (relkind == RELKIND_INDEX ||
    4136                 :                         relkind == RELKIND_PARTITIONED_INDEX);
    4137             227 :         if (obj_is_index || is_index_stmt == obj_is_index)
    4138                 :             break;
    4139                 : 
    4140               6 :         UnlockRelationOid(relid, lockmode);
    4141               6 :         is_index_stmt = obj_is_index;
    4142                 :     }
    4143                 : 
    4144                 :     /* Do the work */
    4145             221 :     RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
    4146 ECB             : 
    4147 CBC         215 :     ObjectAddressSet(address, RelationRelationId, relid);
    4148                 : 
    4149 GIC         215 :     return address;
    4150                 : }
    4151                 : 
    4152                 : /*
    4153 ECB             :  *      RenameRelationInternal - change the name of a relation
    4154                 :  */
    4155                 : void
    4156 CBC         575 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
    4157                 : {
    4158 ECB             :     Relation    targetrelation;
    4159                 :     Relation    relrelation;    /* for RELATION relation */
    4160                 :     HeapTuple   reltup;
    4161                 :     Form_pg_class relform;
    4162                 :     Oid         namespaceId;
    4163                 : 
    4164                 :     /*
    4165                 :      * Grab a lock on the target relation, which we will NOT release until end
    4166                 :      * of transaction.  We need at least a self-exclusive lock so that
    4167                 :      * concurrent DDL doesn't overwrite the rename if they start updating
    4168                 :      * while still seeing the old version.  The lock also guards against
    4169                 :      * triggering relcache reloads in concurrent sessions, which might not
    4170                 :      * handle this information changing under them.  For indexes, we can use a
    4171                 :      * reduced lock level because RelationReloadIndexInfo() handles indexes
    4172                 :      * specially.
    4173                 :      */
    4174 GIC         575 :     targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
    4175 CBC         575 :     namespaceId = RelationGetNamespace(targetrelation);
    4176                 : 
    4177                 :     /*
    4178                 :      * Find relation's pg_class tuple, and make sure newrelname isn't in use.
    4179                 :      */
    4180 GIC         575 :     relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4181 ECB             : 
    4182 CBC         575 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4183             575 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4184 LBC           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4185 CBC         575 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    4186 ECB             : 
    4187 GIC         575 :     if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
    4188               6 :         ereport(ERROR,
    4189                 :                 (errcode(ERRCODE_DUPLICATE_TABLE),
    4190                 :                  errmsg("relation \"%s\" already exists",
    4191                 :                         newrelname)));
    4192                 : 
    4193                 :     /*
    4194                 :      * RenameRelation is careful not to believe the caller's idea of the
    4195 ECB             :      * relation kind being handled.  We don't have to worry about this, but
    4196                 :      * let's not be totally oblivious to it.  We can process an index as
    4197                 :      * not-an-index, but not the other way around.
    4198                 :      */
    4199 GIC         569 :     Assert(!is_index ||
    4200                 :            is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4201                 :                         targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
    4202 ECB             : 
    4203                 :     /*
    4204                 :      * Update pg_class tuple with new relname.  (Scribbling on reltup is OK
    4205                 :      * because it's a copy...)
    4206                 :      */
    4207 CBC         569 :     namestrcpy(&(relform->relname), newrelname);
    4208                 : 
    4209             569 :     CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
    4210                 : 
    4211 GIC         569 :     InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
    4212 ECB             :                                  InvalidOid, is_internal);
    4213                 : 
    4214 GIC         569 :     heap_freetuple(reltup);
    4215             569 :     table_close(relrelation, RowExclusiveLock);
    4216 ECB             : 
    4217                 :     /*
    4218                 :      * Also rename the associated type, if any.
    4219                 :      */
    4220 GIC         569 :     if (OidIsValid(targetrelation->rd_rel->reltype))
    4221              62 :         RenameTypeInternal(targetrelation->rd_rel->reltype,
    4222                 :                            newrelname, namespaceId);
    4223                 : 
    4224 ECB             :     /*
    4225                 :      * Also rename the associated constraint, if any.
    4226                 :      */
    4227 GIC         569 :     if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4228             308 :         targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    4229                 :     {
    4230             270 :         Oid         constraintId = get_index_constraint(myrelid);
    4231                 : 
    4232             270 :         if (OidIsValid(constraintId))
    4233 CBC          18 :             RenameConstraintById(constraintId, newrelname);
    4234                 :     }
    4235                 : 
    4236                 :     /*
    4237                 :      * Close rel, but keep lock!
    4238                 :      */
    4239 GIC         569 :     relation_close(targetrelation, NoLock);
    4240             569 : }
    4241 ECB             : 
    4242                 : /*
    4243                 :  *      ResetRelRewrite - reset relrewrite
    4244                 :  */
    4245                 : void
    4246 GIC         170 : ResetRelRewrite(Oid myrelid)
    4247 ECB             : {
    4248                 :     Relation    relrelation;    /* for RELATION relation */
    4249                 :     HeapTuple   reltup;
    4250                 :     Form_pg_class relform;
    4251                 : 
    4252                 :     /*
    4253                 :      * Find relation's pg_class tuple.
    4254                 :      */
    4255 CBC         170 :     relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4256                 : 
    4257 GIC         170 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4258             170 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4259 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4260 GIC         170 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    4261 ECB             : 
    4262                 :     /*
    4263                 :      * Update pg_class tuple.
    4264                 :      */
    4265 GIC         170 :     relform->relrewrite = InvalidOid;
    4266 ECB             : 
    4267 CBC         170 :     CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
    4268 EUB             : 
    4269 GIC         170 :     heap_freetuple(reltup);
    4270 CBC         170 :     table_close(relrelation, RowExclusiveLock);
    4271 GIC         170 : }
    4272 ECB             : 
    4273                 : /*
    4274                 :  * Disallow ALTER TABLE (and similar commands) when the current backend has
    4275                 :  * any open reference to the target table besides the one just acquired by
    4276                 :  * the calling command; this implies there's an open cursor or active plan.
    4277                 :  * We need this check because our lock doesn't protect us against stomping
    4278                 :  * on our own foot, only other people's feet!
    4279                 :  *
    4280                 :  * For ALTER TABLE, the only case known to cause serious trouble is ALTER
    4281                 :  * COLUMN TYPE, and some changes are obviously pretty benign, so this could
    4282                 :  * possibly be relaxed to only error out for certain types of alterations.
    4283                 :  * But the use-case for allowing any of these things is not obvious, so we
    4284                 :  * won't work hard at it for now.
    4285                 :  *
    4286                 :  * We also reject these commands if there are any pending AFTER trigger events
    4287                 :  * for the rel.  This is certainly necessary for the rewriting variants of
    4288                 :  * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
    4289                 :  * events would try to fetch the wrong tuples.  It might be overly cautious
    4290                 :  * in other cases, but again it seems better to err on the side of paranoia.
    4291                 :  *
    4292                 :  * REINDEX calls this with "rel" referencing the index to be rebuilt; here
    4293                 :  * we are worried about active indexscans on the index.  The trigger-event
    4294                 :  * check can be skipped, since we are doing no damage to the parent table.
    4295                 :  *
    4296                 :  * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
    4297                 :  */
    4298                 : void
    4299 GIC       90091 : CheckTableNotInUse(Relation rel, const char *stmt)
    4300 ECB             : {
    4301                 :     int         expected_refcnt;
    4302                 : 
    4303 GIC       90091 :     expected_refcnt = rel->rd_isnailed ? 2 : 1;
    4304           90091 :     if (rel->rd_refcnt != expected_refcnt)
    4305              12 :         ereport(ERROR,
    4306                 :                 (errcode(ERRCODE_OBJECT_IN_USE),
    4307                 :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    4308 ECB             :                  errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
    4309                 :                         stmt, RelationGetRelationName(rel))));
    4310                 : 
    4311 GIC       90079 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    4312          154916 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    4313           77014 :         AfterTriggerPendingOnRel(RelationGetRelid(rel)))
    4314               9 :         ereport(ERROR,
    4315 ECB             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    4316                 :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    4317                 :                  errmsg("cannot %s \"%s\" because it has pending trigger events",
    4318 EUB             :                         stmt, RelationGetRelationName(rel))));
    4319 GIC       90070 : }
    4320 ECB             : 
    4321                 : /*
    4322                 :  * AlterTableLookupRelation
    4323                 :  *      Look up, and lock, the OID for the relation named by an alter table
    4324                 :  *      statement.
    4325                 :  */
    4326                 : Oid
    4327 GIC       44953 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
    4328 ECB             : {
    4329 GIC       89864 :     return RangeVarGetRelidExtended(stmt->relation, lockmode,
    4330           44953 :                                     stmt->missing_ok ? RVR_MISSING_OK : 0,
    4331                 :                                     RangeVarCallbackForAlterRelation,
    4332                 :                                     (void *) stmt);
    4333 ECB             : }
    4334                 : 
    4335                 : /*
    4336                 :  * AlterTable
    4337                 :  *      Execute ALTER TABLE, which can be a list of subcommands
    4338                 :  *
    4339                 :  * ALTER TABLE is performed in three phases:
    4340                 :  *      1. Examine subcommands and perform pre-transformation checking.
    4341                 :  *      2. Validate and transform subcommands, and update system catalogs.
    4342                 :  *      3. Scan table(s) to check new constraints, and optionally recopy
    4343                 :  *         the data into new table(s).
    4344                 :  * Phase 3 is not performed unless one or more of the subcommands requires
    4345                 :  * it.  The intention of this design is to allow multiple independent
    4346                 :  * updates of the table schema to be performed with only one pass over the
    4347                 :  * data.
    4348                 :  *
    4349                 :  * ATPrepCmd performs phase 1.  A "work queue" entry is created for
    4350                 :  * each table to be affected (there may be multiple affected tables if the
    4351                 :  * commands traverse a table inheritance hierarchy).  Also we do preliminary
    4352                 :  * validation of the subcommands.  Because earlier subcommands may change
    4353                 :  * the catalog state seen by later commands, there are limits to what can
    4354                 :  * be done in this phase.  Generally, this phase acquires table locks,
    4355                 :  * checks permissions and relkind, and recurses to find child tables.
    4356 EUB             :  *
    4357 ECB             :  * ATRewriteCatalogs performs phase 2 for each affected table.
    4358                 :  * Certain subcommands need to be performed before others to avoid
    4359                 :  * unnecessary conflicts; for example, DROP COLUMN should come before
    4360                 :  * ADD COLUMN.  Therefore phase 1 divides the subcommands into multiple
    4361                 :  * lists, one for each logical "pass" of phase 2.
    4362                 :  *
    4363                 :  * ATRewriteTables performs phase 3 for those tables that need it.
    4364                 :  *
    4365                 :  * For most subcommand types, phases 2 and 3 do no explicit recursion,
    4366                 :  * since phase 1 already does it.  However, for certain subcommand types
    4367                 :  * it is only possible to determine how to recurse at phase 2 time; for
    4368                 :  * those cases, phase 1 sets the cmd->recurse flag.
    4369                 :  *
    4370                 :  * Thanks to the magic of MVCC, an error anywhere along the way rolls back
    4371                 :  * the whole operation; we don't have to do anything special to clean up.
    4372                 :  *
    4373                 :  * The caller must lock the relation, with an appropriate lock level
    4374                 :  * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
    4375                 :  * or higher. We pass the lock level down
    4376                 :  * so that we can apply it recursively to inherited tables. Note that the
    4377                 :  * lock level we want as we recurse might well be higher than required for
    4378                 :  * that specific subcommand. So we pass down the overall lock requirement,
    4379                 :  * rather than reassess it at lower levels.
    4380                 :  *
    4381                 :  * The caller also provides a "context" which is to be passed back to
    4382                 :  * utility.c when we need to execute a subcommand such as CREATE INDEX.
    4383                 :  * Some of the fields therein, such as the relid, are used here as well.
    4384                 :  */
    4385                 : void
    4386 GIC       44842 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
    4387                 :            AlterTableUtilityContext *context)
    4388                 : {
    4389                 :     Relation    rel;
    4390                 : 
    4391 ECB             :     /* Caller is required to provide an adequate lock. */
    4392 GIC       44842 :     rel = relation_open(context->relid, NoLock);
    4393 ECB             : 
    4394 GIC       44842 :     CheckTableNotInUse(rel, "ALTER TABLE");
    4395                 : 
    4396           44833 :     ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
    4397           43514 : }
    4398                 : 
    4399                 : /*
    4400                 :  * AlterTableInternal
    4401                 :  *
    4402                 :  * ALTER TABLE with target specified by OID
    4403                 :  *
    4404                 :  * We do not reject if the relation is already open, because it's quite
    4405                 :  * likely that one or more layers of caller have it open.  That means it
    4406                 :  * is unsafe to use this entry point for alterations that could break
    4407 ECB             :  * existing query plans.  On the assumption it's not used for such, we
    4408                 :  * don't have to reject pending AFTER triggers, either.
    4409                 :  *
    4410                 :  * Also, since we don't have an AlterTableUtilityContext, this cannot be
    4411                 :  * used for any subcommand types that require parse transformation or
    4412                 :  * could generate subcommands that have to be passed to ProcessUtility.
    4413                 :  */
    4414                 : void
    4415 CBC         139 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
    4416                 : {
    4417                 :     Relation    rel;
    4418 GIC         139 :     LOCKMODE    lockmode = AlterTableGetLockLevel(cmds);
    4419 ECB             : 
    4420 GIC         139 :     rel = relation_open(relid, lockmode);
    4421 ECB             : 
    4422 GIC         139 :     EventTriggerAlterTableRelid(relid);
    4423                 : 
    4424 CBC         139 :     ATController(NULL, rel, cmds, recurse, lockmode, NULL);
    4425 GIC         139 : }
    4426                 : 
    4427                 : /*
    4428                 :  * AlterTableGetLockLevel
    4429                 :  *
    4430                 :  * Sets the overall lock level required for the supplied list of subcommands.
    4431                 :  * Policy for doing this set according to needs of AlterTable(), see
    4432                 :  * comments there for overall explanation.
    4433 ECB             :  *
    4434                 :  * Function is called before and after parsing, so it must give same
    4435                 :  * answer each time it is called. Some subcommands are transformed
    4436                 :  * into other subcommand types, so the transform must never be made to a
    4437                 :  * lower lock level than previously assigned. All transforms are noted below.
    4438                 :  *
    4439                 :  * Since this is called before we lock the table we cannot use table metadata
    4440                 :  * to influence the type of lock we acquire.
    4441                 :  *
    4442                 :  * There should be no lockmodes hardcoded into the subcommand functions. All
    4443                 :  * lockmode decisions for ALTER TABLE are made here only. The one exception is
    4444                 :  * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
    4445                 :  * and does not travel through this section of code and cannot be combined with
    4446                 :  * any of the subcommands given here.
    4447                 :  *
    4448                 :  * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
    4449                 :  * so any changes that might affect SELECTs running on standbys need to use
    4450                 :  * AccessExclusiveLocks even if you think a lesser lock would do, unless you
    4451                 :  * have a solution for that also.
    4452                 :  *
    4453                 :  * Also note that pg_dump uses only an AccessShareLock, meaning that anything
    4454                 :  * that takes a lock less than AccessExclusiveLock can change object definitions
    4455                 :  * while pg_dump is running. Be careful to check that the appropriate data is
    4456                 :  * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
    4457                 :  * otherwise we might end up with an inconsistent dump that can't restore.
    4458                 :  */
    4459                 : LOCKMODE
    4460 GIC       45092 : AlterTableGetLockLevel(List *cmds)
    4461                 : {
    4462                 :     /*
    4463                 :      * This only works if we read catalog tables using MVCC snapshots.
    4464                 :      */
    4465                 :     ListCell   *lcmd;
    4466           45092 :     LOCKMODE    lockmode = ShareUpdateExclusiveLock;
    4467                 : 
    4468           90928 :     foreach(lcmd, cmds)
    4469                 :     {
    4470           45836 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4471           45836 :         LOCKMODE    cmd_lockmode = AccessExclusiveLock; /* default for compiler */
    4472                 : 
    4473 CBC       45836 :         switch (cmd->subtype)
    4474 ECB             :         {
    4475                 :                 /*
    4476                 :                  * These subcommands rewrite the heap, so require full locks.
    4477                 :                  */
    4478 GIC        1490 :             case AT_AddColumn:  /* may rewrite heap, in some cases and visible
    4479 ECB             :                                  * to SELECT */
    4480                 :             case AT_SetAccessMethod:    /* must rewrite heap */
    4481                 :             case AT_SetTableSpace:  /* must rewrite heap */
    4482                 :             case AT_AlterColumnType:    /* must rewrite heap */
    4483 GBC        1490 :                 cmd_lockmode = AccessExclusiveLock;
    4484 CBC        1490 :                 break;
    4485                 : 
    4486 ECB             :                 /*
    4487                 :                  * These subcommands may require addition of toast tables. If
    4488                 :                  * we add a toast table to a table currently being scanned, we
    4489                 :                  * might miss data added to the new toast table by concurrent
    4490                 :                  * insert transactions.
    4491                 :                  */
    4492 GIC         104 :             case AT_SetStorage: /* may add toast tables, see
    4493                 :                                  * ATRewriteCatalogs() */
    4494             104 :                 cmd_lockmode = AccessExclusiveLock;
    4495             104 :                 break;
    4496                 : 
    4497                 :                 /*
    4498 ECB             :                  * Removing constraints can affect SELECTs that have been
    4499                 :                  * optimized assuming the constraint holds true. See also
    4500                 :                  * CloneFkReferenced.
    4501                 :                  */
    4502 GIC         393 :             case AT_DropConstraint: /* as DROP INDEX */
    4503                 :             case AT_DropNotNull:    /* may change some SQL plans */
    4504             393 :                 cmd_lockmode = AccessExclusiveLock;
    4505             393 :                 break;
    4506 ECB             : 
    4507                 :                 /*
    4508                 :                  * Subcommands that may be visible to concurrent SELECTs
    4509                 :                  */
    4510 CBC         814 :             case AT_DropColumn: /* change visible to SELECT */
    4511                 :             case AT_AddColumnToView:    /* CREATE VIEW */
    4512                 :             case AT_DropOids:   /* used to equiv to DropColumn */
    4513 ECB             :             case AT_EnableAlwaysRule:   /* may change SELECT rules */
    4514                 :             case AT_EnableReplicaRule:  /* may change SELECT rules */
    4515                 :             case AT_EnableRule: /* may change SELECT rules */
    4516                 :             case AT_DisableRule:    /* may change SELECT rules */
    4517 GIC         814 :                 cmd_lockmode = AccessExclusiveLock;
    4518             814 :                 break;
    4519 ECB             : 
    4520                 :                 /*
    4521                 :                  * Changing owner may remove implicit SELECT privileges
    4522                 :                  */
    4523 GIC         861 :             case AT_ChangeOwner:    /* change visible to SELECT */
    4524             861 :                 cmd_lockmode = AccessExclusiveLock;
    4525             861 :                 break;
    4526 ECB             : 
    4527                 :                 /*
    4528                 :                  * Changing foreign table options may affect optimization.
    4529                 :                  */
    4530 GIC         117 :             case AT_GenericOptions:
    4531 ECB             :             case AT_AlterColumnGenericOptions:
    4532 CBC         117 :                 cmd_lockmode = AccessExclusiveLock;
    4533 GIC         117 :                 break;
    4534                 : 
    4535                 :                 /*
    4536                 :                  * These subcommands affect write operations only.
    4537                 :                  */
    4538 CBC         168 :             case AT_EnableTrig:
    4539 ECB             :             case AT_EnableAlwaysTrig:
    4540                 :             case AT_EnableReplicaTrig:
    4541                 :             case AT_EnableTrigAll:
    4542                 :             case AT_EnableTrigUser:
    4543                 :             case AT_DisableTrig:
    4544                 :             case AT_DisableTrigAll:
    4545                 :             case AT_DisableTrigUser:
    4546 GIC         168 :                 cmd_lockmode = ShareRowExclusiveLock;
    4547             168 :                 break;
    4548                 : 
    4549                 :                 /*
    4550                 :                  * These subcommands affect write operations only. XXX
    4551                 :                  * Theoretically, these could be ShareRowExclusiveLock.
    4552                 :                  */
    4553            4135 :             case AT_ColumnDefault:
    4554 ECB             :             case AT_CookedColumnDefault:
    4555                 :             case AT_AlterConstraint:
    4556                 :             case AT_AddIndex:   /* from ADD CONSTRAINT */
    4557                 :             case AT_AddIndexConstraint:
    4558 EUB             :             case AT_ReplicaIdentity:
    4559 ECB             :             case AT_SetNotNull:
    4560                 :             case AT_SetAttNotNull:
    4561                 :             case AT_EnableRowSecurity:
    4562                 :             case AT_DisableRowSecurity:
    4563                 :             case AT_ForceRowSecurity:
    4564                 :             case AT_NoForceRowSecurity:
    4565                 :             case AT_AddIdentity:
    4566                 :             case AT_DropIdentity:
    4567                 :             case AT_SetIdentity:
    4568                 :             case AT_DropExpression:
    4569                 :             case AT_SetCompression:
    4570 CBC        4135 :                 cmd_lockmode = AccessExclusiveLock;
    4571            4135 :                 break;
    4572                 : 
    4573 GIC       35237 :             case AT_AddConstraint:
    4574                 :             case AT_ReAddConstraint:    /* becomes AT_AddConstraint */
    4575                 :             case AT_ReAddDomainConstraint:  /* becomes AT_AddConstraint */
    4576           35237 :                 if (IsA(cmd->def, Constraint))
    4577                 :                 {
    4578           35237 :                     Constraint *con = (Constraint *) cmd->def;
    4579                 : 
    4580           35237 :                     switch (con->contype)
    4581                 :                     {
    4582           33818 :                         case CONSTR_EXCLUSION:
    4583                 :                         case CONSTR_PRIMARY:
    4584                 :                         case CONSTR_UNIQUE:
    4585                 : 
    4586                 :                             /*
    4587                 :                              * Cases essentially the same as CREATE INDEX. We
    4588                 :                              * could reduce the lock strength to ShareLock if
    4589                 :                              * we can work out how to allow concurrent catalog
    4590                 :                              * updates. XXX Might be set down to
    4591                 :                              * ShareRowExclusiveLock but requires further
    4592                 :                              * analysis.
    4593                 :                              */
    4594           33818 :                             cmd_lockmode = AccessExclusiveLock;
    4595           33818 :                             break;
    4596            1000 :                         case CONSTR_FOREIGN:
    4597                 : 
    4598 ECB             :                             /*
    4599                 :                              * We add triggers to both tables when we add a
    4600                 :                              * Foreign Key, so the lock level must be at least
    4601                 :                              * as strong as CREATE TRIGGER.
    4602                 :                              */
    4603 CBC        1000 :                             cmd_lockmode = ShareRowExclusiveLock;
    4604            1000 :                             break;
    4605                 : 
    4606 GIC         419 :                         default:
    4607             419 :                             cmd_lockmode = AccessExclusiveLock;
    4608                 :                     }
    4609                 :                 }
    4610 CBC       35237 :                 break;
    4611 ECB             : 
    4612                 :                 /*
    4613                 :                  * These subcommands affect inheritance behaviour. Queries
    4614                 :                  * started before us will continue to see the old inheritance
    4615                 :                  * behaviour, while queries started after we commit will see
    4616                 :                  * new behaviour. No need to prevent reads or writes to the
    4617                 :                  * subtable while we hook it up though. Changing the TupDesc
    4618                 :                  * may be a problem, so keep highest lock.
    4619                 :                  */
    4620 GIC         165 :             case AT_AddInherit:
    4621                 :             case AT_DropInherit:
    4622             165 :                 cmd_lockmode = AccessExclusiveLock;
    4623             165 :                 break;
    4624                 : 
    4625                 :                 /*
    4626 ECB             :                  * These subcommands affect implicit row type conversion. They
    4627                 :                  * have affects similar to CREATE/DROP CAST on queries. don't
    4628                 :                  * provide for invalidating parse trees as a result of such
    4629                 :                  * changes, so we keep these at AccessExclusiveLock.
    4630                 :                  */
    4631 GIC          36 :             case AT_AddOf:
    4632                 :             case AT_DropOf:
    4633              36 :                 cmd_lockmode = AccessExclusiveLock;
    4634              36 :                 break;
    4635                 : 
    4636                 :                 /*
    4637                 :                  * Only used by CREATE OR REPLACE VIEW which must conflict
    4638                 :                  * with an SELECTs currently using the view.
    4639                 :                  */
    4640              97 :             case AT_ReplaceRelOptions:
    4641              97 :                 cmd_lockmode = AccessExclusiveLock;
    4642              97 :                 break;
    4643                 : 
    4644                 :                 /*
    4645                 :                  * These subcommands affect general strategies for performance
    4646                 :                  * and maintenance, though don't change the semantic results
    4647                 :                  * from normal data reads and writes. Delaying an ALTER TABLE
    4648                 :                  * behind currently active writes only delays the point where
    4649                 :                  * the new strategy begins to take effect, so there is no
    4650                 :                  * benefit in waiting. In this case the minimum restriction
    4651                 :                  * applies: we don't currently allow concurrent catalog
    4652                 :                  * updates.
    4653                 :                  */
    4654             117 :             case AT_SetStatistics:  /* Uses MVCC in getTableAttrs() */
    4655                 :             case AT_ClusterOn:  /* Uses MVCC in getIndexes() */
    4656                 :             case AT_DropCluster:    /* Uses MVCC in getIndexes() */
    4657                 :             case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
    4658                 :             case AT_ResetOptions:   /* Uses MVCC in getTableAttrs() */
    4659             117 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4660             117 :                 break;
    4661                 : 
    4662              35 :             case AT_SetLogged:
    4663                 :             case AT_SetUnLogged:
    4664              35 :                 cmd_lockmode = AccessExclusiveLock;
    4665              35 :                 break;
    4666                 : 
    4667             194 :             case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
    4668             194 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4669             194 :                 break;
    4670                 : 
    4671                 :                 /*
    4672                 :                  * Rel options are more complex than first appears. Options
    4673                 :                  * are set here for tables, views and indexes; for historical
    4674                 :                  * reasons these can all be used with ALTER TABLE, so we can't
    4675                 :                  * decide between them using the basic grammar.
    4676                 :                  */
    4677             367 :             case AT_SetRelOptions:  /* Uses MVCC in getIndexes() and
    4678                 :                                      * getTables() */
    4679                 :             case AT_ResetRelOptions:    /* Uses MVCC in getIndexes() and
    4680                 :                                          * getTables() */
    4681             367 :                 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
    4682             367 :                 break;
    4683                 : 
    4684            1241 :             case AT_AttachPartition:
    4685 CBC        1241 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4686 GIC        1241 :                 break;
    4687                 : 
    4688             258 :             case AT_DetachPartition:
    4689             258 :                 if (((PartitionCmd *) cmd->def)->concurrent)
    4690              79 :                     cmd_lockmode = ShareUpdateExclusiveLock;
    4691 ECB             :                 else
    4692 GIC         179 :                     cmd_lockmode = AccessExclusiveLock;
    4693 CBC         258 :                 break;
    4694                 : 
    4695               7 :             case AT_DetachPartitionFinalize:
    4696               7 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4697 GIC           7 :                 break;
    4698                 : 
    4699 UIC           0 :             case AT_CheckNotNull:
    4700                 : 
    4701                 :                 /*
    4702                 :                  * This only examines the table's schema; but lock must be
    4703                 :                  * strong enough to prevent concurrent DROP NOT NULL.
    4704                 :                  */
    4705               0 :                 cmd_lockmode = AccessShareLock;
    4706               0 :                 break;
    4707                 : 
    4708               0 :             default:            /* oops */
    4709               0 :                 elog(ERROR, "unrecognized alter table type: %d",
    4710                 :                      (int) cmd->subtype);
    4711                 :                 break;
    4712                 :         }
    4713                 : 
    4714 ECB             :         /*
    4715                 :          * Take the greatest lockmode from any subcommand
    4716                 :          */
    4717 CBC       45836 :         if (cmd_lockmode > lockmode)
    4718 GIC       43180 :             lockmode = cmd_lockmode;
    4719 ECB             :     }
    4720                 : 
    4721 CBC       45092 :     return lockmode;
    4722                 : }
    4723 ECB             : 
    4724                 : /*
    4725                 :  * ATController provides top level control over the phases.
    4726                 :  *
    4727                 :  * parsetree is passed in to allow it to be passed to event triggers
    4728                 :  * when requested.
    4729                 :  */
    4730                 : static void
    4731 GIC       44972 : ATController(AlterTableStmt *parsetree,
    4732                 :              Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
    4733                 :              AlterTableUtilityContext *context)
    4734                 : {
    4735           44972 :     List       *wqueue = NIL;
    4736                 :     ListCell   *lcmd;
    4737                 : 
    4738                 :     /* Phase 1: preliminary examination of commands, create work queue */
    4739           90549 :     foreach(lcmd, cmds)
    4740                 :     {
    4741           45713 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4742                 : 
    4743           45713 :         ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
    4744                 :     }
    4745                 : 
    4746                 :     /* Close the relation, but keep lock until commit */
    4747           44836 :     relation_close(rel, NoLock);
    4748                 : 
    4749                 :     /* Phase 2: update system catalogs */
    4750           44836 :     ATRewriteCatalogs(&wqueue, lockmode, context);
    4751                 : 
    4752                 :     /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
    4753           43800 :     ATRewriteTables(parsetree, &wqueue, lockmode, context);
    4754           43653 : }
    4755                 : 
    4756                 : /*
    4757                 :  * ATPrepCmd
    4758                 :  *
    4759 ECB             :  * Traffic cop for ALTER TABLE Phase 1 operations, including simple
    4760                 :  * recursion and permission checks.
    4761                 :  *
    4762                 :  * Caller must have acquired appropriate lock type on relation already.
    4763                 :  * This lock should be held until commit.
    4764                 :  */
    4765                 : static void
    4766 GIC       45921 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
    4767 ECB             :           bool recurse, bool recursing, LOCKMODE lockmode,
    4768                 :           AlterTableUtilityContext *context)
    4769                 : {
    4770                 :     AlteredTableInfo *tab;
    4771 GIC       45921 :     int         pass = AT_PASS_UNSET;
    4772 ECB             : 
    4773                 :     /* Find or create work queue entry for this table */
    4774 GIC       45921 :     tab = ATGetQueueEntry(wqueue, rel);
    4775                 : 
    4776                 :     /*
    4777 ECB             :      * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
    4778                 :      * partitions that are pending detach.
    4779                 :      */
    4780 GIC       45921 :     if (rel->rd_rel->relispartition &&
    4781            1124 :         cmd->subtype != AT_DetachPartitionFinalize &&
    4782 CBC         562 :         PartitionHasPendingDetach(RelationGetRelid(rel)))
    4783               1 :         ereport(ERROR,
    4784                 :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    4785                 :                 errmsg("cannot alter partition \"%s\" with an incomplete detach",
    4786                 :                        RelationGetRelationName(rel)),
    4787                 :                 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
    4788                 : 
    4789                 :     /*
    4790                 :      * Copy the original subcommand for each table, so we can scribble on it.
    4791 ECB             :      * This avoids conflicts when different child tables need to make
    4792                 :      * different parse transformations (for example, the same column may have
    4793                 :      * different column numbers in different children).
    4794                 :      */
    4795 GIC       45920 :     cmd = copyObject(cmd);
    4796                 : 
    4797                 :     /*
    4798                 :      * Do permissions and relkind checking, recursion to child tables if
    4799                 :      * needed, and any additional phase-1 processing needed.  (But beware of
    4800                 :      * adding any processing that looks at table details that another
    4801 ECB             :      * subcommand could change.  In some cases we reject multiple subcommands
    4802                 :      * that could try to change the same state in contrary ways.)
    4803                 :      */
    4804 CBC       45920 :     switch (cmd->subtype)
    4805                 :     {
    4806 GIC         911 :         case AT_AddColumn:      /* ADD COLUMN */
    4807             911 :             ATSimplePermissions(cmd->subtype, rel,
    4808                 :                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    4809 CBC         911 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
    4810                 :                             lockmode, context);
    4811                 :             /* Recursion occurs during execution phase */
    4812 GIC         905 :             pass = AT_PASS_ADD_COL;
    4813             905 :             break;
    4814              12 :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    4815              12 :             ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
    4816 CBC          12 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
    4817 ECB             :                             lockmode, context);
    4818                 :             /* Recursion occurs during execution phase */
    4819 GIC          12 :             pass = AT_PASS_ADD_COL;
    4820              12 :             break;
    4821             266 :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    4822 ECB             : 
    4823                 :             /*
    4824                 :              * We allow defaults on views so that INSERT into a view can have
    4825                 :              * default-ish behavior.  This works because the rewriter
    4826                 :              * substitutes default values into INSERTs before it expands
    4827                 :              * rules.
    4828                 :              */
    4829 CBC         266 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    4830 GIC         266 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4831 ECB             :             /* No command-specific prep needed */
    4832 CBC         266 :             pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
    4833 GIC         266 :             break;
    4834              28 :         case AT_CookedColumnDefault:    /* add a pre-cooked default */
    4835                 :             /* This is currently used only in CREATE TABLE */
    4836                 :             /* (so the permission check really isn't necessary) */
    4837 CBC          28 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4838                 :             /* This command never recurses */
    4839 GIC          28 :             pass = AT_PASS_ADD_OTHERCONSTR;
    4840              28 :             break;
    4841              51 :         case AT_AddIdentity:
    4842              51 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    4843                 :             /* This command never recurses */
    4844              51 :             pass = AT_PASS_ADD_OTHERCONSTR;
    4845 CBC          51 :             break;
    4846              19 :         case AT_SetIdentity:
    4847 GIC          19 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    4848                 :             /* This command never recurses */
    4849                 :             /* This should run after AddIdentity, so do it in MISC pass */
    4850              19 :             pass = AT_PASS_MISC;
    4851              19 :             break;
    4852 CBC          19 :         case AT_DropIdentity:
    4853 GIC          19 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
    4854                 :             /* This command never recurses */
    4855              19 :             pass = AT_PASS_DROP;
    4856              19 :             break;
    4857             110 :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    4858             110 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4859                 :             /* Set up recursion for phase 2; no other prep needed */
    4860 GNC         107 :             if (recurse)
    4861             101 :                 cmd->recurse = true;
    4862 GIC         107 :             pass = AT_PASS_DROP;
    4863             107 :             break;
    4864             162 :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    4865             162 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4866                 :             /* Need command-specific recursion decision */
    4867 GNC         159 :             if (recurse)
    4868             150 :                 cmd->recurse = true;
    4869             159 :             pass = AT_PASS_COL_ATTRS;
    4870             159 :             break;
    4871            3155 :         case AT_SetAttNotNull:  /* set pg_attribute.attnotnull without adding
    4872                 :                                  * a constraint */
    4873            3155 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4874                 :             /* Need command-specific recursion decision */
    4875            3155 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4876 GIC        3155 :             pass = AT_PASS_COL_ATTRS;
    4877 CBC        3155 :             break;
    4878 LBC           0 :         case AT_CheckNotNull:   /* check column is already marked NOT NULL */
    4879 UIC           0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4880 LBC           0 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4881                 :             /* No command-specific prep needed */
    4882 UIC           0 :             pass = AT_PASS_COL_ATTRS;
    4883 LBC           0 :             break;
    4884 GIC          22 :         case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
    4885 CBC          22 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4886 GIC          22 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4887 CBC          22 :             ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
    4888 GIC          16 :             pass = AT_PASS_DROP;
    4889 CBC          16 :             break;
    4890 GIC          82 :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    4891              82 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
    4892              82 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4893                 :             /* No command-specific prep needed */
    4894              82 :             pass = AT_PASS_MISC;
    4895              82 :             break;
    4896              22 :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    4897                 :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    4898              22 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
    4899                 :             /* This command never recurses */
    4900              16 :             pass = AT_PASS_MISC;
    4901 CBC          16 :             break;
    4902             115 :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    4903             115 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
    4904 GIC         115 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4905                 :             /* No command-specific prep needed */
    4906             115 :             pass = AT_PASS_MISC;
    4907             115 :             break;
    4908              33 :         case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    4909              33 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
    4910 ECB             :             /* This command never recurses */
    4911                 :             /* No command-specific prep needed */
    4912 GIC          33 :             pass = AT_PASS_MISC;
    4913 CBC          33 :             break;
    4914             781 :         case AT_DropColumn:     /* DROP COLUMN */
    4915 GIC         781 :             ATSimplePermissions(cmd->subtype, rel,
    4916                 :                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    4917 CBC         778 :             ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
    4918                 :                              lockmode, context);
    4919                 :             /* Recursion occurs during execution phase */
    4920 GIC         772 :             pass = AT_PASS_DROP;
    4921             772 :             break;
    4922 UIC           0 :         case AT_AddIndex:       /* ADD INDEX */
    4923               0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    4924                 :             /* This command never recurses */
    4925                 :             /* No command-specific prep needed */
    4926               0 :             pass = AT_PASS_ADD_INDEX;
    4927 LBC           0 :             break;
    4928 GIC       35224 :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    4929 CBC       35224 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4930 ECB             :             /* Recursion occurs during execution phase */
    4931                 :             /* No command-specific prep needed except saving recurse flag */
    4932 GIC       35224 :             if (recurse)
    4933 GNC       35072 :                 cmd->recurse = true;
    4934 GIC       35224 :             pass = AT_PASS_ADD_CONSTR;
    4935           35224 :             break;
    4936 UIC           0 :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    4937               0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    4938 ECB             :             /* This command never recurses */
    4939                 :             /* No command-specific prep needed */
    4940 LBC           0 :             pass = AT_PASS_ADD_INDEXCONSTR;
    4941               0 :             break;
    4942 GIC         264 :         case AT_DropConstraint: /* DROP CONSTRAINT */
    4943             264 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    4944             264 :             ATCheckPartitionsNotInUse(rel, lockmode);
    4945                 :             /* Other recursion occurs during execution phase */
    4946                 :             /* No command-specific prep needed except saving recurse flag */
    4947 CBC         261 :             if (recurse)
    4948 GNC         249 :                 cmd->recurse = true;
    4949 CBC         261 :             pass = AT_PASS_DROP;
    4950 GIC         261 :             break;
    4951             553 :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    4952             553 :             ATSimplePermissions(cmd->subtype, rel,
    4953                 :                                 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    4954                 :             /* See comments for ATPrepAlterColumnType */
    4955             553 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
    4956                 :                                       AT_PASS_UNSET, context);
    4957             550 :             Assert(cmd != NULL);
    4958                 :             /* Performs own recursion */
    4959             550 :             ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
    4960                 :                                   lockmode, context);
    4961 CBC         478 :             pass = AT_PASS_ALTER_TYPE;
    4962 GIC         478 :             break;
    4963              82 :         case AT_AlterColumnGenericOptions:
    4964              82 :             ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    4965                 :             /* This command never recurses */
    4966 ECB             :             /* No command-specific prep needed */
    4967 CBC          82 :             pass = AT_PASS_MISC;
    4968 GIC          82 :             break;
    4969 CBC         849 :         case AT_ChangeOwner:    /* ALTER OWNER */
    4970                 :             /* This command never recurses */
    4971 ECB             :             /* No command-specific prep needed */
    4972 CBC         849 :             pass = AT_PASS_MISC;
    4973 GIC         849 :             break;
    4974 CBC          32 :         case AT_ClusterOn:      /* CLUSTER ON */
    4975 ECB             :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    4976 CBC          32 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
    4977                 :             /* These commands never recurse */
    4978                 :             /* No command-specific prep needed */
    4979 GIC          32 :             pass = AT_PASS_MISC;
    4980              32 :             break;
    4981              16 :         case AT_SetLogged:      /* SET LOGGED */
    4982              16 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
    4983              16 :             if (tab->chgPersistence)
    4984 LBC           0 :                 ereport(ERROR,
    4985                 :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4986                 :                          errmsg("cannot change persistence setting twice")));
    4987 GIC          16 :             tab->chgPersistence = ATPrepChangePersistence(rel, true);
    4988 ECB             :             /* force rewrite if necessary; see comment in ATRewriteTables */
    4989 CBC          13 :             if (tab->chgPersistence)
    4990                 :             {
    4991              10 :                 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
    4992              10 :                 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
    4993 ECB             :             }
    4994 GIC          13 :             pass = AT_PASS_MISC;
    4995 CBC          13 :             break;
    4996              19 :         case AT_SetUnLogged:    /* SET UNLOGGED */
    4997              19 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
    4998 GIC          19 :             if (tab->chgPersistence)
    4999 LBC           0 :                 ereport(ERROR,
    5000 ECB             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5001                 :                          errmsg("cannot change persistence setting twice")));
    5002 CBC          19 :             tab->chgPersistence = ATPrepChangePersistence(rel, false);
    5003 ECB             :             /* force rewrite if necessary; see comment in ATRewriteTables */
    5004 CBC          16 :             if (tab->chgPersistence)
    5005                 :             {
    5006 GBC          13 :                 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
    5007 GIC          13 :                 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
    5008                 :             }
    5009              16 :             pass = AT_PASS_MISC;
    5010              16 :             break;
    5011               3 :         case AT_DropOids:       /* SET WITHOUT OIDS */
    5012 GBC           3 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5013               3 :             pass = AT_PASS_DROP;
    5014 GIC           3 :             break;
    5015 GBC          24 :         case AT_SetAccessMethod:    /* SET ACCESS METHOD */
    5016              24 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
    5017                 : 
    5018                 :             /* partitioned tables don't have an access method */
    5019 GIC          24 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    5020               3 :                 ereport(ERROR,
    5021                 :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    5022                 :                          errmsg("cannot change access method of a partitioned table")));
    5023                 : 
    5024 ECB             :             /* check if another access method change was already requested */
    5025 CBC          21 :             if (OidIsValid(tab->newAccessMethod))
    5026 GIC           6 :                 ereport(ERROR,
    5027                 :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5028 ECB             :                          errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
    5029                 : 
    5030 GIC          15 :             ATPrepSetAccessMethod(tab, rel, cmd->name);
    5031              15 :             pass = AT_PASS_MISC;    /* does not matter; no work in Phase 2 */
    5032              15 :             break;
    5033              79 :         case AT_SetTableSpace:  /* SET TABLESPACE */
    5034              79 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
    5035                 :                                 ATT_PARTITIONED_INDEX);
    5036                 :             /* This command never recurses */
    5037              79 :             ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
    5038 CBC          79 :             pass = AT_PASS_MISC;    /* doesn't actually matter */
    5039 GIC          79 :             break;
    5040             464 :         case AT_SetRelOptions:  /* SET (...) */
    5041                 :         case AT_ResetRelOptions:    /* RESET (...) */
    5042 ECB             :         case AT_ReplaceRelOptions:  /* reset them all, then set just these */
    5043 GIC         464 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
    5044                 :             /* This command never recurses */
    5045                 :             /* No command-specific prep needed */
    5046 CBC         464 :             pass = AT_PASS_MISC;
    5047 GIC         464 :             break;
    5048 CBC         143 :         case AT_AddInherit:     /* INHERIT */
    5049 GIC         143 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5050 ECB             :             /* This command never recurses */
    5051 GIC         143 :             ATPrepAddInherit(rel);
    5052             134 :             pass = AT_PASS_MISC;
    5053             134 :             break;
    5054 CBC          22 :         case AT_DropInherit:    /* NO INHERIT */
    5055 GIC          22 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5056                 :             /* This command never recurses */
    5057 ECB             :             /* No command-specific prep needed */
    5058 GIC          22 :             pass = AT_PASS_MISC;
    5059              22 :             break;
    5060 CBC          36 :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    5061              36 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5062                 :             /* Recursion occurs during execution phase */
    5063 GIC          33 :             pass = AT_PASS_MISC;
    5064              33 :             break;
    5065             194 :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5066             194 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5067                 :             /* Recursion occurs during execution phase */
    5068                 :             /* No command-specific prep needed except saving recurse flag */
    5069             194 :             if (recurse)
    5070 GNC         194 :                 cmd->recurse = true;
    5071 GIC         194 :             pass = AT_PASS_MISC;
    5072             194 :             break;
    5073 CBC         198 :         case AT_ReplicaIdentity:    /* REPLICA IDENTITY ... */
    5074 GIC         198 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
    5075             198 :             pass = AT_PASS_MISC;
    5076                 :             /* This command never recurses */
    5077                 :             /* No command-specific prep needed */
    5078 CBC         198 :             break;
    5079 GIC         168 :         case AT_EnableTrig:     /* ENABLE TRIGGER variants */
    5080                 :         case AT_EnableAlwaysTrig:
    5081 ECB             :         case AT_EnableReplicaTrig:
    5082                 :         case AT_EnableTrigAll:
    5083                 :         case AT_EnableTrigUser:
    5084                 :         case AT_DisableTrig:    /* DISABLE TRIGGER variants */
    5085                 :         case AT_DisableTrigAll:
    5086                 :         case AT_DisableTrigUser:
    5087 CBC         168 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    5088 ECB             :             /* Set up recursion for phase 2; no other prep needed */
    5089 CBC         168 :             if (recurse)
    5090             154 :                 cmd->recurse = true;
    5091 GIC         168 :             pass = AT_PASS_MISC;
    5092             168 :             break;
    5093             239 :         case AT_EnableRule:     /* ENABLE/DISABLE RULE variants */
    5094                 :         case AT_EnableAlwaysRule:
    5095                 :         case AT_EnableReplicaRule:
    5096                 :         case AT_DisableRule:
    5097                 :         case AT_AddOf:          /* OF */
    5098                 :         case AT_DropOf:         /* NOT OF */
    5099                 :         case AT_EnableRowSecurity:
    5100                 :         case AT_DisableRowSecurity:
    5101                 :         case AT_ForceRowSecurity:
    5102 ECB             :         case AT_NoForceRowSecurity:
    5103 GIC         239 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5104                 :             /* These commands never recurse */
    5105                 :             /* No command-specific prep needed */
    5106             239 :             pass = AT_PASS_MISC;
    5107             239 :             break;
    5108              23 :         case AT_GenericOptions:
    5109              23 :             ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    5110                 :             /* No command-specific prep needed */
    5111 CBC          23 :             pass = AT_PASS_MISC;
    5112 GIC          23 :             break;
    5113 CBC        1235 :         case AT_AttachPartition:
    5114            1235 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
    5115                 :             /* No command-specific prep needed */
    5116            1235 :             pass = AT_PASS_MISC;
    5117 GIC        1235 :             break;
    5118             258 :         case AT_DetachPartition:
    5119 CBC         258 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5120 ECB             :             /* No command-specific prep needed */
    5121 CBC         255 :             pass = AT_PASS_MISC;
    5122             255 :             break;
    5123               7 :         case AT_DetachPartitionFinalize:
    5124 GIC           7 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
    5125                 :             /* No command-specific prep needed */
    5126 CBC           7 :             pass = AT_PASS_MISC;
    5127               7 :             break;
    5128 LBC           0 :         default:                /* oops */
    5129 UIC           0 :             elog(ERROR, "unrecognized alter table type: %d",
    5130                 :                  (int) cmd->subtype);
    5131                 :             pass = AT_PASS_UNSET;   /* keep compiler quiet */
    5132                 :             break;
    5133                 :     }
    5134 GIC       45779 :     Assert(pass > AT_PASS_UNSET);
    5135                 : 
    5136 ECB             :     /* Add the subcommand to the appropriate list for phase 2 */
    5137 CBC       45779 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
    5138 GIC       45779 : }
    5139 ECB             : 
    5140                 : /*
    5141                 :  * ATRewriteCatalogs
    5142                 :  *
    5143                 :  * Traffic cop for ALTER TABLE Phase 2 operations.  Subcommands are
    5144                 :  * dispatched in a "safe" execution order (designed to avoid unnecessary
    5145                 :  * conflicts).
    5146                 :  */
    5147                 : static void
    5148 CBC       44836 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
    5149 ECB             :                   AlterTableUtilityContext *context)
    5150                 : {
    5151                 :     int         pass;
    5152                 :     ListCell   *ltab;
    5153                 : 
    5154                 :     /*
    5155                 :      * We process all the tables "in parallel", one pass at a time.  This is
    5156                 :      * needed because we may have to propagate work from one table to another
    5157                 :      * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
    5158                 :      * re-adding of the foreign key constraint to the other table).  Work can
    5159                 :      * only be propagated into later passes, however.
    5160                 :      */
    5161 GIC      533639 :     for (pass = 0; pass < AT_NUM_PASSES; pass++)
    5162 ECB             :     {
    5163                 :         /* Go through each table that needs to be processed */
    5164 CBC      983980 :         foreach(ltab, *wqueue)
    5165 ECB             :         {
    5166 GIC      495177 :             AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5167 CBC      495177 :             List       *subcmds = tab->subcmds[pass];
    5168 ECB             :             ListCell   *lcmd;
    5169                 : 
    5170 CBC      495177 :             if (subcmds == NIL)
    5171          395396 :                 continue;
    5172 ECB             : 
    5173                 :             /*
    5174                 :              * Open the relation and store it in tab.  This allows subroutines
    5175                 :              * close and reopen, if necessary.  Appropriate lock was obtained
    5176                 :              * by phase 1, needn't get it again.
    5177                 :              */
    5178 CBC       99781 :             tab->rel = relation_open(tab->relid, NoLock);
    5179                 : 
    5180          205421 :             foreach(lcmd, subcmds)
    5181 GIC      106676 :                 ATExecCmd(wqueue, tab,
    5182 CBC      106676 :                           lfirst_node(AlterTableCmd, lcmd),
    5183 ECB             :                           lockmode, pass, context);
    5184                 : 
    5185 EUB             :             /*
    5186                 :              * After the ALTER TYPE pass, do cleanup work (this is not done in
    5187                 :              * ATExecAlterColumnType since it should be done only once if
    5188                 :              * multiple columns of a table are altered).
    5189                 :              */
    5190 GBC       98745 :             if (pass == AT_PASS_ALTER_TYPE)
    5191 CBC         430 :                 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
    5192 ECB             : 
    5193 CBC       98745 :             if (tab->rel)
    5194 ECB             :             {
    5195 CBC       98745 :                 relation_close(tab->rel, NoLock);
    5196           98745 :                 tab->rel = NULL;
    5197 ECB             :             }
    5198                 :         }
    5199                 :     }
    5200                 : 
    5201                 :     /* Check to see if a toast table must be added. */
    5202 CBC       89325 :     foreach(ltab, *wqueue)
    5203 ECB             :     {
    5204 GIC       45525 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5205 ECB             : 
    5206                 :         /*
    5207                 :          * If the table is source table of ATTACH PARTITION command, we did
    5208                 :          * not modify anything about it that will change its toasting
    5209                 :          * requirement, so no need to check.
    5210                 :          */
    5211 CBC       45525 :         if (((tab->relkind == RELKIND_RELATION ||
    5212 GIC        2926 :               tab->relkind == RELKIND_PARTITIONED_TABLE) &&
    5213 CBC       44623 :              tab->partition_constraint == NULL) ||
    5214            1811 :             tab->relkind == RELKIND_MATVIEW)
    5215           43739 :             AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
    5216 ECB             :     }
    5217 GIC       43800 : }
    5218                 : 
    5219 ECB             : /*
    5220                 :  * ATExecCmd: dispatch a subcommand to appropriate execution routine
    5221                 :  */
    5222                 : static void
    5223 GIC      106676 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
    5224 ECB             :           AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,
    5225                 :           AlterTableUtilityContext *context)
    5226                 : {
    5227 CBC      106676 :     ObjectAddress address = InvalidObjectAddress;
    5228          106676 :     Relation    rel = tab->rel;
    5229 EUB             : 
    5230 GBC      106676 :     switch (cmd->subtype)
    5231                 :     {
    5232 GIC         914 :         case AT_AddColumn:      /* ADD COLUMN */
    5233 EUB             :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    5234 GBC         914 :             address = ATExecAddColumn(wqueue, tab, rel, &cmd,
    5235 GNC         914 :                                       cmd->recurse, false,
    5236 ECB             :                                       lockmode, cur_pass, context);
    5237 CBC         857 :             break;
    5238 GBC         266 :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    5239             266 :             address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
    5240 GIC         242 :             break;
    5241              28 :         case AT_CookedColumnDefault:    /* add a pre-cooked default */
    5242 GBC          28 :             address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
    5243              28 :             break;
    5244 CBC          51 :         case AT_AddIdentity:
    5245              51 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5246 ECB             :                                       cur_pass, context);
    5247 GIC          48 :             Assert(cmd != NULL);
    5248              48 :             address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
    5249 CBC          36 :             break;
    5250              19 :         case AT_SetIdentity:
    5251              19 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5252 ECB             :                                       cur_pass, context);
    5253 CBC          19 :             Assert(cmd != NULL);
    5254              19 :             address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
    5255 GIC          16 :             break;
    5256              19 :         case AT_DropIdentity:
    5257 CBC          19 :             address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
    5258 GIC          16 :             break;
    5259 CBC         107 :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    5260 GNC         107 :             address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
    5261 CBC          65 :             break;
    5262 GIC         159 :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    5263 GNC         159 :             address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
    5264             159 :                                        cmd->recurse, false, NULL, lockmode);
    5265             144 :             break;
    5266           28396 :         case AT_SetAttNotNull:  /* set pg_attribute.attnotnull */
    5267           28396 :             ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
    5268 CBC       28387 :             break;
    5269 LBC           0 :         case AT_CheckNotNull:   /* check column is already marked NOT NULL */
    5270               0 :             ATExecCheckNotNull(tab, rel, cmd->name, lockmode);
    5271 UIC           0 :             break;
    5272 GIC          16 :         case AT_DropExpression:
    5273 CBC          16 :             address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
    5274              13 :             break;
    5275              82 :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    5276 GIC          82 :             address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
    5277              58 :             break;
    5278 CBC          13 :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    5279              13 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
    5280              13 :             break;
    5281 GIC           3 :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    5282 CBC           3 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
    5283 GIC           3 :             break;
    5284             115 :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    5285 CBC         115 :             address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
    5286             109 :             break;
    5287 GNC          33 :         case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    5288              33 :             address = ATExecSetCompression(rel, cmd->name, cmd->def,
    5289 ECB             :                                            lockmode);
    5290 GBC          30 :             break;
    5291 GIC         772 :         case AT_DropColumn:     /* DROP COLUMN */
    5292             772 :             address = ATExecDropColumn(wqueue, rel, cmd->name,
    5293 GNC         772 :                                        cmd->behavior, cmd->recurse, false,
    5294 CBC         772 :                                        cmd->missing_ok, lockmode,
    5295 ECB             :                                        NULL);
    5296 CBC         688 :             break;
    5297             435 :         case AT_AddIndex:       /* ADD INDEX */
    5298             435 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
    5299 EUB             :                                      lockmode);
    5300 GIC         383 :             break;
    5301             199 :         case AT_ReAddIndex:     /* ADD INDEX */
    5302 CBC         199 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
    5303                 :                                      lockmode);
    5304             199 :             break;
    5305 GIC           7 :         case AT_ReAddStatistics:    /* ADD STATISTICS */
    5306 CBC           7 :             address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
    5307 ECB             :                                           true, lockmode);
    5308 GIC           7 :             break;
    5309 CBC       36768 :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    5310 ECB             :             /* Transform the command only during initial examination */
    5311 CBC       36768 :             if (cur_pass == AT_PASS_ADD_CONSTR)
    5312           35209 :                 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
    5313 GNC       35224 :                                           cmd->recurse, lockmode,
    5314 ECB             :                                           cur_pass, context);
    5315                 :             /* Depending on constraint type, might be no more work to do now */
    5316 CBC       36753 :             if (cmd != NULL)
    5317                 :                 address =
    5318 GIC        1544 :                     ATExecAddConstraint(wqueue, tab, rel,
    5319 CBC        1544 :                                         (Constraint *) cmd->def,
    5320 GNC        1544 :                                         cmd->recurse, false, lockmode);
    5321 CBC       36566 :             break;
    5322 GIC          82 :         case AT_ReAddConstraint:    /* Re-add pre-existing check constraint */
    5323                 :             address =
    5324 CBC          82 :                 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
    5325 ECB             :                                     true, true, lockmode);
    5326 CBC          76 :             break;
    5327               7 :         case AT_ReAddDomainConstraint:  /* Re-add pre-existing domain check
    5328                 :                                          * constraint */
    5329                 :             address =
    5330               7 :                 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
    5331 GIC           7 :                                          ((AlterDomainStmt *) cmd->def)->def,
    5332                 :                                          NULL);
    5333 CBC           4 :             break;
    5334              27 :         case AT_ReAddComment:   /* Re-add existing comment */
    5335              27 :             address = CommentObject((CommentStmt *) cmd->def);
    5336              27 :             break;
    5337 GIC       33361 :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    5338 CBC       33361 :             address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
    5339 ECB             :                                                lockmode);
    5340 CBC       33355 :             break;
    5341              33 :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    5342              33 :             address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
    5343 GIC          27 :             break;
    5344             194 :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5345 GNC         194 :             address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
    5346 ECB             :                                                false, lockmode);
    5347 CBC         194 :             break;
    5348             261 :         case AT_DropConstraint: /* DROP CONSTRAINT */
    5349 GIC         261 :             ATExecDropConstraint(rel, cmd->name, cmd->behavior,
    5350 GNC         261 :                                  cmd->recurse, false,
    5351 CBC         261 :                                  cmd->missing_ok, lockmode);
    5352             174 :             break;
    5353 GIC         463 :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    5354                 :             /* parse transformation was done earlier */
    5355 CBC         463 :             address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
    5356             445 :             break;
    5357 GIC          82 :         case AT_AlterColumnGenericOptions:  /* ALTER COLUMN OPTIONS */
    5358                 :             address =
    5359              82 :                 ATExecAlterColumnGenericOptions(rel, cmd->name,
    5360              82 :                                                 (List *) cmd->def, lockmode);
    5361              79 :             break;
    5362             849 :         case AT_ChangeOwner:    /* ALTER OWNER */
    5363             846 :             ATExecChangeOwner(RelationGetRelid(rel),
    5364 CBC         849 :                               get_rolespec_oid(cmd->newowner, false),
    5365                 :                               false, lockmode);
    5366             840 :             break;
    5367              32 :         case AT_ClusterOn:      /* CLUSTER ON */
    5368              32 :             address = ATExecClusterOn(rel, cmd->name, lockmode);
    5369              29 :             break;
    5370               9 :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    5371 GIC           9 :             ATExecDropCluster(rel, lockmode);
    5372               6 :             break;
    5373              29 :         case AT_SetLogged:      /* SET LOGGED */
    5374                 :         case AT_SetUnLogged:    /* SET UNLOGGED */
    5375              29 :             break;
    5376               3 :         case AT_DropOids:       /* SET WITHOUT OIDS */
    5377                 :             /* nothing to do here, oid columns don't exist anymore */
    5378               3 :             break;
    5379               9 :         case AT_SetAccessMethod:    /* SET ACCESS METHOD */
    5380 ECB             :             /* handled specially in Phase 3 */
    5381 GIC           9 :             break;
    5382              79 :         case AT_SetTableSpace:  /* SET TABLESPACE */
    5383 ECB             : 
    5384                 :             /*
    5385                 :              * Only do this for partitioned tables and indexes, for which this
    5386                 :              * is just a catalog change.  Other relation types which have
    5387                 :              * storage are handled by Phase 3.
    5388                 :              */
    5389 CBC          79 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
    5390              73 :                 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    5391              18 :                 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
    5392                 : 
    5393              76 :             break;
    5394             464 :         case AT_SetRelOptions:  /* SET (...) */
    5395 ECB             :         case AT_ResetRelOptions:    /* RESET (...) */
    5396                 :         case AT_ReplaceRelOptions:  /* replace entire option list */
    5397 GIC         464 :             ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
    5398 CBC         438 :             break;
    5399              60 :         case AT_EnableTrig:     /* ENABLE TRIGGER name */
    5400              60 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5401 ECB             :                                        TRIGGER_FIRES_ON_ORIGIN, false,
    5402 GIC          60 :                                        cmd->recurse,
    5403 ECB             :                                        lockmode);
    5404 CBC          60 :             break;
    5405 GBC          20 :         case AT_EnableAlwaysTrig:   /* ENABLE ALWAYS TRIGGER name */
    5406              20 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5407                 :                                        TRIGGER_FIRES_ALWAYS, false,
    5408 GIC          20 :                                        cmd->recurse,
    5409                 :                                        lockmode);
    5410              20 :             break;
    5411 CBC           8 :         case AT_EnableReplicaTrig:  /* ENABLE REPLICA TRIGGER name */
    5412 GIC           8 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5413                 :                                        TRIGGER_FIRES_ON_REPLICA, false,
    5414 CBC           8 :                                        cmd->recurse,
    5415 ECB             :                                        lockmode);
    5416 GIC           8 :             break;
    5417              68 :         case AT_DisableTrig:    /* DISABLE TRIGGER name */
    5418              68 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5419                 :                                        TRIGGER_DISABLED, false,
    5420              68 :                                        cmd->recurse,
    5421                 :                                        lockmode);
    5422              68 :             break;
    5423 UIC           0 :         case AT_EnableTrigAll:  /* ENABLE TRIGGER ALL */
    5424               0 :             ATExecEnableDisableTrigger(rel, NULL,
    5425 ECB             :                                        TRIGGER_FIRES_ON_ORIGIN, false,
    5426 UIC           0 :                                        cmd->recurse,
    5427                 :                                        lockmode);
    5428               0 :             break;
    5429 GIC           6 :         case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
    5430               6 :             ATExecEnableDisableTrigger(rel, NULL,
    5431                 :                                        TRIGGER_DISABLED, false,
    5432               6 :                                        cmd->recurse,
    5433                 :                                        lockmode);
    5434               6 :             break;
    5435 UIC           0 :         case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
    5436               0 :             ATExecEnableDisableTrigger(rel, NULL,
    5437                 :                                        TRIGGER_FIRES_ON_ORIGIN, true,
    5438 LBC           0 :                                        cmd->recurse,
    5439                 :                                        lockmode);
    5440 UIC           0 :             break;
    5441 CBC           6 :         case AT_DisableTrigUser:    /* DISABLE TRIGGER USER */
    5442 GIC           6 :             ATExecEnableDisableTrigger(rel, NULL,
    5443 ECB             :                                        TRIGGER_DISABLED, true,
    5444 CBC           6 :                                        cmd->recurse,
    5445                 :                                        lockmode);
    5446 GIC           6 :             break;
    5447 ECB             : 
    5448 CBC           3 :         case AT_EnableRule:     /* ENABLE RULE name */
    5449 GIC           3 :             ATExecEnableDisableRule(rel, cmd->name,
    5450                 :                                     RULE_FIRES_ON_ORIGIN, lockmode);
    5451               3 :             break;
    5452 UIC           0 :         case AT_EnableAlwaysRule:   /* ENABLE ALWAYS RULE name */
    5453               0 :             ATExecEnableDisableRule(rel, cmd->name,
    5454                 :                                     RULE_FIRES_ALWAYS, lockmode);
    5455 LBC           0 :             break;
    5456 GIC           3 :         case AT_EnableReplicaRule:  /* ENABLE REPLICA RULE name */
    5457 CBC           3 :             ATExecEnableDisableRule(rel, cmd->name,
    5458 ECB             :                                     RULE_FIRES_ON_REPLICA, lockmode);
    5459 CBC           3 :             break;
    5460 GIC           3 :         case AT_DisableRule:    /* DISABLE RULE name */
    5461               3 :             ATExecEnableDisableRule(rel, cmd->name,
    5462                 :                                     RULE_DISABLED, lockmode);
    5463               3 :             break;
    5464                 : 
    5465             134 :         case AT_AddInherit:
    5466             134 :             address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
    5467 CBC          95 :             break;
    5468              22 :         case AT_DropInherit:
    5469 GIC          22 :             address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
    5470 CBC          19 :             break;
    5471 GIC          33 :         case AT_AddOf:
    5472 CBC          33 :             address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
    5473              15 :             break;
    5474 GIC           3 :         case AT_DropOf:
    5475               3 :             ATExecDropOf(rel, lockmode);
    5476               3 :             break;
    5477             207 :         case AT_ReplicaIdentity:
    5478             207 :             ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
    5479 CBC         186 :             break;
    5480 GIC         135 :         case AT_EnableRowSecurity:
    5481 CBC         135 :             ATExecSetRowSecurity(rel, true);
    5482 GIC         135 :             break;
    5483               4 :         case AT_DisableRowSecurity:
    5484               4 :             ATExecSetRowSecurity(rel, false);
    5485               4 :             break;
    5486              40 :         case AT_ForceRowSecurity:
    5487              40 :             ATExecForceNoForceRowSecurity(rel, true);
    5488 CBC          40 :             break;
    5489              15 :         case AT_NoForceRowSecurity:
    5490              15 :             ATExecForceNoForceRowSecurity(rel, false);
    5491              15 :             break;
    5492              23 :         case AT_GenericOptions:
    5493 GIC          23 :             ATExecGenericOptions(rel, (List *) cmd->def);
    5494 CBC          22 :             break;
    5495 GIC        1235 :         case AT_AttachPartition:
    5496            1235 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5497                 :                                       cur_pass, context);
    5498            1220 :             Assert(cmd != NULL);
    5499            1220 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    5500 GNC        1034 :                 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
    5501                 :                                                 context);
    5502                 :             else
    5503             186 :                 address = ATExecAttachPartitionIdx(wqueue, rel,
    5504             186 :                                                    ((PartitionCmd *) cmd->def)->name);
    5505 CBC        1064 :             break;
    5506 GIC         255 :         case AT_DetachPartition:
    5507 CBC         255 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5508                 :                                       cur_pass, context);
    5509             252 :             Assert(cmd != NULL);
    5510                 :             /* ATPrepCmd ensures it must be a table */
    5511             252 :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5512 GNC         252 :             address = ATExecDetachPartition(wqueue, tab, rel,
    5513             252 :                                             ((PartitionCmd *) cmd->def)->name,
    5514             252 :                                             ((PartitionCmd *) cmd->def)->concurrent);
    5515 CBC         187 :             break;
    5516               7 :         case AT_DetachPartitionFinalize:
    5517 GNC           7 :             address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
    5518 CBC           7 :             break;
    5519 LBC           0 :         default:                /* oops */
    5520               0 :             elog(ERROR, "unrecognized alter table type: %d",
    5521 ECB             :                  (int) cmd->subtype);
    5522                 :             break;
    5523                 :     }
    5524                 : 
    5525                 :     /*
    5526                 :      * Report the subcommand to interested event triggers.
    5527                 :      */
    5528 CBC      105640 :     if (cmd)
    5529 GIC       70431 :         EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
    5530 ECB             : 
    5531                 :     /*
    5532                 :      * Bump the command counter to ensure the next subcommand in the sequence
    5533                 :      * can see the changes so far
    5534                 :      */
    5535 CBC      105640 :     CommandCounterIncrement();
    5536          105640 : }
    5537 ECB             : 
    5538                 : /*
    5539                 :  * ATParseTransformCmd: perform parse transformation for one subcommand
    5540                 :  *
    5541                 :  * Returns the transformed subcommand tree, if there is one, else NULL.
    5542                 :  *
    5543                 :  * The parser may hand back additional AlterTableCmd(s) and/or other
    5544                 :  * utility statements, either before or after the original subcommand.
    5545                 :  * Other AlterTableCmds are scheduled into the appropriate slot of the
    5546 EUB             :  * AlteredTableInfo (they had better be for later passes than the current one).
    5547                 :  * Utility statements that are supposed to happen before the AlterTableCmd
    5548                 :  * are executed immediately.  Those that are supposed to happen afterwards
    5549 ECB             :  * are added to the tab->afterStmts list to be done at the very end.
    5550                 :  */
    5551                 : static AlterTableCmd *
    5552 CBC       38191 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
    5553 ECB             :                     AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    5554                 :                     int cur_pass, AlterTableUtilityContext *context)
    5555                 : {
    5556 CBC       38191 :     AlterTableCmd *newcmd = NULL;
    5557           38191 :     AlterTableStmt *atstmt = makeNode(AlterTableStmt);
    5558 ECB             :     List       *beforeStmts;
    5559                 :     List       *afterStmts;
    5560                 :     ListCell   *lc;
    5561                 : 
    5562                 :     /* Gin up an AlterTableStmt with just this subcommand and this table */
    5563 CBC       38191 :     atstmt->relation =
    5564           38191 :         makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
    5565           38191 :                      pstrdup(RelationGetRelationName(rel)),
    5566                 :                      -1);
    5567           38191 :     atstmt->relation->inh = recurse;
    5568           38191 :     atstmt->cmds = list_make1(cmd);
    5569           38191 :     atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
    5570           38191 :     atstmt->missing_ok = false;
    5571 ECB             : 
    5572                 :     /* Transform the AlterTableStmt */
    5573 CBC       38191 :     atstmt = transformAlterTableStmt(RelationGetRelid(rel),
    5574 ECB             :                                      atstmt,
    5575                 :                                      context->queryString,
    5576                 :                                      &beforeStmts,
    5577                 :                                      &afterStmts);
    5578                 : 
    5579                 :     /* Execute any statements that should happen before these subcommand(s) */
    5580 GIC       38313 :     foreach(lc, beforeStmts)
    5581 ECB             :     {
    5582 CBC         161 :         Node       *stmt = (Node *) lfirst(lc);
    5583 ECB             : 
    5584 GIC         161 :         ProcessUtilityForAlterTable(stmt, context);
    5585 CBC         155 :         CommandCounterIncrement();
    5586 ECB             :     }
    5587                 : 
    5588                 :     /* Examine the transformed subcommands and schedule them appropriately */
    5589 CBC      101688 :     foreach(lc, atstmt->cmds)
    5590 ECB             :     {
    5591 GIC       63536 :         AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
    5592                 :         int         pass;
    5593 ECB             : 
    5594                 :         /*
    5595                 :          * This switch need only cover the subcommand types that can be added
    5596                 :          * by parse_utilcmd.c; otherwise, we'll use the default strategy of
    5597                 :          * executing the subcommand immediately, as a substitute for the
    5598                 :          * original subcommand.  (Note, however, that this does cause
    5599                 :          * AT_AddConstraint subcommands to be rescheduled into later passes,
    5600                 :          * which is important for index and foreign key constraints.)
    5601                 :          *
    5602                 :          * We assume we needn't do any phase-1 checks for added subcommands.
    5603                 :          */
    5604 CBC       63536 :         switch (cmd2->subtype)
    5605                 :         {
    5606 GNC       25241 :             case AT_SetAttNotNull:
    5607           25241 :                 ATSimpleRecursion(wqueue, rel, cmd2, recurse, lockmode, context);
    5608 CBC       25241 :                 pass = AT_PASS_COL_ATTRS;
    5609           25241 :                 break;
    5610             444 :             case AT_AddIndex:
    5611 ECB             :                 /* This command never recurses */
    5612                 :                 /* No command-specific prep needed */
    5613 GIC         444 :                 pass = AT_PASS_ADD_INDEX;
    5614 CBC         444 :                 break;
    5615           33361 :             case AT_AddIndexConstraint:
    5616 ECB             :                 /* This command never recurses */
    5617                 :                 /* No command-specific prep needed */
    5618 CBC       33361 :                 pass = AT_PASS_ADD_INDEXCONSTR;
    5619           33361 :                 break;
    5620 GIC        1547 :             case AT_AddConstraint:
    5621 ECB             :                 /* Recursion occurs during execution phase */
    5622 CBC        1547 :                 if (recurse)
    5623 GNC        1526 :                     cmd2->recurse = true;
    5624 CBC        1547 :                 switch (castNode(Constraint, cmd2->def)->contype)
    5625 ECB             :                 {
    5626 LBC           0 :                     case CONSTR_PRIMARY:
    5627 ECB             :                     case CONSTR_UNIQUE:
    5628                 :                     case CONSTR_EXCLUSION:
    5629 LBC           0 :                         pass = AT_PASS_ADD_INDEXCONSTR;
    5630               0 :                         break;
    5631 CBC        1547 :                     default:
    5632 GIC        1547 :                         pass = AT_PASS_ADD_OTHERCONSTR;
    5633 CBC        1547 :                         break;
    5634 ECB             :                 }
    5635 CBC        1547 :                 break;
    5636 LBC           0 :             case AT_AlterColumnGenericOptions:
    5637 ECB             :                 /* This command never recurses */
    5638                 :                 /* No command-specific prep needed */
    5639 UIC           0 :                 pass = AT_PASS_MISC;
    5640 LBC           0 :                 break;
    5641 CBC        2943 :             default:
    5642            2943 :                 pass = cur_pass;
    5643            2943 :                 break;
    5644 ECB             :         }
    5645                 : 
    5646 CBC       63536 :         if (pass < cur_pass)
    5647 ECB             :         {
    5648                 :             /* Cannot schedule into a pass we already finished */
    5649 LBC           0 :             elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
    5650 ECB             :                  pass);
    5651                 :         }
    5652 CBC       63536 :         else if (pass > cur_pass)
    5653 ECB             :         {
    5654                 :             /* OK, queue it up for later */
    5655 CBC       60593 :             tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
    5656 ECB             :         }
    5657                 :         else
    5658                 :         {
    5659                 :             /*
    5660                 :              * We should see at most one subcommand for the current pass,
    5661                 :              * which is the transformed version of the original subcommand.
    5662                 :              */
    5663 CBC        2943 :             if (newcmd == NULL && cmd->subtype == cmd2->subtype)
    5664 ECB             :             {
    5665                 :                 /* Found the transformed version of our subcommand */
    5666 GIC        2943 :                 newcmd = cmd2;
    5667 ECB             :             }
    5668                 :             else
    5669 UIC           0 :                 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
    5670                 :                      pass);
    5671 ECB             :         }
    5672                 :     }
    5673                 : 
    5674                 :     /* Queue up any after-statements to happen at the end */
    5675 GIC       38152 :     tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
    5676 ECB             : 
    5677 GIC       38152 :     return newcmd;
    5678 ECB             : }
    5679                 : 
    5680                 : /*
    5681                 :  * ATRewriteTables: ALTER TABLE phase 3
    5682                 :  */
    5683                 : static void
    5684 CBC       43800 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
    5685 ECB             :                 AlterTableUtilityContext *context)
    5686                 : {
    5687                 :     ListCell   *ltab;
    5688                 : 
    5689                 :     /* Go through each table that needs to be checked or rewritten */
    5690 CBC       89203 :     foreach(ltab, *wqueue)
    5691 ECB             :     {
    5692 CBC       45519 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5693                 : 
    5694 ECB             :         /* Relations without storage may be ignored here */
    5695 GIC       45519 :         if (!RELKIND_HAS_STORAGE(tab->relkind))
    5696 CBC        2788 :             continue;
    5697 EUB             : 
    5698                 :         /*
    5699                 :          * If we change column data types, the operation has to be propagated
    5700                 :          * to tables that use this table's rowtype as a column type.
    5701                 :          * tab->newvals will also be non-NULL in the case where we're adding a
    5702                 :          * column with a default.  We choose to forbid that case as well,
    5703 ECB             :          * since composite types might eventually support defaults.
    5704                 :          *
    5705                 :          * (Eventually we'll probably need to check for composite type
    5706                 :          * dependencies even when we're just scanning the table without a
    5707                 :          * rewrite, but at the moment a composite type does not enforce any
    5708                 :          * constraints, so it's not necessary/appropriate to enforce them just
    5709 EUB             :          * during ALTER.)
    5710                 :          */
    5711 GIC       42731 :         if (tab->newvals != NIL || tab->rewrite > 0)
    5712 EUB             :         {
    5713                 :             Relation    rel;
    5714                 : 
    5715 CBC         652 :             rel = table_open(tab->relid, NoLock);
    5716             652 :             find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
    5717 GIC         646 :             table_close(rel, NoLock);
    5718 ECB             :         }
    5719                 : 
    5720                 :         /*
    5721                 :          * We only need to rewrite the table if at least one column needs to
    5722                 :          * be recomputed, or we are changing its persistence or access method.
    5723                 :          *
    5724                 :          * There are two reasons for requiring a rewrite when changing
    5725                 :          * persistence: on one hand, we need to ensure that the buffers
    5726 EUB             :          * belonging to each of the two relations are marked with or without
    5727                 :          * BM_PERMANENT properly.  On the other hand, since rewriting creates
    5728                 :          * and assigns a new relfilenumber, we automatically create or drop an
    5729                 :          * init fork for the relation as appropriate.
    5730 ECB             :          */
    5731 CBC       42725 :         if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
    5732 GIC         356 :         {
    5733 ECB             :             /* Build a temporary relation and copy data */
    5734                 :             Relation    OldHeap;
    5735                 :             Oid         OIDNewHeap;
    5736                 :             Oid         NewAccessMethod;
    5737                 :             Oid         NewTableSpace;
    5738                 :             char        persistence;
    5739                 : 
    5740 CBC         372 :             OldHeap = table_open(tab->relid, NoLock);
    5741 ECB             : 
    5742                 :             /*
    5743                 :              * We don't support rewriting of system catalogs; there are too
    5744                 :              * many corner cases and too little benefit.  In particular this
    5745                 :              * is certainly not going to work for mapped catalogs.
    5746                 :              */
    5747 CBC         372 :             if (IsSystemRelation(OldHeap))
    5748 LBC           0 :                 ereport(ERROR,
    5749 ECB             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5750                 :                          errmsg("cannot rewrite system relation \"%s\"",
    5751                 :                                 RelationGetRelationName(OldHeap))));
    5752                 : 
    5753 CBC         372 :             if (RelationIsUsedAsCatalogTable(OldHeap))
    5754               1 :                 ereport(ERROR,
    5755 ECB             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5756                 :                          errmsg("cannot rewrite table \"%s\" used as a catalog table",
    5757                 :                                 RelationGetRelationName(OldHeap))));
    5758                 : 
    5759                 :             /*
    5760                 :              * Don't allow rewrite on temp tables of other backends ... their
    5761                 :              * local buffer manager is not going to cope.
    5762                 :              */
    5763 CBC         371 :             if (RELATION_IS_OTHER_TEMP(OldHeap))
    5764 LBC           0 :                 ereport(ERROR,
    5765 ECB             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5766                 :                          errmsg("cannot rewrite temporary tables of other sessions")));
    5767                 : 
    5768                 :             /*
    5769                 :              * Select destination tablespace (same as original unless user
    5770                 :              * requested a change)
    5771                 :              */
    5772 CBC         371 :             if (tab->newTableSpace)
    5773 LBC           0 :                 NewTableSpace = tab->newTableSpace;
    5774 ECB             :             else
    5775 GIC         371 :                 NewTableSpace = OldHeap->rd_rel->reltablespace;
    5776                 : 
    5777 ECB             :             /*
    5778                 :              * Select destination access method (same as original unless user
    5779                 :              * requested a change)
    5780                 :              */
    5781 CBC         371 :             if (OidIsValid(tab->newAccessMethod))
    5782 GIC           9 :                 NewAccessMethod = tab->newAccessMethod;
    5783 ECB             :             else
    5784 GIC         362 :                 NewAccessMethod = OldHeap->rd_rel->relam;
    5785 ECB             : 
    5786                 :             /*
    5787                 :              * Select persistence of transient table (same as original unless
    5788                 :              * user requested a change)
    5789                 :              */
    5790 CBC         371 :             persistence = tab->chgPersistence ?
    5791             354 :                 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
    5792 ECB             : 
    5793 GBC         371 :             table_close(OldHeap, NoLock);
    5794 EUB             : 
    5795                 :             /*
    5796                 :              * Fire off an Event Trigger now, before actually rewriting the
    5797                 :              * table.
    5798                 :              *
    5799                 :              * We don't support Event Trigger for nested commands anywhere,
    5800                 :              * here included, and parsetree is given NULL when coming from
    5801                 :              * AlterTableInternal.
    5802 ECB             :              *
    5803                 :              * And fire it only once.
    5804                 :              */
    5805 GIC         371 :             if (parsetree)
    5806             371 :                 EventTriggerTableRewrite((Node *) parsetree,
    5807                 :                                          tab->relid,
    5808                 :                                          tab->rewrite);
    5809 ECB             : 
    5810                 :             /*
    5811                 :              * Create transient table that will receive the modified data.
    5812                 :              *
    5813                 :              * Ensure it is marked correctly as logged or unlogged.  We have
    5814                 :              * to do this here so that buffers for the new relfilenumber will
    5815                 :              * have the right persistence set, and at the same time ensure
    5816                 :              * that the original filenumbers's buffers will get read in with
    5817                 :              * the correct setting (i.e. the original one).  Otherwise a
    5818                 :              * rollback after the rewrite would possibly result with buffers
    5819                 :              * for the original filenumbers having the wrong persistence
    5820                 :              * setting.
    5821                 :              *
    5822                 :              * NB: This relies on swap_relation_files() also swapping the
    5823                 :              * persistence. That wouldn't work for pg_class, but that can't be
    5824                 :              * unlogged anyway.
    5825                 :              */
    5826 GIC         368 :             OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
    5827 ECB             :                                        persistence, lockmode);
    5828                 : 
    5829                 :             /*
    5830                 :              * Copy the heap data into the new table with the desired
    5831                 :              * modifications, and test the current data within the table
    5832                 :              * against new constraints generated by ALTER TABLE commands.
    5833                 :              */
    5834 GIC         368 :             ATRewriteTable(tab, OIDNewHeap, lockmode);
    5835                 : 
    5836                 :             /*
    5837                 :              * Swap the physical files of the old and new heaps, then rebuild
    5838 ECB             :              * indexes and discard the old heap.  We can use RecentXmin for
    5839                 :              * the table's new relfrozenxid because we rewrote all the tuples
    5840                 :              * in ATRewriteTable, so no older Xid remains in the table.  Also,
    5841                 :              * we never try to swap toast tables by content, since we have no
    5842                 :              * interest in letting this code work on system catalogs.
    5843                 :              */
    5844 CBC         359 :             finish_heap_swap(tab->relid, OIDNewHeap,
    5845 ECB             :                              false, false, true,
    5846 GIC         359 :                              !OidIsValid(tab->newTableSpace),
    5847                 :                              RecentXmin,
    5848 ECB             :                              ReadNextMultiXactId(),
    5849                 :                              persistence);
    5850                 : 
    5851 GIC         356 :             InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
    5852                 :         }
    5853           42353 :         else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
    5854                 :         {
    5855 CBC           6 :             if (tab->chgPersistence)
    5856 GIC           6 :                 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
    5857 ECB             :         }
    5858                 :         else
    5859                 :         {
    5860                 :             /*
    5861                 :              * If required, test the current data within the table against new
    5862                 :              * constraints generated by ALTER TABLE commands, but don't
    5863                 :              * rebuild data.
    5864                 :              */
    5865 GIC       42347 :             if (tab->constraints != NIL || tab->verify_new_notnull ||
    5866 CBC       41184 :                 tab->partition_constraint != NULL)
    5867 GIC        1998 :                 ATRewriteTable(tab, InvalidOid, lockmode);
    5868                 : 
    5869                 :             /*
    5870                 :              * If we had SET TABLESPACE but no reason to reconstruct tuples,
    5871                 :              * just do a block-by-block copy.
    5872                 :              */
    5873           42253 :             if (tab->newTableSpace)
    5874              61 :                 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
    5875                 :         }
    5876                 : 
    5877                 :         /*
    5878                 :          * Also change persistence of owned sequences, so that it matches the
    5879 ECB             :          * table persistence.
    5880                 :          */
    5881 CBC       42615 :         if (tab->chgPersistence)
    5882 ECB             :         {
    5883 CBC          23 :             List       *seqlist = getOwnedSequences(tab->relid);
    5884 ECB             :             ListCell   *lc;
    5885                 : 
    5886 GIC          38 :             foreach(lc, seqlist)
    5887                 :             {
    5888 CBC          15 :                 Oid         seq_relid = lfirst_oid(lc);
    5889 ECB             : 
    5890 CBC          15 :                 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
    5891                 :             }
    5892                 :         }
    5893 ECB             :     }
    5894                 : 
    5895                 :     /*
    5896                 :      * Foreign key constraints are checked in a final pass, since (a) it's
    5897                 :      * generally best to examine each one separately, and (b) it's at least
    5898                 :      * theoretically possible that we have changed both relations of the
    5899                 :      * foreign key, and we'd better have finished both rewrites before we try
    5900                 :      * to read the tables.
    5901 EUB             :      */
    5902 GIC       88994 :     foreach(ltab, *wqueue)
    5903                 :     {
    5904 GBC       45341 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5905           45341 :         Relation    rel = NULL;
    5906 ECB             :         ListCell   *lcon;
    5907                 : 
    5908                 :         /* Relations without storage may be ignored here too */
    5909 GIC       45341 :         if (!RELKIND_HAS_STORAGE(tab->relkind))
    5910 CBC        2751 :             continue;
    5911 EUB             : 
    5912 GIC       43348 :         foreach(lcon, tab->constraints)
    5913                 :         {
    5914 GBC         789 :             NewConstraint *con = lfirst(lcon);
    5915 EUB             : 
    5916 CBC         789 :             if (con->contype == CONSTR_FOREIGN)
    5917 ECB             :             {
    5918 CBC         466 :                 Constraint *fkconstraint = (Constraint *) con->qual;
    5919                 :                 Relation    refrel;
    5920                 : 
    5921             466 :                 if (rel == NULL)
    5922                 :                 {
    5923                 :                     /* Long since locked, no need for another */
    5924 GBC         460 :                     rel = table_open(tab->relid, NoLock);
    5925                 :                 }
    5926                 : 
    5927 CBC         466 :                 refrel = table_open(con->refrelid, RowShareLock);
    5928                 : 
    5929 GIC         466 :                 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
    5930 ECB             :                                              con->refindid,
    5931                 :                                              con->conid);
    5932                 : 
    5933                 :                 /*
    5934                 :                  * No need to mark the constraint row as validated, we did
    5935                 :                  * that when we inserted the row earlier.
    5936                 :                  */
    5937                 : 
    5938 CBC         435 :                 table_close(refrel, NoLock);
    5939                 :             }
    5940                 :         }
    5941 ECB             : 
    5942 GIC       42559 :         if (rel)
    5943             429 :             table_close(rel, NoLock);
    5944 EUB             :     }
    5945                 : 
    5946                 :     /* Finally, run any afterStmts that were queued up */
    5947 GIC       88944 :     foreach(ltab, *wqueue)
    5948                 :     {
    5949           45291 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5950 ECB             :         ListCell   *lc;
    5951                 : 
    5952 CBC       45325 :         foreach(lc, tab->afterStmts)
    5953                 :         {
    5954 GIC          34 :             Node       *stmt = (Node *) lfirst(lc);
    5955                 : 
    5956              34 :             ProcessUtilityForAlterTable(stmt, context);
    5957              34 :             CommandCounterIncrement();
    5958                 :         }
    5959 ECB             :     }
    5960 GIC       43653 : }
    5961                 : 
    5962                 : /*
    5963                 :  * ATRewriteTable: scan or rewrite one table
    5964                 :  *
    5965 ECB             :  * OIDNewHeap is InvalidOid if we don't need to rewrite
    5966                 :  */
    5967                 : static void
    5968 GIC        2366 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
    5969                 : {
    5970 ECB             :     Relation    oldrel;
    5971                 :     Relation    newrel;
    5972                 :     TupleDesc   oldTupDesc;
    5973                 :     TupleDesc   newTupDesc;
    5974 GIC        2366 :     bool        needscan = false;
    5975                 :     List       *notnull_attrs;
    5976                 :     int         i;
    5977                 :     ListCell   *l;
    5978                 :     EState     *estate;
    5979                 :     CommandId   mycid;
    5980                 :     BulkInsertState bistate;
    5981                 :     int         ti_options;
    5982            2366 :     ExprState  *partqualstate = NULL;
    5983                 : 
    5984                 :     /*
    5985                 :      * Open the relation(s).  We have surely already locked the existing
    5986 ECB             :      * table.
    5987                 :      */
    5988 GIC        2366 :     oldrel = table_open(tab->relid, NoLock);
    5989            2366 :     oldTupDesc = tab->oldDesc;
    5990 CBC        2366 :     newTupDesc = RelationGetDescr(oldrel);  /* includes all mods */
    5991 ECB             : 
    5992 CBC        2366 :     if (OidIsValid(OIDNewHeap))
    5993 GIC         368 :         newrel = table_open(OIDNewHeap, lockmode);
    5994                 :     else
    5995            1998 :         newrel = NULL;
    5996                 : 
    5997                 :     /*
    5998                 :      * Prepare a BulkInsertState and options for table_tuple_insert.  The FSM
    5999                 :      * is empty, so don't bother using it.
    6000                 :      */
    6001            2366 :     if (newrel)
    6002                 :     {
    6003             368 :         mycid = GetCurrentCommandId(true);
    6004             368 :         bistate = GetBulkInsertState();
    6005             368 :         ti_options = TABLE_INSERT_SKIP_FSM;
    6006 ECB             :     }
    6007                 :     else
    6008                 :     {
    6009                 :         /* keep compiler quiet about using these uninitialized */
    6010 GIC        1998 :         mycid = 0;
    6011            1998 :         bistate = NULL;
    6012            1998 :         ti_options = 0;
    6013                 :     }
    6014                 : 
    6015 ECB             :     /*
    6016                 :      * Generate the constraint and default execution states
    6017                 :      */
    6018                 : 
    6019 GIC        2366 :     estate = CreateExecutorState();
    6020                 : 
    6021                 :     /* Build the needed expression execution states */
    6022 CBC        3200 :     foreach(l, tab->constraints)
    6023 EUB             :     {
    6024 GIC         834 :         NewConstraint *con = lfirst(l);
    6025                 : 
    6026             834 :         switch (con->contype)
    6027                 :         {
    6028 CBC         365 :             case CONSTR_CHECK:
    6029             365 :                 needscan = true;
    6030 GIC         365 :                 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
    6031             365 :                 break;
    6032             469 :             case CONSTR_FOREIGN:
    6033                 :                 /* Nothing to do here */
    6034             469 :                 break;
    6035 UIC           0 :             default:
    6036               0 :                 elog(ERROR, "unrecognized constraint type: %d",
    6037                 :                      (int) con->contype);
    6038 ECB             :         }
    6039 EUB             :     }
    6040                 : 
    6041                 :     /* Build expression execution states for partition check quals */
    6042 GIC        2366 :     if (tab->partition_constraint)
    6043                 :     {
    6044             906 :         needscan = true;
    6045             906 :         partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
    6046                 :     }
    6047 ECB             : 
    6048 GBC        2747 :     foreach(l, tab->newvals)
    6049                 :     {
    6050 CBC         381 :         NewColumnValue *ex = lfirst(l);
    6051                 : 
    6052                 :         /* expr already planned */
    6053 GIC         381 :         ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
    6054                 :     }
    6055                 : 
    6056 CBC        2366 :     notnull_attrs = NIL;
    6057            2366 :     if (newrel || tab->verify_new_notnull)
    6058                 :     {
    6059 ECB             :         /*
    6060                 :          * If we are rebuilding the tuples OR if we added any new but not
    6061                 :          * verified NOT NULL constraints, check all not-null constraints. This
    6062                 :          * is a bit of overkill but it minimizes risk of bugs, and
    6063                 :          * heap_attisnull is a pretty cheap test anyway.
    6064                 :          */
    6065 CBC        2831 :         for (i = 0; i < newTupDesc->natts; i++)
    6066 ECB             :         {
    6067 GIC        2073 :             Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
    6068 ECB             : 
    6069 GIC        2073 :             if (attr->attnotnull && !attr->attisdropped)
    6070             805 :                 notnull_attrs = lappend_int(notnull_attrs, i);
    6071                 :         }
    6072             758 :         if (notnull_attrs)
    6073             571 :             needscan = true;
    6074                 :     }
    6075                 : 
    6076            2366 :     if (newrel || needscan)
    6077                 :     {
    6078                 :         ExprContext *econtext;
    6079                 :         TupleTableSlot *oldslot;
    6080 ECB             :         TupleTableSlot *newslot;
    6081                 :         TableScanDesc scan;
    6082                 :         MemoryContext oldCxt;
    6083 GIC        1983 :         List       *dropped_attrs = NIL;
    6084                 :         ListCell   *lc;
    6085                 :         Snapshot    snapshot;
    6086                 : 
    6087            1983 :         if (newrel)
    6088             368 :             ereport(DEBUG1,
    6089                 :                     (errmsg_internal("rewriting table \"%s\"",
    6090                 :                                      RelationGetRelationName(oldrel))));
    6091                 :         else
    6092            1615 :             ereport(DEBUG1,
    6093                 :                     (errmsg_internal("verifying table \"%s\"",
    6094                 :                                      RelationGetRelationName(oldrel))));
    6095                 : 
    6096            1983 :         if (newrel)
    6097                 :         {
    6098                 :             /*
    6099                 :              * All predicate locks on the tuples or pages are about to be made
    6100                 :              * invalid, because we move tuples around.  Promote them to
    6101 ECB             :              * relation locks.
    6102                 :              */
    6103 GIC         368 :             TransferPredicateLocksToHeapRelation(oldrel);
    6104                 :         }
    6105                 : 
    6106            1983 :         econtext = GetPerTupleExprContext(estate);
    6107                 : 
    6108                 :         /*
    6109 ECB             :          * Create necessary tuple slots. When rewriting, two slots are needed,
    6110                 :          * otherwise one suffices. In the case where one slot suffices, we
    6111                 :          * need to use the new tuple descriptor, otherwise some constraints
    6112                 :          * can't be evaluated.  Note that even when the tuple layout is the
    6113                 :          * same and no rewrite is required, the tupDescs might not be
    6114                 :          * (consider ADD COLUMN without a default).
    6115                 :          */
    6116 GIC        1983 :         if (tab->rewrite)
    6117                 :         {
    6118             368 :             Assert(newrel != NULL);
    6119 CBC         368 :             oldslot = MakeSingleTupleTableSlot(oldTupDesc,
    6120                 :                                                table_slot_callbacks(oldrel));
    6121             368 :             newslot = MakeSingleTupleTableSlot(newTupDesc,
    6122                 :                                                table_slot_callbacks(newrel));
    6123                 : 
    6124                 :             /*
    6125                 :              * Set all columns in the new slot to NULL initially, to ensure
    6126 ECB             :              * columns added as part of the rewrite are initialized to NULL.
    6127                 :              * That is necessary as tab->newvals will not contain an
    6128                 :              * expression for columns with a NULL default, e.g. when adding a
    6129                 :              * column without a default together with a column with a default
    6130                 :              * requiring an actual rewrite.
    6131                 :              */
    6132 GIC         368 :             ExecStoreAllNullTuple(newslot);
    6133                 :         }
    6134                 :         else
    6135                 :         {
    6136            1615 :             oldslot = MakeSingleTupleTableSlot(newTupDesc,
    6137                 :                                                table_slot_callbacks(oldrel));
    6138            1615 :             newslot = NULL;
    6139                 :         }
    6140 ECB             : 
    6141                 :         /*
    6142                 :          * Any attributes that are dropped according to the new tuple
    6143                 :          * descriptor can be set to NULL. We precompute the list of dropped
    6144                 :          * attributes to avoid needing to do so in the per-tuple loop.
    6145                 :          */
    6146 GIC        7029 :         for (i = 0; i < newTupDesc->natts; i++)
    6147                 :         {
    6148 CBC        5046 :             if (TupleDescAttr(newTupDesc, i)->attisdropped)
    6149             381 :                 dropped_attrs = lappend_int(dropped_attrs, i);
    6150                 :         }
    6151                 : 
    6152                 :         /*
    6153                 :          * Scan through the rows, generating a new row if needed and then
    6154                 :          * checking all the constraints.
    6155                 :          */
    6156            1983 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
    6157 GIC        1983 :         scan = table_beginscan(oldrel, snapshot, 0, NULL);
    6158 ECB             : 
    6159                 :         /*
    6160                 :          * Switch to per-tuple memory context and reset it for each tuple
    6161                 :          * produced, so we don't leak memory.
    6162                 :          */
    6163 CBC        1983 :         oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
    6164                 : 
    6165          385085 :         while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
    6166                 :         {
    6167                 :             TupleTableSlot *insertslot;
    6168                 : 
    6169 GIC      381222 :             if (tab->rewrite > 0)
    6170                 :             {
    6171                 :                 /* Extract data from old tuple */
    6172           48647 :                 slot_getallattrs(oldslot);
    6173           48647 :                 ExecClearTuple(newslot);
    6174                 : 
    6175                 :                 /* copy attributes */
    6176           48647 :                 memcpy(newslot->tts_values, oldslot->tts_values,
    6177 CBC       48647 :                        sizeof(Datum) * oldslot->tts_nvalid);
    6178 GIC       48647 :                 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
    6179 CBC       48647 :                        sizeof(bool) * oldslot->tts_nvalid);
    6180 ECB             : 
    6181                 :                 /* Set dropped attributes to null in new tuple */
    6182 GIC       48690 :                 foreach(lc, dropped_attrs)
    6183              43 :                     newslot->tts_isnull[lfirst_int(lc)] = true;
    6184 ECB             : 
    6185                 :                 /*
    6186                 :                  * Constraints and GENERATED expressions might reference the
    6187                 :                  * tableoid column, so fill tts_tableOid with the desired
    6188                 :                  * value.  (We must do this each time, because it gets
    6189                 :                  * overwritten with newrel's OID during storing.)
    6190                 :                  */
    6191 CBC       48647 :                 newslot->tts_tableOid = RelationGetRelid(oldrel);
    6192                 : 
    6193 ECB             :                 /*
    6194                 :                  * Process supplied expressions to replace selected columns.
    6195                 :                  *
    6196                 :                  * First, evaluate expressions whose inputs come from the old
    6197                 :                  * tuple.
    6198                 :                  */
    6199 CBC       48647 :                 econtext->ecxt_scantuple = oldslot;
    6200                 : 
    6201 GIC      100279 :                 foreach(l, tab->newvals)
    6202 ECB             :                 {
    6203 GIC       51638 :                     NewColumnValue *ex = lfirst(l);
    6204 ECB             : 
    6205 GIC       51638 :                     if (ex->is_generated)
    6206              30 :                         continue;
    6207                 : 
    6208           51608 :                     newslot->tts_values[ex->attnum - 1]
    6209           51602 :                         = ExecEvalExpr(ex->exprstate,
    6210                 :                                        econtext,
    6211           51608 :                                        &newslot->tts_isnull[ex->attnum - 1]);
    6212                 :                 }
    6213 ECB             : 
    6214 GIC       48641 :                 ExecStoreVirtualTuple(newslot);
    6215                 : 
    6216                 :                 /*
    6217 ECB             :                  * Now, evaluate any expressions whose inputs come from the
    6218                 :                  * new tuple.  We assume these columns won't reference each
    6219                 :                  * other, so that there's no ordering dependency.
    6220                 :                  */
    6221 GIC       48641 :                 econtext->ecxt_scantuple = newslot;
    6222 ECB             : 
    6223 GIC      100273 :                 foreach(l, tab->newvals)
    6224 ECB             :                 {
    6225 GIC       51632 :                     NewColumnValue *ex = lfirst(l);
    6226                 : 
    6227 CBC       51632 :                     if (!ex->is_generated)
    6228 GIC       51602 :                         continue;
    6229 ECB             : 
    6230 GIC          30 :                     newslot->tts_values[ex->attnum - 1]
    6231 CBC          30 :                         = ExecEvalExpr(ex->exprstate,
    6232 ECB             :                                        econtext,
    6233 GIC          30 :                                        &newslot->tts_isnull[ex->attnum - 1]);
    6234                 :                 }
    6235 ECB             : 
    6236 GIC       48641 :                 insertslot = newslot;
    6237                 :             }
    6238                 :             else
    6239                 :             {
    6240                 :                 /*
    6241                 :                  * If there's no rewrite, old and new table are guaranteed to
    6242                 :                  * have the same AM, so we can just use the old slot to verify
    6243 ECB             :                  * new constraints etc.
    6244                 :                  */
    6245 GIC      332575 :                 insertslot = oldslot;
    6246                 :             }
    6247                 : 
    6248                 :             /* Now check any constraints on the possibly-changed tuple */
    6249 CBC      381216 :             econtext->ecxt_scantuple = insertslot;
    6250                 : 
    6251 GIC     1669304 :             foreach(l, notnull_attrs)
    6252                 :             {
    6253         1288115 :                 int         attn = lfirst_int(l);
    6254                 : 
    6255         1288115 :                 if (slot_attisnull(insertslot, attn + 1))
    6256                 :                 {
    6257 CBC          27 :                     Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
    6258                 : 
    6259 GIC          27 :                     ereport(ERROR,
    6260                 :                             (errcode(ERRCODE_NOT_NULL_VIOLATION),
    6261                 :                              errmsg("column \"%s\" of relation \"%s\" contains null values",
    6262                 :                                     NameStr(attr->attname),
    6263 ECB             :                                     RelationGetRelationName(oldrel)),
    6264                 :                              errtablecol(oldrel, attn + 1)));
    6265                 :                 }
    6266                 :             }
    6267                 : 
    6268 CBC      385230 :             foreach(l, tab->constraints)
    6269                 :             {
    6270            4074 :                 NewConstraint *con = lfirst(l);
    6271                 : 
    6272 GIC        4074 :                 switch (con->contype)
    6273                 :                 {
    6274            4030 :                     case CONSTR_CHECK:
    6275            4030 :                         if (!ExecCheck(con->qualstate, econtext))
    6276 CBC          33 :                             ereport(ERROR,
    6277                 :                                     (errcode(ERRCODE_CHECK_VIOLATION),
    6278 ECB             :                                      errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
    6279                 :                                             con->name,
    6280                 :                                             RelationGetRelationName(oldrel)),
    6281                 :                                      errtableconstraint(oldrel, con->name)));
    6282 GIC        3997 :                         break;
    6283 GNC          44 :                     case CONSTR_NOTNULL:
    6284                 :                     case CONSTR_FOREIGN:
    6285                 :                         /* Nothing to do here */
    6286 CBC          44 :                         break;
    6287 LBC           0 :                     default:
    6288               0 :                         elog(ERROR, "unrecognized constraint type: %d",
    6289                 :                              (int) con->contype);
    6290                 :                 }
    6291                 :             }
    6292                 : 
    6293 GIC      381156 :             if (partqualstate && !ExecCheck(partqualstate, econtext))
    6294                 :             {
    6295 CBC          37 :                 if (tab->validate_default)
    6296 GIC          13 :                     ereport(ERROR,
    6297                 :                             (errcode(ERRCODE_CHECK_VIOLATION),
    6298 ECB             :                              errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
    6299                 :                                     RelationGetRelationName(oldrel)),
    6300                 :                              errtable(oldrel)));
    6301                 :                 else
    6302 CBC          24 :                     ereport(ERROR,
    6303                 :                             (errcode(ERRCODE_CHECK_VIOLATION),
    6304 ECB             :                              errmsg("partition constraint of relation \"%s\" is violated by some row",
    6305                 :                                     RelationGetRelationName(oldrel)),
    6306                 :                              errtable(oldrel)));
    6307                 :             }
    6308                 : 
    6309                 :             /* Write the tuple out to the new relation */
    6310 CBC      381119 :             if (newrel)
    6311 GBC       48638 :                 table_tuple_insert(newrel, insertslot, mycid,
    6312 EUB             :                                    ti_options, bistate);
    6313                 : 
    6314 GIC      381119 :             ResetExprContext(econtext);
    6315                 : 
    6316          381119 :             CHECK_FOR_INTERRUPTS();
    6317                 :         }
    6318 ECB             : 
    6319 GIC        1880 :         MemoryContextSwitchTo(oldCxt);
    6320 CBC        1880 :         table_endscan(scan);
    6321            1880 :         UnregisterSnapshot(snapshot);
    6322                 : 
    6323 GIC        1880 :         ExecDropSingleTupleTableSlot(oldslot);
    6324 CBC        1880 :         if (newslot)
    6325 GIC         359 :             ExecDropSingleTupleTableSlot(newslot);
    6326 ECB             :     }
    6327                 : 
    6328 GIC        2263 :     FreeExecutorState(estate);
    6329 ECB             : 
    6330 GIC        2263 :     table_close(oldrel, NoLock);
    6331            2263 :     if (newrel)
    6332 ECB             :     {
    6333 CBC         359 :         FreeBulkInsertState(bistate);
    6334                 : 
    6335 GIC         359 :         table_finish_bulk_insert(newrel, ti_options);
    6336                 : 
    6337             359 :         table_close(newrel, NoLock);
    6338                 :     }
    6339            2263 : }
    6340                 : 
    6341 ECB             : /*
    6342                 :  * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
    6343                 :  */
    6344                 : static AlteredTableInfo *
    6345 CBC       48759 : ATGetQueueEntry(List **wqueue, Relation rel)
    6346 ECB             : {
    6347 GIC       48759 :     Oid         relid = RelationGetRelid(rel);
    6348 ECB             :     AlteredTableInfo *tab;
    6349                 :     ListCell   *ltab;
    6350                 : 
    6351 GIC       52842 :     foreach(ltab, *wqueue)
    6352 ECB             :     {
    6353 GIC        6083 :         tab = (AlteredTableInfo *) lfirst(ltab);
    6354            6083 :         if (tab->relid == relid)
    6355            2000 :             return tab;
    6356                 :     }
    6357                 : 
    6358                 :     /*
    6359 ECB             :      * Not there, so add it.  Note that we make a copy of the relation's
    6360                 :      * existing descriptor before anything interesting can happen to it.
    6361                 :      */
    6362 GIC       46759 :     tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
    6363 CBC       46759 :     tab->relid = relid;
    6364           46759 :     tab->rel = NULL;         /* set later */
    6365 GIC       46759 :     tab->relkind = rel->rd_rel->relkind;
    6366           46759 :     tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
    6367           46759 :     tab->newAccessMethod = InvalidOid;
    6368 CBC       46759 :     tab->newTableSpace = InvalidOid;
    6369 GIC       46759 :     tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
    6370           46759 :     tab->chgPersistence = false;
    6371                 : 
    6372 CBC       46759 :     *wqueue = lappend(*wqueue, tab);
    6373                 : 
    6374 GIC       46759 :     return tab;
    6375                 : }
    6376                 : 
    6377                 : static const char *
    6378              21 : alter_table_type_to_string(AlterTableType cmdtype)
    6379 ECB             : {
    6380 GIC          21 :     switch (cmdtype)
    6381                 :     {
    6382 LBC           0 :         case AT_AddColumn:
    6383                 :         case AT_AddColumnToView:
    6384 UIC           0 :             return "ADD COLUMN";
    6385               0 :         case AT_ColumnDefault:
    6386                 :         case AT_CookedColumnDefault:
    6387               0 :             return "ALTER COLUMN ... SET DEFAULT";
    6388 GIC           3 :         case AT_DropNotNull:
    6389               3 :             return "ALTER COLUMN ... DROP NOT NULL";
    6390               3 :         case AT_SetNotNull:
    6391 CBC           3 :             return "ALTER COLUMN ... SET NOT NULL";
    6392 UNC           0 :         case AT_SetAttNotNull:
    6393               0 :             return NULL;        /* not real grammar */
    6394 UIC           0 :         case AT_DropExpression:
    6395 LBC           0 :             return "ALTER COLUMN ... DROP EXPRESSION";
    6396               0 :         case AT_CheckNotNull:
    6397 UIC           0 :             return NULL;        /* not real grammar */
    6398 LBC           0 :         case AT_SetStatistics:
    6399 UIC           0 :             return "ALTER COLUMN ... SET STATISTICS";
    6400 GIC           6 :         case AT_SetOptions:
    6401               6 :             return "ALTER COLUMN ... SET";
    6402 UIC           0 :         case AT_ResetOptions:
    6403               0 :             return "ALTER COLUMN ... RESET";
    6404               0 :         case AT_SetStorage:
    6405               0 :             return "ALTER COLUMN ... SET STORAGE";
    6406               0 :         case AT_SetCompression:
    6407               0 :             return "ALTER COLUMN ... SET COMPRESSION";
    6408 GIC           3 :         case AT_DropColumn:
    6409               3 :             return "DROP COLUMN";
    6410 UIC           0 :         case AT_AddIndex:
    6411                 :         case AT_ReAddIndex:
    6412 LBC           0 :             return NULL;        /* not real grammar */
    6413 UIC           0 :         case AT_AddConstraint:
    6414                 :         case AT_ReAddConstraint:
    6415                 :         case AT_ReAddDomainConstraint:
    6416                 :         case AT_AddIndexConstraint:
    6417               0 :             return "ADD CONSTRAINT";
    6418 GIC           3 :         case AT_AlterConstraint:
    6419               3 :             return "ALTER CONSTRAINT";
    6420 UIC           0 :         case AT_ValidateConstraint:
    6421               0 :             return "VALIDATE CONSTRAINT";
    6422 LBC           0 :         case AT_DropConstraint:
    6423 UIC           0 :             return "DROP CONSTRAINT";
    6424               0 :         case AT_ReAddComment:
    6425               0 :             return NULL;        /* not real grammar */
    6426               0 :         case AT_AlterColumnType:
    6427               0 :             return "ALTER COLUMN ... SET DATA TYPE";
    6428               0 :         case AT_AlterColumnGenericOptions:
    6429 LBC           0 :             return "ALTER COLUMN ... OPTIONS";
    6430               0 :         case AT_ChangeOwner:
    6431 UIC           0 :             return "OWNER TO";
    6432               0 :         case AT_ClusterOn:
    6433               0 :             return "CLUSTER ON";
    6434               0 :         case AT_DropCluster:
    6435               0 :             return "SET WITHOUT CLUSTER";
    6436 LBC           0 :         case AT_SetAccessMethod:
    6437 UIC           0 :             return "SET ACCESS METHOD";
    6438 LBC           0 :         case AT_SetLogged:
    6439 UIC           0 :             return "SET LOGGED";
    6440               0 :         case AT_SetUnLogged:
    6441               0 :             return "SET UNLOGGED";
    6442 LBC           0 :         case AT_DropOids:
    6443 UIC           0 :             return "SET WITHOUT OIDS";
    6444               0 :         case AT_SetTableSpace:
    6445 LBC           0 :             return "SET TABLESPACE";
    6446               0 :         case AT_SetRelOptions:
    6447 UIC           0 :             return "SET";
    6448               0 :         case AT_ResetRelOptions:
    6449 LBC           0 :             return "RESET";
    6450               0 :         case AT_ReplaceRelOptions:
    6451               0 :             return NULL;        /* not real grammar */
    6452               0 :         case AT_EnableTrig:
    6453 UIC           0 :             return "ENABLE TRIGGER";
    6454               0 :         case AT_EnableAlwaysTrig:
    6455 LBC           0 :             return "ENABLE ALWAYS TRIGGER";
    6456               0 :         case AT_EnableReplicaTrig:
    6457 UIC           0 :             return "ENABLE REPLICA TRIGGER";
    6458               0 :         case AT_DisableTrig:
    6459               0 :             return "DISABLE TRIGGER";
    6460               0 :         case AT_EnableTrigAll:
    6461               0 :             return "ENABLE TRIGGER ALL";
    6462               0 :         case AT_DisableTrigAll:
    6463               0 :             return "DISABLE TRIGGER ALL";
    6464 LBC           0 :         case AT_EnableTrigUser:
    6465 UIC           0 :             return "ENABLE TRIGGER USER";
    6466               0 :         case AT_DisableTrigUser:
    6467               0 :             return "DISABLE TRIGGER USER";
    6468               0 :         case AT_EnableRule:
    6469               0 :             return "ENABLE RULE";
    6470               0 :         case AT_EnableAlwaysRule:
    6471               0 :             return "ENABLE ALWAYS RULE";
    6472 LBC           0 :         case AT_EnableReplicaRule:
    6473 UIC           0 :             return "ENABLE REPLICA RULE";
    6474 LBC           0 :         case AT_DisableRule:
    6475 UIC           0 :             return "DISABLE RULE";
    6476 LBC           0 :         case AT_AddInherit:
    6477 UIC           0 :             return "INHERIT";
    6478 LBC           0 :         case AT_DropInherit:
    6479               0 :             return "NO INHERIT";
    6480 UIC           0 :         case AT_AddOf:
    6481 LBC           0 :             return "OF";
    6482               0 :         case AT_DropOf:
    6483 UIC           0 :             return "NOT OF";
    6484 LBC           0 :         case AT_ReplicaIdentity:
    6485 UIC           0 :             return "REPLICA IDENTITY";
    6486               0 :         case AT_EnableRowSecurity:
    6487 LBC           0 :             return "ENABLE ROW SECURITY";
    6488 UIC           0 :         case AT_DisableRowSecurity:
    6489               0 :             return "DISABLE ROW SECURITY";
    6490               0 :         case AT_ForceRowSecurity:
    6491               0 :             return "FORCE ROW SECURITY";
    6492               0 :         case AT_NoForceRowSecurity:
    6493               0 :             return "NO FORCE ROW SECURITY";
    6494 LBC           0 :         case AT_GenericOptions:
    6495 UIC           0 :             return "OPTIONS";
    6496 LBC           0 :         case AT_AttachPartition:
    6497 UIC           0 :             return "ATTACH PARTITION";
    6498 CBC           3 :         case AT_DetachPartition:
    6499 GIC           3 :             return "DETACH PARTITION";
    6500 LBC           0 :         case AT_DetachPartitionFinalize:
    6501               0 :             return "DETACH PARTITION ... FINALIZE";
    6502 UIC           0 :         case AT_AddIdentity:
    6503 LBC           0 :             return "ALTER COLUMN ... ADD IDENTITY";
    6504               0 :         case AT_SetIdentity:
    6505 UIC           0 :             return "ALTER COLUMN ... SET";
    6506 LBC           0 :         case AT_DropIdentity:
    6507 UIC           0 :             return "ALTER COLUMN ... DROP IDENTITY";
    6508               0 :         case AT_ReAddStatistics:
    6509 LBC           0 :             return NULL;        /* not real grammar */
    6510                 :     }
    6511                 : 
    6512 UIC           0 :     return NULL;
    6513                 : }
    6514                 : 
    6515                 : /*
    6516                 :  * ATSimplePermissions
    6517                 :  *
    6518 ECB             :  * - Ensure that it is a relation (or possibly a view)
    6519                 :  * - Ensure this user is the owner
    6520                 :  * - Ensure that it is not a system table
    6521                 :  */
    6522                 : static void
    6523 GIC       47080 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
    6524 ECB             : {
    6525                 :     int         actual_target;
    6526                 : 
    6527 GIC       47080 :     switch (rel->rd_rel->relkind)
    6528 ECB             :     {
    6529 GIC       45994 :         case RELKIND_RELATION:
    6530 ECB             :         case RELKIND_PARTITIONED_TABLE:
    6531 GIC       45994 :             actual_target = ATT_TABLE;
    6532 CBC       45994 :             break;
    6533 GIC         198 :         case RELKIND_VIEW:
    6534             198 :             actual_target = ATT_VIEW;
    6535             198 :             break;
    6536              23 :         case RELKIND_MATVIEW:
    6537              23 :             actual_target = ATT_MATVIEW;
    6538              23 :             break;
    6539             113 :         case RELKIND_INDEX:
    6540             113 :             actual_target = ATT_INDEX;
    6541 CBC         113 :             break;
    6542 GIC         207 :         case RELKIND_PARTITIONED_INDEX:
    6543 CBC         207 :             actual_target = ATT_PARTITIONED_INDEX;
    6544 GIC         207 :             break;
    6545 CBC         107 :         case RELKIND_COMPOSITE_TYPE:
    6546 GIC         107 :             actual_target = ATT_COMPOSITE_TYPE;
    6547 CBC         107 :             break;
    6548             432 :         case RELKIND_FOREIGN_TABLE:
    6549             432 :             actual_target = ATT_FOREIGN_TABLE;
    6550 GIC         432 :             break;
    6551               6 :         case RELKIND_SEQUENCE:
    6552               6 :             actual_target = ATT_SEQUENCE;
    6553               6 :             break;
    6554 UIC           0 :         default:
    6555 LBC           0 :             actual_target = 0;
    6556               0 :             break;
    6557                 :     }
    6558                 : 
    6559 ECB             :     /* Wrong target type? */
    6560 GBC       47080 :     if ((actual_target & allowed_targets) == 0)
    6561 EUB             :     {
    6562 GIC          21 :         const char *action_str = alter_table_type_to_string(cmdtype);
    6563                 : 
    6564              21 :         if (action_str)
    6565              21 :             ereport(ERROR,
    6566 ECB             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6567                 :             /* translator: %s is a group of some SQL keywords */
    6568                 :                      errmsg("ALTER action %s cannot be performed on relation \"%s\"",
    6569                 :                             action_str, RelationGetRelationName(rel)),
    6570                 :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
    6571                 :         else
    6572                 :             /* internal error? */
    6573 UIC           0 :             elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
    6574                 :                  RelationGetRelationName(rel));
    6575 ECB             :     }
    6576                 : 
    6577                 :     /* Permissions checks */
    6578 GNC       47059 :     if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
    6579 GIC           3 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
    6580               3 :                        RelationGetRelationName(rel));
    6581                 : 
    6582           47056 :     if (!allowSystemTableMods && IsSystemRelation(rel))
    6583 LBC           0 :         ereport(ERROR,
    6584 ECB             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    6585                 :                  errmsg("permission denied: \"%s\" is a system catalog",
    6586                 :                         RelationGetRelationName(rel))));
    6587 CBC       47056 : }
    6588                 : 
    6589 ECB             : /*
    6590                 :  * ATSimpleRecursion
    6591                 :  *
    6592                 :  * Simple table recursion sufficient for most ALTER TABLE operations.
    6593                 :  * All direct and indirect children are processed in an unspecified order.
    6594                 :  * Note that if a child inherits from the original table via multiple
    6595                 :  * inheritance paths, it will be visited just once.
    6596                 :  */
    6597                 : static void
    6598 CBC       28881 : ATSimpleRecursion(List **wqueue, Relation rel,
    6599                 :                   AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    6600                 :                   AlterTableUtilityContext *context)
    6601 ECB             : {
    6602                 :     /*
    6603                 :      * Propagate to children, if desired and if there are (or might be) any
    6604                 :      * children.
    6605                 :      */
    6606 CBC       28881 :     if (recurse && rel->rd_rel->relhassubclass)
    6607                 :     {
    6608              50 :         Oid         relid = RelationGetRelid(rel);
    6609                 :         ListCell   *child;
    6610 ECB             :         List       *children;
    6611                 : 
    6612 CBC          50 :         children = find_all_inheritors(relid, lockmode, NULL);
    6613                 : 
    6614                 :         /*
    6615                 :          * find_all_inheritors does the recursive search of the inheritance
    6616                 :          * hierarchy, so all we have to do is process all of the relids in the
    6617                 :          * list that it returns.
    6618 ECB             :          */
    6619 GIC         192 :         foreach(child, children)
    6620 ECB             :         {
    6621 GIC         142 :             Oid         childrelid = lfirst_oid(child);
    6622                 :             Relation    childrel;
    6623                 : 
    6624 CBC         142 :             if (childrelid == relid)
    6625 GIC          50 :                 continue;
    6626 ECB             :             /* find_all_inheritors already got lock */
    6627 CBC          92 :             childrel = relation_open(childrelid, NoLock);
    6628              92 :             CheckTableNotInUse(childrel, "ALTER TABLE");
    6629 GIC          92 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
    6630              92 :             relation_close(childrel, NoLock);
    6631                 :         }
    6632                 :     }
    6633           28881 : }
    6634                 : 
    6635 ECB             : /*
    6636                 :  * Obtain list of partitions of the given table, locking them all at the given
    6637                 :  * lockmode and ensuring that they all pass CheckTableNotInUse.
    6638                 :  *
    6639                 :  * This function is a no-op if the given relation is not a partitioned table;
    6640                 :  * in particular, nothing is done if it's a legacy inheritance parent.
    6641                 :  */
    6642                 : static void
    6643 CBC         264 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
    6644                 : {
    6645             264 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    6646                 :     {
    6647 ECB             :         List       *inh;
    6648                 :         ListCell   *cell;
    6649                 : 
    6650 GIC          61 :         inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
    6651 ECB             :         /* first element is the parent rel; must ignore it */
    6652 GIC         206 :         for_each_from(cell, inh, 1)
    6653 ECB             :         {
    6654                 :             Relation    childrel;
    6655 EUB             : 
    6656                 :             /* find_all_inheritors already got lock */
    6657 GBC         148 :             childrel = table_open(lfirst_oid(cell), NoLock);
    6658             148 :             CheckTableNotInUse(childrel, "ALTER TABLE");
    6659 GIC         145 :             table_close(childrel, NoLock);
    6660 EUB             :         }
    6661 CBC          58 :         list_free(inh);
    6662 ECB             :     }
    6663 CBC         261 : }
    6664 ECB             : 
    6665 EUB             : /*
    6666                 :  * ATTypedTableRecursion
    6667                 :  *
    6668                 :  * Propagate ALTER TYPE operations to the typed tables of that type.
    6669                 :  * Also check the RESTRICT/CASCADE behavior.  Given CASCADE, also permit
    6670                 :  * recursion to inheritance children of the typed tables.
    6671                 :  */
    6672                 : static void
    6673 CBC          95 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
    6674 ECB             :                       LOCKMODE lockmode, AlterTableUtilityContext *context)
    6675 EUB             : {
    6676                 :     ListCell   *child;
    6677                 :     List       *children;
    6678                 : 
    6679 GBC          95 :     Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    6680 EUB             : 
    6681 CBC          95 :     children = find_typed_table_dependencies(rel->rd_rel->reltype,
    6682              95 :                                              RelationGetRelationName(rel),
    6683 EUB             :                                              cmd->behavior);
    6684                 : 
    6685 GBC         101 :     foreach(child, children)
    6686 EUB             :     {
    6687 GIC          15 :         Oid         childrelid = lfirst_oid(child);
    6688                 :         Relation    childrel;
    6689                 : 
    6690 GBC          15 :         childrel = relation_open(childrelid, lockmode);
    6691 CBC          15 :         CheckTableNotInUse(childrel, "ALTER TABLE");
    6692              15 :         ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
    6693 GBC          15 :         relation_close(childrel, NoLock);
    6694 EUB             :     }
    6695 GBC          86 : }
    6696 EUB             : 
    6697                 : 
    6698                 : /*
    6699                 :  * find_composite_type_dependencies
    6700                 :  *
    6701                 :  * Check to see if the type "typeOid" is being used as a column in some table
    6702                 :  * (possibly nested several levels deep in composite types, arrays, etc!).
    6703                 :  * Eventually, we'd like to propagate the check or rewrite operation
    6704                 :  * into such tables, but for now, just error out if we find any.
    6705                 :  *
    6706                 :  * Caller should provide either the associated relation of a rowtype,
    6707                 :  * or a type name (not both) for use in the error message, if any.
    6708                 :  *
    6709                 :  * Note that "typeOid" is not necessarily a composite type; it could also be
    6710                 :  * another container type such as an array or range, or a domain over one of
    6711                 :  * these things.  The name of this function is therefore somewhat historical,
    6712                 :  * but it's not worth changing.
    6713                 :  *
    6714                 :  * We assume that functions and views depending on the type are not reasons
    6715                 :  * to reject the ALTER.  (How safe is this really?)
    6716                 :  */
    6717                 : void
    6718 GBC        1752 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
    6719 EUB             :                                  const char *origTypeName)
    6720                 : {
    6721                 :     Relation    depRel;
    6722                 :     ScanKeyData key[2];
    6723                 :     SysScanDesc depScan;
    6724                 :     HeapTuple   depTup;
    6725                 : 
    6726                 :     /* since this function recurses, it could be driven to stack overflow */
    6727 GBC        1752 :     check_stack_depth();
    6728 EUB             : 
    6729                 :     /*
    6730                 :      * We scan pg_depend to find those things that depend on the given type.
    6731                 :      * (We assume we can ignore refobjsubid for a type.)
    6732                 :      */
    6733 GBC        1752 :     depRel = table_open(DependRelationId, AccessShareLock);
    6734 EUB             : 
    6735 GBC        1752 :     ScanKeyInit(&key[0],
    6736 EUB             :                 Anum_pg_depend_refclassid,
    6737                 :                 BTEqualStrategyNumber, F_OIDEQ,
    6738                 :                 ObjectIdGetDatum(TypeRelationId));
    6739 GBC        1752 :     ScanKeyInit(&key[1],
    6740 EUB             :                 Anum_pg_depend_refobjid,
    6741                 :                 BTEqualStrategyNumber, F_OIDEQ,
    6742                 :                 ObjectIdGetDatum(typeOid));
    6743                 : 
    6744 GBC        1752 :     depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
    6745 EUB             :                                  NULL, 2, key);
    6746                 : 
    6747 GBC        2720 :     while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
    6748 EUB             :     {
    6749 GBC        1016 :         Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
    6750 EUB             :         Relation    rel;
    6751                 :         TupleDesc   tupleDesc;
    6752                 :         Form_pg_attribute att;
    6753                 : 
    6754                 :         /* Check for directly dependent types */
    6755 GBC        1016 :         if (pg_depend->classid == TypeRelationId)
    6756 EUB             :         {
    6757                 :             /*
    6758                 :              * This must be an array, domain, or range containing the given
    6759                 :              * type, so recursively check for uses of this type.  Note that
    6760                 :              * any error message will mention the original type not the
    6761                 :              * container; this is intentional.
    6762                 :              */
    6763 GBC         843 :             find_composite_type_dependencies(pg_depend->objid,
    6764 EUB             :                                              origRelation, origTypeName);
    6765 GBC         831 :             continue;
    6766 EUB             :         }
    6767                 : 
    6768                 :         /* Else, ignore dependees that aren't relations */
    6769 GBC         173 :         if (pg_depend->classid != RelationRelationId)
    6770              61 :             continue;
    6771 ECB             : 
    6772 CBC         112 :         rel = relation_open(pg_depend->objid, AccessShareLock);
    6773 GBC         112 :         tupleDesc = RelationGetDescr(rel);
    6774 EUB             : 
    6775                 :         /*
    6776                 :          * If objsubid identifies a specific column, refer to that in error
    6777                 :          * messages.  Otherwise, search to see if there's a user column of the
    6778                 :          * type.  (We assume system columns are never of interesting types.)
    6779                 :          * The search is needed because an index containing an expression
    6780                 :          * column of the target type will just be recorded as a whole-relation
    6781                 :          * dependency.  If we do not find a column of the type, the dependency
    6782                 :          * must indicate that the type is transiently referenced in an index
    6783                 :          * expression but not stored on disk, which we assume is OK, just as
    6784                 :          * we do for references in views.  (It could also be that the target
    6785                 :          * type is embedded in some container type that is stored in an index
    6786                 :          * column, but the previous recursion should catch such cases.)
    6787                 :          */
    6788 GIC         112 :         if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
    6789              33 :             att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
    6790                 :         else
    6791                 :         {
    6792              79 :             att = NULL;
    6793             203 :             for (int attno = 1; attno <= tupleDesc->natts; attno++)
    6794                 :             {
    6795             127 :                 att = TupleDescAttr(tupleDesc, attno - 1);
    6796 CBC         127 :                 if (att->atttypid == typeOid && !att->attisdropped)
    6797 GIC           3 :                     break;
    6798             124 :                 att = NULL;
    6799                 :             }
    6800 CBC          79 :             if (att == NULL)
    6801                 :             {
    6802 ECB             :                 /* No such column, so assume OK */
    6803 GIC          76 :                 relation_close(rel, AccessShareLock);
    6804 CBC          76 :                 continue;
    6805 ECB             :             }
    6806                 :         }
    6807                 : 
    6808                 :         /*
    6809                 :          * We definitely should reject if the relation has storage.  If it's
    6810                 :          * partitioned, then perhaps we don't have to reject: if there are
    6811                 :          * partitions then we'll fail when we find one, else there is no
    6812                 :          * stored data to worry about.  However, it's possible that the type
    6813                 :          * change would affect conclusions about whether the type is sortable
    6814                 :          * or hashable and thus (if it's a partitioning column) break the
    6815                 :          * partitioning rule.  For now, reject for partitioned rels too.
    6816                 :          */
    6817 CBC          36 :         if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
    6818 LBC           0 :             RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
    6819 ECB             :         {
    6820 CBC          36 :             if (origTypeName)
    6821              15 :                 ereport(ERROR,
    6822 ECB             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6823                 :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    6824                 :                                 origTypeName,
    6825                 :                                 RelationGetRelationName(rel),
    6826                 :                                 NameStr(att->attname))));
    6827 GBC          21 :             else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    6828               9 :                 ereport(ERROR,
    6829 EUB             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6830                 :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    6831                 :                                 RelationGetRelationName(origRelation),
    6832                 :                                 RelationGetRelationName(rel),
    6833 ECB             :                                 NameStr(att->attname))));
    6834 GIC          12 :             else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    6835 CBC           3 :                 ereport(ERROR,
    6836                 :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6837 ECB             :                          errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
    6838                 :                                 RelationGetRelationName(origRelation),
    6839                 :                                 RelationGetRelationName(rel),
    6840                 :                                 NameStr(att->attname))));
    6841                 :             else
    6842 GIC           9 :                 ereport(ERROR,
    6843                 :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6844                 :                          errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
    6845                 :                                 RelationGetRelationName(origRelation),
    6846 EUB             :                                 RelationGetRelationName(rel),
    6847                 :                                 NameStr(att->attname))));
    6848                 :         }
    6849 UIC           0 :         else if (OidIsValid(rel->rd_rel->reltype))
    6850                 :         {
    6851 ECB             :             /*
    6852                 :              * A view or composite type itself isn't a problem, but we must
    6853                 :              * recursively check for indirect dependencies via its rowtype.
    6854                 :              */
    6855 LBC           0 :             find_composite_type_dependencies(rel->rd_rel->reltype,
    6856 EUB             :                                              origRelation, origTypeName);
    6857                 :         }
    6858                 : 
    6859 UIC           0 :         relation_close(rel, AccessShareLock);
    6860 ECB             :     }
    6861                 : 
    6862 GIC        1704 :     systable_endscan(depScan);
    6863                 : 
    6864            1704 :     relation_close(depRel, AccessShareLock);
    6865            1704 : }
    6866                 : 
    6867                 : 
    6868                 : /*
    6869                 :  * find_typed_table_dependencies
    6870                 :  *
    6871 ECB             :  * Check to see if a composite type is being used as the type of a
    6872                 :  * typed table.  Abort if any are found and behavior is RESTRICT.
    6873                 :  * Else return the list of tables.
    6874                 :  */
    6875                 : static List *
    6876 GIC         107 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
    6877                 : {
    6878                 :     Relation    classRel;
    6879 ECB             :     ScanKeyData key[1];
    6880                 :     TableScanDesc scan;
    6881                 :     HeapTuple   tuple;
    6882 GIC         107 :     List       *result = NIL;
    6883                 : 
    6884             107 :     classRel = table_open(RelationRelationId, AccessShareLock);
    6885 ECB             : 
    6886 GIC         107 :     ScanKeyInit(&key[0],
    6887                 :                 Anum_pg_class_reloftype,
    6888                 :                 BTEqualStrategyNumber, F_OIDEQ,
    6889                 :                 ObjectIdGetDatum(typeOid));
    6890                 : 
    6891             107 :     scan = table_beginscan_catalog(classRel, 1, key);
    6892 ECB             : 
    6893 GIC         125 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    6894 ECB             :     {
    6895 GIC          30 :         Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
    6896                 : 
    6897 CBC          30 :         if (behavior == DROP_RESTRICT)
    6898              12 :             ereport(ERROR,
    6899                 :                     (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
    6900 ECB             :                      errmsg("cannot alter type \"%s\" because it is the type of a typed table",
    6901                 :                             typeName),
    6902                 :                      errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
    6903                 :         else
    6904 GIC          18 :             result = lappend_oid(result, classform->oid);
    6905                 :     }
    6906 ECB             : 
    6907 GIC          95 :     table_endscan(scan);
    6908              95 :     table_close(classRel, AccessShareLock);
    6909                 : 
    6910              95 :     return result;
    6911                 : }
    6912                 : 
    6913                 : 
    6914                 : /*
    6915                 :  * check_of_type
    6916 ECB             :  *
    6917                 :  * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF.  If it
    6918                 :  * isn't suitable, throw an error.  Currently, we require that the type
    6919                 :  * originated with CREATE TYPE AS.  We could support any row type, but doing so
    6920                 :  * would require handling a number of extra corner cases in the DDL commands.
    6921                 :  * (Also, allowing domain-over-composite would open up a can of worms about
    6922                 :  * whether and how the domain's constraints should apply to derived tables.)
    6923                 :  */
    6924                 : void
    6925 CBC          85 : check_of_type(HeapTuple typetuple)
    6926                 : {
    6927 GIC          85 :     Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
    6928              85 :     bool        typeOk = false;
    6929                 : 
    6930 CBC          85 :     if (typ->typtype == TYPTYPE_COMPOSITE)
    6931 ECB             :     {
    6932                 :         Relation    typeRelation;
    6933                 : 
    6934 CBC          85 :         Assert(OidIsValid(typ->typrelid));
    6935 GIC          85 :         typeRelation = relation_open(typ->typrelid, AccessShareLock);
    6936 CBC          85 :         typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    6937                 : 
    6938                 :         /*
    6939                 :          * Close the parent rel, but keep our AccessShareLock on it until xact
    6940                 :          * commit.  That will prevent someone else from deleting or ALTERing
    6941                 :          * the type before the typed table creation/conversion commits.
    6942                 :          */
    6943 GIC          85 :         relation_close(typeRelation, NoLock);
    6944                 :     }
    6945              85 :     if (!typeOk)
    6946 CBC           3 :         ereport(ERROR,
    6947                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6948                 :                  errmsg("type %s is not a composite type",
    6949                 :                         format_type_be(typ->oid))));
    6950 GIC          82 : }
    6951                 : 
    6952 ECB             : 
    6953                 : /*
    6954                 :  * ALTER TABLE ADD COLUMN
    6955                 :  *
    6956                 :  * Adds an additional attribute to a relation making the assumption that
    6957                 :  * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
    6958                 :  * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
    6959                 :  * AlterTableCmd's.
    6960                 :  *
    6961                 :  * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
    6962                 :  * have to decide at runtime whether to recurse or not depending on whether we
    6963                 :  * actually add a column or merely merge with an existing column.  (We can't
    6964                 :  * check this in a static pre-pass because it won't handle multiple inheritance
    6965                 :  * situations correctly.)
    6966                 :  */
    6967                 : static void
    6968 CBC         923 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    6969                 :                 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
    6970                 :                 AlterTableUtilityContext *context)
    6971                 : {
    6972 GIC         923 :     if (rel->rd_rel->reloftype && !recursing)
    6973               3 :         ereport(ERROR,
    6974                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6975                 :                  errmsg("cannot add column to typed table")));
    6976                 : 
    6977             920 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    6978              29 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    6979                 : 
    6980             917 :     if (recurse && !is_view)
    6981 GNC         867 :         cmd->recurse = true;
    6982 GIC         917 : }
    6983                 : 
    6984                 : /*
    6985                 :  * Add a column to a table.  The return value is the address of the
    6986                 :  * new column in the parent relation.
    6987                 :  *
    6988                 :  * cmd is pass-by-ref so that we can replace it with the parse-transformed
    6989                 :  * copy (but that happens only after we check for IF NOT EXISTS).
    6990                 :  */
    6991 ECB             : static ObjectAddress
    6992 GIC        1205 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
    6993                 :                 AlterTableCmd **cmd, bool recurse, bool recursing,
    6994                 :                 LOCKMODE lockmode, int cur_pass,
    6995                 :                 AlterTableUtilityContext *context)
    6996                 : {
    6997            1205 :     Oid         myrelid = RelationGetRelid(rel);
    6998            1205 :     ColumnDef  *colDef = castNode(ColumnDef, (*cmd)->def);
    6999 CBC        1205 :     bool        if_not_exists = (*cmd)->missing_ok;
    7000                 :     Relation    pgclass,
    7001                 :                 attrdesc;
    7002                 :     HeapTuple   reltup;
    7003                 :     FormData_pg_attribute attribute;
    7004                 :     int         newattnum;
    7005 ECB             :     char        relkind;
    7006                 :     HeapTuple   typeTuple;
    7007                 :     Oid         typeOid;
    7008                 :     int32       typmod;
    7009                 :     Oid         collOid;
    7010                 :     Form_pg_type tform;
    7011                 :     Expr       *defval;
    7012                 :     List       *children;
    7013                 :     ListCell   *child;
    7014                 :     AlterTableCmd *childcmd;
    7015                 :     AclResult   aclresult;
    7016                 :     ObjectAddress address;
    7017                 :     TupleDesc   tupdesc;
    7018 GIC        1205 :     FormData_pg_attribute *aattr[] = {&attribute};
    7019 ECB             : 
    7020                 :     /* At top level, permission check was done in ATPrepCmd, else do it */
    7021 CBC        1205 :     if (recursing)
    7022 GIC         291 :         ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    7023                 : 
    7024            1205 :     if (rel->rd_rel->relispartition && !recursing)
    7025               6 :         ereport(ERROR,
    7026                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7027 ECB             :                  errmsg("cannot add column to a partition")));
    7028                 : 
    7029 GIC        1199 :     attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
    7030                 : 
    7031                 :     /*
    7032                 :      * Are we adding the column to a recursion child?  If so, check whether to
    7033                 :      * merge with an existing definition for the column.  If we do merge, we
    7034                 :      * must not recurse.  Children will already have the column, and recursing
    7035 ECB             :      * into them would mess up attinhcount.
    7036                 :      */
    7037 CBC        1199 :     if (colDef->inhcount > 0)
    7038                 :     {
    7039                 :         HeapTuple   tuple;
    7040                 : 
    7041 ECB             :         /* Does child already have a column by this name? */
    7042 CBC         291 :         tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
    7043 GIC         291 :         if (HeapTupleIsValid(tuple))
    7044 ECB             :         {
    7045 CBC          12 :             Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    7046                 :             Oid         ctypeId;
    7047                 :             int32       ctypmod;
    7048                 :             Oid         ccollid;
    7049                 : 
    7050                 :             /* Child column must match on type, typmod, and collation */
    7051 GIC          12 :             typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
    7052              12 :             if (ctypeId != childatt->atttypid ||
    7053              12 :                 ctypmod != childatt->atttypmod)
    7054 UIC           0 :                 ereport(ERROR,
    7055                 :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    7056                 :                          errmsg("child table \"%s\" has different type for column \"%s\"",
    7057                 :                                 RelationGetRelationName(rel), colDef->colname)));
    7058 GIC          12 :             ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
    7059              12 :             if (ccollid != childatt->attcollation)
    7060 LBC           0 :                 ereport(ERROR,
    7061 ECB             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
    7062                 :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
    7063                 :                                 RelationGetRelationName(rel), colDef->colname),
    7064                 :                          errdetail("\"%s\" versus \"%s\"",
    7065                 :                                    get_collation_name(ccollid),
    7066                 :                                    get_collation_name(childatt->attcollation))));
    7067                 : 
    7068                 :             /* Bump the existing child att's inhcount */
    7069 CBC          12 :             childatt->attinhcount++;
    7070 GNC          12 :             if (childatt->attinhcount < 0)
    7071 UNC           0 :                 ereport(ERROR,
    7072                 :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    7073                 :                         errmsg("too many inheritance parents"));
    7074 CBC          12 :             CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
    7075                 : 
    7076              12 :             heap_freetuple(tuple);
    7077                 : 
    7078                 :             /* Inform the user about the merge */
    7079              12 :             ereport(NOTICE,
    7080 ECB             :                     (errmsg("merging definition of column \"%s\" for child \"%s\"",
    7081                 :                             colDef->colname, RelationGetRelationName(rel))));
    7082                 : 
    7083 GIC          12 :             table_close(attrdesc, RowExclusiveLock);
    7084              12 :             return InvalidObjectAddress;
    7085                 :         }
    7086                 :     }
    7087                 : 
    7088                 :     /* skip if the name already exists and if_not_exists is true */
    7089            1187 :     if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
    7090                 :     {
    7091              27 :         table_close(attrdesc, RowExclusiveLock);
    7092              27 :         return InvalidObjectAddress;
    7093 ECB             :     }
    7094 EUB             : 
    7095                 :     /*
    7096 ECB             :      * Okay, we need to add the column, so go ahead and do parse
    7097                 :      * transformation.  This can result in queueing up, or even immediately
    7098                 :      * executing, subsidiary operations (such as creation of unique indexes);
    7099                 :      * so we mustn't do it until we have made the if_not_exists check.
    7100                 :      *
    7101                 :      * When recursing, the command was already transformed and we needn't do
    7102                 :      * so again.  Also, if context isn't given we can't transform.  (That
    7103                 :      * currently happens only for AT_AddColumnToView; we expect that view.c
    7104                 :      * passed us a ColumnDef that doesn't need work.)
    7105                 :      */
    7106 GIC        1145 :     if (context != NULL && !recursing)
    7107                 :     {
    7108             854 :         *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
    7109                 :                                    cur_pass, context);
    7110 CBC         854 :         Assert(*cmd != NULL);
    7111             854 :         colDef = castNode(ColumnDef, (*cmd)->def);
    7112                 :     }
    7113                 : 
    7114                 :     /*
    7115                 :      * Cannot add identity column if table has children, because identity does
    7116                 :      * not inherit.  (Adding column and identity separately will work.)
    7117                 :      */
    7118            1145 :     if (colDef->identity &&
    7119 GIC          15 :         recurse &&
    7120              15 :         find_inheritance_children(myrelid, NoLock) != NIL)
    7121               3 :         ereport(ERROR,
    7122                 :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7123                 :                  errmsg("cannot recursively add identity column to table that has child tables")));
    7124                 : 
    7125 GBC        1142 :     pgclass = table_open(RelationRelationId, RowExclusiveLock);
    7126                 : 
    7127 GIC        1142 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    7128            1142 :     if (!HeapTupleIsValid(reltup))
    7129 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    7130 GIC        1142 :     relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
    7131 EUB             : 
    7132                 :     /* Determine the new attribute's number */
    7133 GIC        1142 :     newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
    7134            1142 :     if (newattnum > MaxHeapAttributeNumber)
    7135 UBC           0 :         ereport(ERROR,
    7136                 :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    7137                 :                  errmsg("tables can have at most %d columns",
    7138 ECB             :                         MaxHeapAttributeNumber)));
    7139                 : 
    7140 CBC        1142 :     typeTuple = typenameType(NULL, colDef->typeName, &typmod);
    7141            1142 :     tform = (Form_pg_type) GETSTRUCT(typeTuple);
    7142 GIC        1142 :     typeOid = tform->oid;
    7143                 : 
    7144 GNC        1142 :     aclresult = object_aclcheck(TypeRelationId, typeOid, GetUserId(), ACL_USAGE);
    7145 GIC        1142 :     if (aclresult != ACLCHECK_OK)
    7146               6 :         aclcheck_error_type(aclresult, typeOid);
    7147                 : 
    7148            1136 :     collOid = GetColumnDefCollation(NULL, colDef, typeOid);
    7149                 : 
    7150                 :     /* make sure datatype is legal for a column */
    7151            1136 :     CheckAttributeType(colDef->colname, typeOid, collOid,
    7152 CBC        1136 :                        list_make1_oid(rel->rd_rel->reltype),
    7153                 :                        0);
    7154                 : 
    7155                 :     /*
    7156                 :      * Construct new attribute's pg_attribute entry.  (Variable-length fields
    7157                 :      * are handled by InsertPgAttributeTuples().)
    7158 ECB             :      */
    7159 GIC        1121 :     attribute.attrelid = myrelid;
    7160 CBC        1121 :     namestrcpy(&(attribute.attname), colDef->colname);
    7161 GIC        1121 :     attribute.atttypid = typeOid;
    7162 CBC        1121 :     attribute.attstattarget = (newattnum > 0) ? -1 : 0;
    7163 GIC        1121 :     attribute.attlen = tform->typlen;
    7164            1121 :     attribute.attnum = newattnum;
    7165 GNC        1121 :     if (list_length(colDef->typeName->arrayBounds) > PG_INT16_MAX)
    7166 UNC           0 :         ereport(ERROR,
    7167                 :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    7168                 :                 errmsg("too many array dimensions"));
    7169 GIC        1121 :     attribute.attndims = list_length(colDef->typeName->arrayBounds);
    7170            1121 :     attribute.atttypmod = typmod;
    7171 CBC        1121 :     attribute.attbyval = tform->typbyval;
    7172 GIC        1121 :     attribute.attalign = tform->typalign;
    7173 GNC        1121 :     if (colDef->storage_name)
    7174 UNC           0 :         attribute.attstorage = GetAttributeStorage(typeOid, colDef->storage_name);
    7175                 :     else
    7176 GNC        1121 :         attribute.attstorage = tform->typstorage;
    7177 GIC        1121 :     attribute.attcompression = GetAttributeCompression(typeOid,
    7178 ECB             :                                                        colDef->compression);
    7179 GIC        1121 :     attribute.attnotnull = colDef->is_not_null;
    7180 CBC        1121 :     attribute.atthasdef = false;
    7181            1121 :     attribute.atthasmissing = false;
    7182 GIC        1121 :     attribute.attidentity = colDef->identity;
    7183            1121 :     attribute.attgenerated = colDef->generated;
    7184            1121 :     attribute.attisdropped = false;
    7185            1121 :     attribute.attislocal = colDef->is_local;
    7186            1121 :     attribute.attinhcount = colDef->inhcount;
    7187 CBC        1121 :     attribute.attcollation = collOid;
    7188                 : 
    7189 GIC        1121 :     ReleaseSysCache(typeTuple);
    7190 ECB             : 
    7191 CBC        1121 :     tupdesc = CreateTupleDesc(lengthof(aattr), (FormData_pg_attribute **) &aattr);
    7192                 : 
    7193            1121 :     InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
    7194                 : 
    7195 GIC        1121 :     table_close(attrdesc, RowExclusiveLock);
    7196                 : 
    7197                 :     /*
    7198                 :      * Update pg_class tuple as appropriate
    7199                 :      */
    7200            1121 :     ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
    7201                 : 
    7202            1121 :     CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
    7203                 : 
    7204            1121 :     heap_freetuple(reltup);
    7205                 : 
    7206                 :     /* Post creation hook for new attribute */
    7207            1121 :     InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
    7208 ECB             : 
    7209 GIC        1121 :     table_close(pgclass, RowExclusiveLock);
    7210 ECB             : 
    7211                 :     /* Make the attribute's catalog entry visible */
    7212 GIC        1121 :     CommandCounterIncrement();
    7213 ECB             : 
    7214                 :     /*
    7215                 :      * Store the DEFAULT, if any, in the catalogs
    7216                 :      */
    7217 CBC        1121 :     if (colDef->raw_default)
    7218 ECB             :     {
    7219                 :         RawColumnDefault *rawEnt;
    7220                 : 
    7221 GIC         334 :         rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    7222             334 :         rawEnt->attnum = attribute.attnum;
    7223             334 :         rawEnt->raw_default = copyObject(colDef->raw_default);
    7224                 : 
    7225                 :         /*
    7226 ECB             :          * Attempt to skip a complete table rewrite by storing the specified
    7227                 :          * DEFAULT value outside of the heap.  This may be disabled inside
    7228                 :          * AddRelationNewConstraints if the optimization cannot be applied.
    7229                 :          */
    7230 GIC         334 :         rawEnt->missingMode = (!colDef->generated);
    7231                 : 
    7232             334 :         rawEnt->generated = colDef->generated;
    7233 ECB             : 
    7234                 :         /*
    7235                 :          * This function is intended for CREATE TABLE, so it processes a
    7236                 :          * _list_ of defaults, but we just do one.
    7237                 :          */
    7238 GIC         334 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    7239                 :                                   false, true, false, NULL);
    7240                 : 
    7241                 :         /* Make the additional catalog changes visible */
    7242             328 :         CommandCounterIncrement();
    7243                 : 
    7244                 :         /*
    7245                 :          * Did the request for a missing value work? If not we'll have to do a
    7246                 :          * rewrite
    7247                 :          */
    7248             328 :         if (!rawEnt->missingMode)
    7249              48 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7250                 :     }
    7251 ECB             : 
    7252                 :     /*
    7253                 :      * Tell Phase 3 to fill in the default expression, if there is one.
    7254                 :      *
    7255                 :      * If there is no default, Phase 3 doesn't have to do anything, because
    7256                 :      * that effectively means that the default is NULL.  The heap tuple access
    7257                 :      * routines always check for attnum > # of attributes in tuple, and return
    7258                 :      * NULL if so, so without any modification of the tuple data we will get
    7259                 :      * the effect of NULL values in the new column.
    7260                 :      *
    7261                 :      * An exception occurs when the new column is of a domain type: the domain
    7262                 :      * might have a NOT NULL constraint, or a check constraint that indirectly
    7263                 :      * rejects nulls.  If there are any domain constraints then we construct
    7264                 :      * an explicit NULL default value that will be passed through
    7265                 :      * CoerceToDomain processing.  (This is a tad inefficient, since it causes
    7266                 :      * rewriting the table which we really don't have to do, but the present
    7267                 :      * design of domain processing doesn't offer any simple way of checking
    7268                 :      * the constraints more directly.)
    7269                 :      *
    7270                 :      * Note: we use build_column_default, and not just the cooked default
    7271                 :      * returned by AddRelationNewConstraints, so that the right thing happens
    7272                 :      * when a datatype's default applies.
    7273                 :      *
    7274                 :      * Note: it might seem that this should happen at the end of Phase 2, so
    7275                 :      * that the effects of subsequent subcommands can be taken into account.
    7276                 :      * It's intentional that we do it now, though.  The new column should be
    7277                 :      * filled according to what is said in the ADD COLUMN subcommand, so that
    7278                 :      * the effects are the same as if this subcommand had been run by itself
    7279                 :      * and the later subcommands had been issued in new ALTER TABLE commands.
    7280                 :      *
    7281                 :      * We can skip this entirely for relations without storage, since Phase 3
    7282                 :      * is certainly not going to touch them.  System attributes don't have
    7283                 :      * interesting defaults, either.
    7284                 :      */
    7285 GIC        1115 :     if (RELKIND_HAS_STORAGE(relkind) && attribute.attnum > 0)
    7286                 :     {
    7287                 :         /*
    7288                 :          * For an identity column, we can't use build_column_default(),
    7289                 :          * because the sequence ownership isn't set yet.  So do it manually.
    7290                 :          */
    7291             945 :         if (colDef->identity)
    7292                 :         {
    7293              12 :             NextValueExpr *nve = makeNode(NextValueExpr);
    7294                 : 
    7295              12 :             nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
    7296              12 :             nve->typeId = typeOid;
    7297                 : 
    7298              12 :             defval = (Expr *) nve;
    7299                 : 
    7300                 :             /* must do a rewrite for identity columns */
    7301 CBC          12 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7302                 :         }
    7303                 :         else
    7304             933 :             defval = (Expr *) build_column_default(rel, attribute.attnum);
    7305 ECB             : 
    7306 GIC         945 :         if (!defval && DomainHasConstraints(typeOid))
    7307 ECB             :         {
    7308                 :             Oid         baseTypeId;
    7309                 :             int32       baseTypeMod;
    7310                 :             Oid         baseTypeColl;
    7311                 : 
    7312 CBC           3 :             baseTypeMod = typmod;
    7313 GIC           3 :             baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
    7314               3 :             baseTypeColl = get_typcollation(baseTypeId);
    7315               3 :             defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
    7316               3 :             defval = (Expr *) coerce_to_target_type(NULL,
    7317                 :                                                     (Node *) defval,
    7318                 :                                                     baseTypeId,
    7319                 :                                                     typeOid,
    7320 ECB             :                                                     typmod,
    7321                 :                                                     COERCION_ASSIGNMENT,
    7322                 :                                                     COERCE_IMPLICIT_CAST,
    7323                 :                                                     -1);
    7324 GIC           3 :             if (defval == NULL) /* should not happen */
    7325 LBC           0 :                 elog(ERROR, "failed to coerce base type to domain");
    7326 ECB             :         }
    7327                 : 
    7328 CBC         945 :         if (defval)
    7329                 :         {
    7330                 :             NewColumnValue *newval;
    7331                 : 
    7332 GIC         286 :             newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
    7333             286 :             newval->attnum = attribute.attnum;
    7334 CBC         286 :             newval->expr = expression_planner(defval);
    7335             286 :             newval->is_generated = (colDef->generated != '\0');
    7336 ECB             : 
    7337 GBC         286 :             tab->newvals = lappend(tab->newvals, newval);
    7338                 :         }
    7339                 : 
    7340 GIC         945 :         if (DomainHasConstraints(typeOid))
    7341 CBC           6 :             tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7342 ECB             : 
    7343 GBC         945 :         if (!TupleDescAttr(rel->rd_att, attribute.attnum - 1)->atthasmissing)
    7344                 :         {
    7345                 :             /*
    7346                 :              * If the new column is NOT NULL, and there is no missing value,
    7347                 :              * tell Phase 3 it needs to check for NULLs.
    7348                 :              */
    7349 GIC         725 :             tab->verify_new_notnull |= colDef->is_not_null;
    7350                 :         }
    7351                 :     }
    7352 ECB             : 
    7353                 :     /*
    7354 EUB             :      * Add needed dependency entries for the new column.
    7355                 :      */
    7356 GIC        1115 :     add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
    7357 CBC        1115 :     add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
    7358                 : 
    7359 ECB             :     /*
    7360                 :      * Propagate to children as appropriate.  Unlike most other ALTER
    7361                 :      * routines, we have to do this one level of recursion at a time; we can't
    7362                 :      * use find_all_inheritors to do it in one pass.
    7363                 :      */
    7364                 :     children =
    7365 GIC        1115 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    7366 ECB             : 
    7367                 :     /*
    7368                 :      * If we are told not to recurse, there had better not be any child
    7369                 :      * tables; else the addition would put them out of step.
    7370                 :      */
    7371 GIC        1115 :     if (children && !recurse)
    7372 CBC           6 :         ereport(ERROR,
    7373                 :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7374 ECB             :                  errmsg("column must be added to child tables too")));
    7375                 : 
    7376                 :     /* Children should see column as singly inherited */
    7377 GIC        1109 :     if (!recursing)
    7378                 :     {
    7379             830 :         childcmd = copyObject(*cmd);
    7380             830 :         colDef = castNode(ColumnDef, childcmd->def);
    7381             830 :         colDef->inhcount = 1;
    7382             830 :         colDef->is_local = false;
    7383                 :     }
    7384                 :     else
    7385             279 :         childcmd = *cmd;        /* no need to copy again */
    7386                 : 
    7387            1400 :     foreach(child, children)
    7388                 :     {
    7389 CBC         291 :         Oid         childrelid = lfirst_oid(child);
    7390                 :         Relation    childrel;
    7391 ECB             :         AlteredTableInfo *childtab;
    7392                 : 
    7393                 :         /* find_inheritance_children already got lock */
    7394 CBC         291 :         childrel = table_open(childrelid, NoLock);
    7395 GIC         291 :         CheckTableNotInUse(childrel, "ALTER TABLE");
    7396                 : 
    7397                 :         /* Find or create work queue entry for this table */
    7398             291 :         childtab = ATGetQueueEntry(wqueue, childrel);
    7399                 : 
    7400                 :         /* Recurse to child; return value is ignored */
    7401 CBC         291 :         ATExecAddColumn(wqueue, childtab, childrel,
    7402 ECB             :                         &childcmd, recurse, true,
    7403                 :                         lockmode, cur_pass, context);
    7404                 : 
    7405 GIC         291 :         table_close(childrel, NoLock);
    7406                 :     }
    7407                 : 
    7408 CBC        1109 :     ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
    7409 GIC        1109 :     return address;
    7410 ECB             : }
    7411                 : 
    7412 EUB             : /*
    7413 ECB             :  * If a new or renamed column will collide with the name of an existing
    7414                 :  * column and if_not_exists is false then error out, else do nothing.
    7415                 :  */
    7416                 : static bool
    7417 CBC        1403 : check_for_column_name_collision(Relation rel, const char *colname,
    7418 EUB             :                                 bool if_not_exists)
    7419                 : {
    7420                 :     HeapTuple   attTuple;
    7421                 :     int         attnum;
    7422                 : 
    7423 ECB             :     /*
    7424                 :      * this test is deliberately not attisdropped-aware, since if one tries to
    7425                 :      * add a column matching a dropped column name, it's gonna fail anyway.
    7426                 :      */
    7427 CBC        1403 :     attTuple = SearchSysCache2(ATTNAME,
    7428 ECB             :                                ObjectIdGetDatum(RelationGetRelid(rel)),
    7429                 :                                PointerGetDatum(colname));
    7430 GIC        1403 :     if (!HeapTupleIsValid(attTuple))
    7431 CBC        1355 :         return true;
    7432                 : 
    7433 GIC          48 :     attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
    7434 CBC          48 :     ReleaseSysCache(attTuple);
    7435 ECB             : 
    7436                 :     /*
    7437                 :      * We throw a different error message for conflicts with system column
    7438                 :      * names, since they are normally not shown and the user might otherwise
    7439                 :      * be confused about the reason for the conflict.
    7440                 :      */
    7441 GIC          48 :     if (attnum <= 0)
    7442 CBC           6 :         ereport(ERROR,
    7443 ECB             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7444                 :                  errmsg("column name \"%s\" conflicts with a system column name",
    7445                 :                         colname)));
    7446                 :     else
    7447                 :     {
    7448 CBC          42 :         if (if_not_exists)
    7449 EUB             :         {
    7450 GIC          27 :             ereport(NOTICE,
    7451                 :                     (errcode(ERRCODE_DUPLICATE_COLUMN),
    7452 ECB             :                      errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
    7453                 :                             colname, RelationGetRelationName(rel))));
    7454 CBC          27 :             return false;
    7455 ECB             :         }
    7456                 : 
    7457 GBC          15 :         ereport(ERROR,
    7458                 :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7459 ECB             :                  errmsg("column \"%s\" of relation \"%s\" already exists",
    7460                 :                         colname, RelationGetRelationName(rel))));
    7461                 :     }
    7462                 : 
    7463                 :     return true;
    7464                 : }
    7465                 : 
    7466                 : /*
    7467                 :  * Install a column's dependency on its datatype.
    7468                 :  */
    7469                 : static void
    7470 CBC        1560 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
    7471                 : {
    7472 ECB             :     ObjectAddress myself,
    7473                 :                 referenced;
    7474                 : 
    7475 GIC        1560 :     myself.classId = RelationRelationId;
    7476 CBC        1560 :     myself.objectId = relid;
    7477 GIC        1560 :     myself.objectSubId = attnum;
    7478 CBC        1560 :     referenced.classId = TypeRelationId;
    7479 GIC        1560 :     referenced.objectId = typid;
    7480            1560 :     referenced.objectSubId = 0;
    7481            1560 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7482            1560 : }
    7483 ECB             : 
    7484                 : /*
    7485                 :  * Install a column's dependency on its collation.
    7486                 :  */
    7487                 : static void
    7488 GIC        1560 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
    7489                 : {
    7490 ECB             :     ObjectAddress myself,
    7491                 :                 referenced;
    7492                 : 
    7493                 :     /* We know the default collation is pinned, so don't bother recording it */
    7494 GIC        1560 :     if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
    7495 ECB             :     {
    7496 GIC           9 :         myself.classId = RelationRelationId;
    7497               9 :         myself.objectId = relid;
    7498               9 :         myself.objectSubId = attnum;
    7499               9 :         referenced.classId = CollationRelationId;
    7500 CBC           9 :         referenced.objectId = collid;
    7501 GIC           9 :         referenced.objectSubId = 0;
    7502               9 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7503                 :     }
    7504 CBC        1560 : }
    7505 ECB             : 
    7506                 : /*
    7507                 :  * ALTER TABLE ALTER COLUMN DROP NOT NULL
    7508                 :  *
    7509                 :  * Return the address of the modified column.  If the column was already
    7510                 :  * nullable, InvalidObjectAddress is returned.
    7511                 :  */
    7512                 : static ObjectAddress
    7513 GNC         107 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
    7514                 :                   LOCKMODE lockmode)
    7515                 : {
    7516                 :     HeapTuple   tuple;
    7517                 :     HeapTuple   conTup;
    7518                 :     Form_pg_attribute attTup;
    7519                 :     AttrNumber  attnum;
    7520                 :     Relation    attr_rel;
    7521                 :     ObjectAddress address;
    7522                 : 
    7523                 :     /*
    7524                 :      * lookup the attribute
    7525                 :      */
    7526 GIC         107 :     attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7527                 : 
    7528             107 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    7529             107 :     if (!HeapTupleIsValid(tuple))
    7530               9 :         ereport(ERROR,
    7531                 :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7532                 :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7533                 :                         colName, RelationGetRelationName(rel))));
    7534              98 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    7535              98 :     attnum = attTup->attnum;
    7536 GNC          98 :     ObjectAddressSubSet(address, RelationRelationId,
    7537                 :                         RelationGetRelid(rel), attnum);
    7538                 : 
    7539                 :     /* If the column is already nullable there's nothing to do. */
    7540              98 :     if (!attTup->attnotnull)
    7541                 :     {
    7542               3 :         table_close(attr_rel, RowExclusiveLock);
    7543               3 :         return InvalidObjectAddress;
    7544                 :     }
    7545                 : 
    7546                 :     /* Prevent them from altering a system attribute */
    7547 GIC          95 :     if (attnum <= 0)
    7548 UIC           0 :         ereport(ERROR,
    7549                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7550                 :                  errmsg("cannot alter system column \"%s\"",
    7551                 :                         colName)));
    7552                 : 
    7553 GIC          95 :     if (attTup->attidentity)
    7554               3 :         ereport(ERROR,
    7555 ECB             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    7556                 :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    7557                 :                         colName, RelationGetRelationName(rel))));
    7558                 : 
    7559                 :     /*
    7560                 :      * It's not OK to remove a constraint only for the parent and leave it in
    7561                 :      * the children, so disallow that.
    7562                 :      */
    7563 GNC          92 :     if (!recurse)
    7564                 :     {
    7565               6 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    7566 ECB             :         {
    7567                 :             PartitionDesc partdesc;
    7568                 : 
    7569 GNC           6 :             partdesc = RelationGetPartitionDesc(rel, true);
    7570                 : 
    7571               6 :             if (partdesc->nparts > 0)
    7572               3 :                 ereport(ERROR,
    7573                 :                         errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7574                 :                         errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
    7575                 :                         errhint("Do not specify the ONLY keyword."));
    7576                 :         }
    7577 UNC           0 :         else if (rel->rd_rel->relhassubclass &&
    7578               0 :                  find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
    7579                 :         {
    7580               0 :             ereport(ERROR,
    7581                 :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7582                 :                     errmsg("NOT NULL constraint on column \"%s\" must be removed in child tables too",
    7583                 :                            colName),
    7584                 :                     errhint("Do not specify the ONLY keyword."));
    7585                 :         }
    7586 ECB             :     }
    7587                 : 
    7588                 :     /*
    7589                 :      * If rel is partition, shouldn't drop NOT NULL if parent has the same.
    7590                 :      */
    7591 GIC          89 :     if (rel->rd_rel->relispartition)
    7592                 :     {
    7593 GNC           9 :         Oid         parentId = get_partition_parent(RelationGetRelid(rel), false);
    7594               9 :         Relation    parent = table_open(parentId, AccessShareLock);
    7595               9 :         TupleDesc   tupDesc = RelationGetDescr(parent);
    7596                 :         AttrNumber  parent_attnum;
    7597                 : 
    7598 GIC           9 :         parent_attnum = get_attnum(parentId, colName);
    7599               9 :         if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
    7600               9 :             ereport(ERROR,
    7601 ECB             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7602                 :                      errmsg("column \"%s\" is marked NOT NULL in parent table",
    7603                 :                             colName)));
    7604 UIC           0 :         table_close(parent, AccessShareLock);
    7605                 :     }
    7606                 : 
    7607                 :     /*
    7608                 :      * Find the constraint that makes this column NOT NULL.
    7609                 :      */
    7610 GNC          80 :     conTup = findNotNullConstraint(rel, colName);
    7611              80 :     if (conTup == NULL)
    7612                 :     {
    7613                 :         Bitmapset  *pkcols;
    7614                 : 
    7615                 :         /*
    7616                 :          * There's no NOT NULL constraint, so throw an error.  If the column
    7617                 :          * is in a primary key, we can throw a specific error.  Otherwise,
    7618                 :          * this is unexpected.
    7619                 :          */
    7620               3 :         pkcols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
    7621               3 :         if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
    7622                 :                           pkcols))
    7623               3 :             ereport(ERROR,
    7624                 :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7625                 :                     errmsg("column \"%s\" is in a primary key", colName));
    7626                 : 
    7627                 :         /* this shouldn't happen */
    7628 UNC           0 :         elog(ERROR, "no NOT NULL constraint found to drop");
    7629                 :     }
    7630                 : 
    7631 GNC          77 :     dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
    7632                 :                             false, NULL, lockmode);
    7633                 : 
    7634              62 :     heap_freetuple(conTup);
    7635 ECB             : 
    7636 CBC          62 :     table_close(attr_rel, RowExclusiveLock);
    7637 ECB             : 
    7638 CBC          62 :     return address;
    7639                 : }
    7640                 : 
    7641 ECB             : /*
    7642                 :  * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
    7643                 :  * to verify it; recurses to apply the same to children.
    7644                 :  *
    7645                 :  * When called to alter an existing table, 'wqueue' must be given so that we can
    7646                 :  * queue a check that existing tuples pass the constraint.  When called from
    7647                 :  * table creation, 'wqueue' should be passed as NULL.
    7648                 :  */
    7649                 : static void
    7650 GNC       32180 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool recurse,
    7651                 :                LOCKMODE lockmode)
    7652                 : {
    7653                 :     HeapTuple   tuple;
    7654                 :     Form_pg_attribute attForm;
    7655                 :     List       *children;
    7656                 :     ListCell   *lc;
    7657                 : 
    7658           32180 :     tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
    7659           32180 :     if (!HeapTupleIsValid(tuple))
    7660 UNC           0 :         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
    7661                 :              attnum, RelationGetRelid(rel));
    7662 GNC       32180 :     attForm = (Form_pg_attribute) GETSTRUCT(tuple);
    7663           32180 :     if (!attForm->attnotnull)
    7664                 :     {
    7665                 :         Relation    attr_rel;
    7666                 : 
    7667             556 :         attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7668                 : 
    7669             556 :         attForm->attnotnull = true;
    7670             556 :         CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    7671                 : 
    7672             556 :         table_close(attr_rel, RowExclusiveLock);
    7673                 : 
    7674                 :         /*
    7675                 :          * And set up for existing values to be checked, unless another constraint
    7676                 :          * already proves this.
    7677                 :          */
    7678             556 :         if (wqueue && !NotNullImpliedByRelConstraints(rel, attForm))
    7679                 :         {
    7680                 :             AlteredTableInfo *tab;
    7681                 : 
    7682             528 :             tab = ATGetQueueEntry(wqueue, rel);
    7683             528 :             tab->verify_new_notnull = true;
    7684                 :         }
    7685                 :     }
    7686                 : 
    7687                 :     /* if no recursion is desired, we're done */
    7688           32180 :     if (!recurse)
    7689 GIC       31839 :         return;
    7690                 : 
    7691 GNC         341 :     children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    7692             386 :     foreach(lc, children)
    7693 ECB             :     {
    7694 GNC          45 :         Oid         childrelid = lfirst_oid(lc);
    7695                 :         Relation    childrel;
    7696                 :         AttrNumber  childattno;
    7697                 : 
    7698                 :         /* find_inheritance_children already got lock */
    7699              45 :         childrel = table_open(childrelid, NoLock);
    7700              45 :         CheckTableNotInUse(childrel, "ALTER TABLE");
    7701                 : 
    7702              45 :         childattno = get_attnum(RelationGetRelid(childrel),
    7703              45 :                                 get_attname(RelationGetRelid(rel), attnum,
    7704                 :                                             false));
    7705              45 :         set_attnotnull(wqueue, childrel, childattno,
    7706                 :                        recurse, lockmode);
    7707              45 :         table_close(childrel, NoLock);
    7708 ECB             :     }
    7709                 : }
    7710                 : 
    7711                 : /*
    7712                 :  * ALTER TABLE ALTER COLUMN SET NOT NULL
    7713                 :  *
    7714                 :  * Add a NOT NULL constraint to a single table and its children.  Returns
    7715                 :  * the address of the constraint added to the parent relation, if one gets
    7716                 :  * added, or InvalidObjectAddress otherwise.
    7717                 :  *
    7718                 :  * We must recurse to child tables during execution, rather than using
    7719                 :  * ALTER TABLE's normal prep-time recursion.
    7720                 :  */
    7721                 : static ObjectAddress
    7722 GNC         221 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
    7723                 :                  bool recurse, bool recursing, List **readyRels,
    7724                 :                  LOCKMODE lockmode)
    7725                 : {
    7726                 :     HeapTuple   tuple;
    7727                 :     Relation    constr_rel;
    7728                 :     ScanKeyData skey;
    7729                 :     SysScanDesc conscan;
    7730                 :     AttrNumber  attnum;
    7731                 :     ObjectAddress address;
    7732                 :     Constraint *constraint;
    7733                 :     CookedConstraint *ccon;
    7734                 :     List       *cooked;
    7735             221 :     List       *ready = NIL;
    7736                 : 
    7737                 :     /*
    7738                 :      * In cases of multiple inheritance, we might visit the same child more
    7739                 :      * than once.  In the topmost call, set up a list that we fill with all
    7740                 :      * visited relations, to skip those.
    7741                 :      */
    7742             221 :     if (readyRels == NULL)
    7743             159 :         readyRels = &ready;
    7744             221 :     if (list_member_oid(*readyRels, RelationGetRelid(rel)))
    7745               3 :         return InvalidObjectAddress;
    7746             218 :     *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
    7747                 : 
    7748                 :     /* At top level, permission check was done in ATPrepCmd, else do it */
    7749             218 :     if (recursing)
    7750                 :     {
    7751              59 :         ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    7752              59 :         Assert(conName != NULL);
    7753                 :     }
    7754                 : 
    7755             218 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    7756             218 :     if (attnum == InvalidAttrNumber)
    7757 CBC           9 :         ereport(ERROR,
    7758 ECB             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7759                 :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7760                 :                         colName, RelationGetRelationName(rel))));
    7761                 : 
    7762                 :     /* Prevent them from altering a system attribute */
    7763 GIC         209 :     if (attnum <= 0)
    7764 UIC           0 :         ereport(ERROR,
    7765                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7766                 :                  errmsg("cannot alter system column \"%s\"",
    7767                 :                         colName)));
    7768 ECB             : 
    7769                 :     /* See if there's already a constraint */
    7770 GNC         209 :     constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
    7771             209 :     ScanKeyInit(&skey,
    7772                 :                 Anum_pg_constraint_conrelid,
    7773                 :                 BTEqualStrategyNumber, F_OIDEQ,
    7774                 :                 ObjectIdGetDatum(RelationGetRelid(rel)));
    7775             209 :     conscan = systable_beginscan(constr_rel, ConstraintRelidTypidNameIndexId, true,
    7776                 :                                  NULL, 1, &skey);
    7777                 : 
    7778             401 :     while (HeapTupleIsValid(tuple = systable_getnext(conscan)))
    7779                 :     {
    7780             202 :         Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
    7781             202 :         bool        changed = false;
    7782                 :         HeapTuple   copytup;
    7783                 : 
    7784             202 :         if (conForm->contype != CONSTRAINT_NOTNULL)
    7785              97 :             continue;
    7786                 : 
    7787             105 :         if (extractNotNullColumn(tuple) != attnum)
    7788              95 :             continue;
    7789                 : 
    7790              10 :         copytup = heap_copytuple(tuple);
    7791              10 :         conForm = (Form_pg_constraint) GETSTRUCT(copytup);
    7792 ECB             : 
    7793                 :         /*
    7794                 :          * If we find an appropriate constraint, we're almost done, but just
    7795                 :          * need to change some properties on it: if we're recursing, increment
    7796                 :          * coninhcount; if not, set conislocal if not already set.
    7797                 :          */
    7798 GNC          10 :         if (recursing)
    7799                 :         {
    7800               3 :             conForm->coninhcount++;
    7801               3 :             changed = true;
    7802                 :         }
    7803               7 :         else if (!conForm->conislocal)
    7804                 :         {
    7805 UNC           0 :             conForm->conislocal = true;
    7806               0 :             changed = true;
    7807                 :         }
    7808                 : 
    7809 GNC          10 :         if (changed)
    7810                 :         {
    7811               3 :             CatalogTupleUpdate(constr_rel, &copytup->t_self, copytup);
    7812               3 :             ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
    7813                 :         }
    7814                 : 
    7815              10 :         systable_endscan(conscan);
    7816              10 :         table_close(constr_rel, RowExclusiveLock);
    7817                 : 
    7818              10 :         if (changed)
    7819               3 :             return address;
    7820                 :         else
    7821               7 :             return InvalidObjectAddress;
    7822 ECB             :     }
    7823                 : 
    7824 GNC         199 :     systable_endscan(conscan);
    7825             199 :     table_close(constr_rel, RowExclusiveLock);
    7826                 : 
    7827                 :     /*
    7828                 :      * If we're asked not to recurse, and children exist, raise an error.
    7829                 :      */
    7830             208 :     if (!recurse &&
    7831               9 :         find_inheritance_children(RelationGetRelid(rel),
    7832                 :                                   NoLock) != NIL)
    7833                 :     {
    7834               6 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    7835               3 :             ereport(ERROR,
    7836                 :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7837                 :                     errmsg("cannot add constraint to only the partitioned table when partitions exist"),
    7838                 :                     errhint("Do not specify the ONLY keyword."));
    7839                 :         else
    7840               3 :             ereport(ERROR,
    7841                 :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7842                 :                     errmsg("cannot add constraint only to table with inheritance children"),
    7843                 :                     errhint("Do not specify the ONLY keyword."));
    7844                 :     }
    7845                 : 
    7846                 :     /*
    7847                 :      * No constraint exists; we must add one.  First determine a name to use,
    7848                 :      * if we haven't already.
    7849                 :      */
    7850             193 :     if (!recursing)
    7851                 :     {
    7852             137 :         Assert(conName == NULL);
    7853             137 :         conName = ChooseConstraintName(RelationGetRelationName(rel),
    7854                 :                                        colName, "not_null",
    7855             137 :                                        RelationGetNamespace(rel),
    7856                 :                                        NIL);
    7857                 :     }
    7858             193 :     constraint = makeNode(Constraint);
    7859             193 :     constraint->contype = CONSTR_NOTNULL;
    7860             193 :     constraint->conname = conName;
    7861             193 :     constraint->deferrable = false;
    7862             193 :     constraint->initdeferred = false;
    7863             193 :     constraint->location = -1;
    7864             193 :     constraint->colname = colName;
    7865             193 :     constraint->skip_validation = false;
    7866             193 :     constraint->initially_valid = true;
    7867                 : 
    7868                 :     /* and do it */
    7869             193 :     cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
    7870             193 :                                        false, !recursing, false, NULL);
    7871             193 :     ccon = linitial(cooked);
    7872             193 :     ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
    7873                 : 
    7874                 :     /*
    7875                 :      * Mark pg_attribute.attnotnull for the column. Tell that function not to
    7876                 :      * recurse, because we're going to do it here.
    7877                 :      */
    7878             193 :     set_attnotnull(wqueue, rel, attnum, false, lockmode);
    7879                 : 
    7880                 :     /*
    7881                 :      * Recurse to propagate the constraint to children that don't have one.
    7882                 :      */
    7883             193 :     if (recurse)
    7884                 :     {
    7885                 :         List       *children;
    7886                 :         ListCell   *lc;
    7887                 : 
    7888             190 :         children = find_inheritance_children(RelationGetRelid(rel),
    7889                 :                                              lockmode);
    7890                 : 
    7891             252 :         foreach(lc, children)
    7892                 :         {
    7893                 :             Relation    childrel;
    7894                 : 
    7895              62 :             childrel = table_open(lfirst_oid(lc), NoLock);
    7896                 : 
    7897              62 :             ATExecSetNotNull(wqueue, childrel,
    7898                 :                              conName, colName, recurse, true,
    7899                 :                              readyRels, lockmode);
    7900                 : 
    7901              62 :             table_close(childrel, NoLock);
    7902                 :         }
    7903                 :     }
    7904                 : 
    7905 GIC         193 :     return address;
    7906                 : }
    7907                 : 
    7908                 : /*
    7909                 :  * ALTER TABLE ALTER COLUMN SET ATTNOTNULL
    7910                 :  *
    7911                 :  * This doesn't exist in the grammar; it's used when creating a
    7912                 :  * primary key and the column is not already marked attnotnull.
    7913                 :  */
    7914                 : static void
    7915 GNC       28396 : ATExecSetAttNotNull(List **wqueue, Relation rel,
    7916                 :                     const char *colName, LOCKMODE lockmode)
    7917                 : {
    7918                 :     AttrNumber  attnum;
    7919                 : 
    7920           28396 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    7921           28396 :     if (attnum == InvalidAttrNumber)    /* XXX should not happen .. elog? */
    7922               9 :         ereport(ERROR,
    7923                 :                 errcode(ERRCODE_UNDEFINED_COLUMN),
    7924                 :                 errmsg("column \"%s\" of relation \"%s\" does not exist",
    7925                 :                        colName, RelationGetRelationName(rel)));
    7926                 : 
    7927           28387 :     set_attnotnull(wqueue, rel, attnum, false, lockmode);
    7928           28387 : }
    7929                 : 
    7930                 : /*
    7931 ECB             :  * ALTER TABLE ALTER COLUMN CHECK NOT NULL
    7932                 :  *
    7933                 :  * This doesn't exist in the grammar, but we generate AT_CheckNotNull
    7934                 :  * commands against the partitions of a partitioned table if the user
    7935                 :  * writes ALTER TABLE ONLY ... SET NOT NULL on the partitioned table,
    7936                 :  * or tries to create a primary key on it (which internally creates
    7937                 :  * AT_SetNotNull on the partitioned table).   Such a command doesn't
    7938                 :  * allow us to actually modify any partition, but we want to let it
    7939                 :  * go through if the partitions are already properly marked.
    7940                 :  *
    7941                 :  * In future, this might need to adjust the child table's state, likely
    7942                 :  * by incrementing an inheritance count for the attnotnull constraint.
    7943                 :  * For now we need only check for the presence of the flag.
    7944                 :  */
    7945                 : static void
    7946 UIC           0 : ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
    7947 ECB             :                    const char *colName, LOCKMODE lockmode)
    7948                 : {
    7949                 :     HeapTuple   tuple;
    7950                 : 
    7951 UIC           0 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    7952 ECB             : 
    7953 UBC           0 :     if (!HeapTupleIsValid(tuple))
    7954 UIC           0 :         ereport(ERROR,
    7955                 :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7956                 :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7957                 :                         colName, RelationGetRelationName(rel))));
    7958 ECB             : 
    7959 LBC           0 :     if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
    7960 UIC           0 :         ereport(ERROR,
    7961                 :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7962                 :                  errmsg("constraint must be added to child tables too"),
    7963                 :                  errdetail("Column \"%s\" of relation \"%s\" is not already NOT NULL.",
    7964                 :                            colName, RelationGetRelationName(rel)),
    7965                 :                  errhint("Do not specify the ONLY keyword.")));
    7966                 : 
    7967               0 :     ReleaseSysCache(tuple);
    7968 LBC           0 : }
    7969                 : 
    7970 ECB             : /*
    7971                 :  * NotNullImpliedByRelConstraints
    7972                 :  *      Does rel's existing constraints imply NOT NULL for the given attribute?
    7973                 :  */
    7974                 : static bool
    7975 GIC         553 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
    7976 ECB             : {
    7977 CBC         553 :     NullTest   *nnulltest = makeNode(NullTest);
    7978                 : 
    7979 GIC        1106 :     nnulltest->arg = (Expr *) makeVar(1,
    7980             553 :                                       attr->attnum,
    7981                 :                                       attr->atttypid,
    7982 EUB             :                                       attr->atttypmod,
    7983                 :                                       attr->attcollation,
    7984                 :                                       0);
    7985 GBC         553 :     nnulltest->nulltesttype = IS_NOT_NULL;
    7986                 : 
    7987                 :     /*
    7988                 :      * argisrow = false is correct even for a composite column, because
    7989                 :      * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
    7990                 :      * case, just IS DISTINCT FROM NULL.
    7991                 :      */
    7992 GIC         553 :     nnulltest->argisrow = false;
    7993             553 :     nnulltest->location = -1;
    7994                 : 
    7995             553 :     if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
    7996 ECB             :     {
    7997 GIC          25 :         ereport(DEBUG1,
    7998 ECB             :                 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
    7999                 :                                  RelationGetRelationName(rel), NameStr(attr->attname))));
    8000 CBC          25 :         return true;
    8001                 :     }
    8002                 : 
    8003             528 :     return false;
    8004 ECB             : }
    8005                 : 
    8006                 : /*
    8007                 :  * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
    8008                 :  *
    8009 EUB             :  * Return the address of the affected column.
    8010                 :  */
    8011                 : static ObjectAddress
    8012 GIC         266 : ATExecColumnDefault(Relation rel, const char *colName,
    8013                 :                     Node *newDefault, LOCKMODE lockmode)
    8014                 : {
    8015 CBC         266 :     TupleDesc   tupdesc = RelationGetDescr(rel);
    8016 ECB             :     AttrNumber  attnum;
    8017                 :     ObjectAddress address;
    8018                 : 
    8019                 :     /*
    8020                 :      * get the number of the attribute
    8021                 :      */
    8022 GIC         266 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    8023             266 :     if (attnum == InvalidAttrNumber)
    8024              15 :         ereport(ERROR,
    8025 ECB             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8026                 :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8027                 :                         colName, RelationGetRelationName(rel))));
    8028                 : 
    8029                 :     /* Prevent them from altering a system attribute */
    8030 GIC         251 :     if (attnum <= 0)
    8031 UIC           0 :         ereport(ERROR,
    8032                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8033 EUB             :                  errmsg("cannot alter system column \"%s\"",
    8034                 :                         colName)));
    8035                 : 
    8036 CBC         251 :     if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
    8037 GIC           3 :         ereport(ERROR,
    8038                 :                 (errcode(ERRCODE_SYNTAX_ERROR),
    8039 ECB             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    8040                 :                         colName, RelationGetRelationName(rel)),
    8041                 :                  newDefault ? 0 : errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.")));
    8042                 : 
    8043 CBC         248 :     if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
    8044 GIC           3 :         ereport(ERROR,
    8045                 :                 (errcode(ERRCODE_SYNTAX_ERROR),
    8046                 :                  errmsg("column \"%s\" of relation \"%s\" is a generated column",
    8047                 :                         colName, RelationGetRelationName(rel)),
    8048                 :                  newDefault || TupleDescAttr(tupdesc, attnum - 1)->attgenerated != ATTRIBUTE_GENERATED_STORED ? 0 :
    8049                 :                  errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION instead.")));
    8050                 : 
    8051                 :     /*
    8052                 :      * Remove any old default for the column.  We use RESTRICT here for
    8053                 :      * safety, but at present we do not expect anything to depend on the
    8054                 :      * default.
    8055 ECB             :      *
    8056                 :      * We treat removing the existing default as an internal operation when it
    8057                 :      * is preparatory to adding a new default, but as a user-initiated
    8058                 :      * operation when the user asked for a drop.
    8059                 :      */
    8060 GIC         245 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    8061                 :                       newDefault != NULL);
    8062                 : 
    8063 CBC         245 :     if (newDefault)
    8064 ECB             :     {
    8065 EUB             :         /* SET DEFAULT */
    8066                 :         RawColumnDefault *rawEnt;
    8067 ECB             : 
    8068 CBC         158 :         rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
    8069 GIC         158 :         rawEnt->attnum = attnum;
    8070             158 :         rawEnt->raw_default = newDefault;
    8071             158 :         rawEnt->missingMode = false;
    8072 CBC         158 :         rawEnt->generated = '\0';
    8073                 : 
    8074 ECB             :         /*
    8075                 :          * This function is intended for CREATE TABLE, so it processes a
    8076                 :          * _list_ of defaults, but we just do one.
    8077                 :          */
    8078 GIC         158 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    8079                 :                                   false, true, false, NULL);
    8080                 :     }
    8081                 : 
    8082             242 :     ObjectAddressSubSet(address, RelationRelationId,
    8083 ECB             :                         RelationGetRelid(rel), attnum);
    8084 GIC         242 :     return address;
    8085                 : }
    8086                 : 
    8087 ECB             : /*
    8088                 :  * Add a pre-cooked default expression.
    8089                 :  *
    8090                 :  * Return the address of the affected column.
    8091                 :  */
    8092                 : static ObjectAddress
    8093 CBC          28 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
    8094 ECB             :                           Node *newDefault)
    8095                 : {
    8096                 :     ObjectAddress address;
    8097                 : 
    8098                 :     /* We assume no checking is required */
    8099                 : 
    8100                 :     /*
    8101                 :      * Remove any old default for the column.  We use RESTRICT here for
    8102                 :      * safety, but at present we do not expect anything to depend on the
    8103                 :      * default.  (In ordinary cases, there could not be a default in place
    8104                 :      * anyway, but it's possible when combining LIKE with inheritance.)
    8105                 :      */
    8106 GIC          28 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    8107 ECB             :                       true);
    8108                 : 
    8109 GIC          28 :     (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
    8110 ECB             : 
    8111 GIC          28 :     ObjectAddressSubSet(address, RelationRelationId,
    8112 ECB             :                         RelationGetRelid(rel), attnum);
    8113 GIC          28 :     return address;
    8114                 : }
    8115                 : 
    8116                 : /*
    8117                 :  * ALTER TABLE ALTER COLUMN ADD IDENTITY
    8118                 :  *
    8119                 :  * Return the address of the affected column.
    8120                 :  */
    8121                 : static ObjectAddress
    8122              48 : ATExecAddIdentity(Relation rel, const char *colName,
    8123                 :                   Node *def, LOCKMODE lockmode)
    8124                 : {
    8125                 :     Relation    attrelation;
    8126                 :     HeapTuple   tuple;
    8127 ECB             :     Form_pg_attribute attTup;
    8128                 :     AttrNumber  attnum;
    8129                 :     ObjectAddress address;
    8130 GIC          48 :     ColumnDef  *cdef = castNode(ColumnDef, def);
    8131                 : 
    8132              48 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8133                 : 
    8134              48 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8135              48 :     if (!HeapTupleIsValid(tuple))
    8136 UIC           0 :         ereport(ERROR,
    8137                 :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8138                 :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8139                 :                         colName, RelationGetRelationName(rel))));
    8140 CBC          48 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8141 GIC          48 :     attnum = attTup->attnum;
    8142                 : 
    8143                 :     /* Can't alter a system attribute */
    8144              48 :     if (attnum <= 0)
    8145 UIC           0 :         ereport(ERROR,
    8146                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8147 ECB             :                  errmsg("cannot alter system column \"%s\"",
    8148                 :                         colName)));
    8149                 : 
    8150                 :     /*
    8151                 :      * Creating a column as identity implies NOT NULL, so adding the identity
    8152                 :      * to an existing column that is not NOT NULL would create a state that
    8153                 :      * cannot be reproduced without contortions.
    8154                 :      */
    8155 GIC          48 :     if (!attTup->attnotnull)
    8156 CBC           3 :         ereport(ERROR,
    8157 ECB             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8158                 :                  errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
    8159                 :                         colName, RelationGetRelationName(rel))));
    8160                 : 
    8161 CBC          45 :     if (attTup->attidentity)
    8162               6 :         ereport(ERROR,
    8163                 :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8164                 :                  errmsg("column \"%s\" of relation \"%s\" is already an identity column",
    8165                 :                         colName, RelationGetRelationName(rel))));
    8166                 : 
    8167 GIC          39 :     if (attTup->atthasdef)
    8168 CBC           3 :         ereport(ERROR,
    8169 EUB             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8170                 :                  errmsg("column \"%s\" of relation \"%s\" already has a default value",
    8171                 :                         colName, RelationGetRelationName(rel))));
    8172                 : 
    8173 GIC          36 :     attTup->attidentity = cdef->identity;
    8174              36 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8175 ECB             : 
    8176 CBC          36 :     InvokeObjectPostAlterHook(RelationRelationId,
    8177                 :                               RelationGetRelid(rel),
    8178                 :                               attTup->attnum);
    8179 GIC          36 :     ObjectAddressSubSet(address, RelationRelationId,
    8180 ECB             :                         RelationGetRelid(rel), attnum);
    8181 GIC          36 :     heap_freetuple(tuple);
    8182                 : 
    8183 CBC          36 :     table_close(attrelation, RowExclusiveLock);
    8184                 : 
    8185              36 :     return address;
    8186 ECB             : }
    8187                 : 
    8188                 : /*
    8189                 :  * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
    8190                 :  *
    8191                 :  * Return the address of the affected column.
    8192                 :  */
    8193                 : static ObjectAddress
    8194 GIC          19 : ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
    8195 ECB             : {
    8196                 :     ListCell   *option;
    8197 GIC          19 :     DefElem    *generatedEl = NULL;
    8198                 :     HeapTuple   tuple;
    8199                 :     Form_pg_attribute attTup;
    8200                 :     AttrNumber  attnum;
    8201                 :     Relation    attrelation;
    8202                 :     ObjectAddress address;
    8203 ECB             : 
    8204 GIC          32 :     foreach(option, castNode(List, def))
    8205 ECB             :     {
    8206 CBC          13 :         DefElem    *defel = lfirst_node(DefElem, option);
    8207                 : 
    8208              13 :         if (strcmp(defel->defname, "generated") == 0)
    8209                 :         {
    8210 GBC          13 :             if (generatedEl)
    8211 UBC           0 :                 ereport(ERROR,
    8212                 :                         (errcode(ERRCODE_SYNTAX_ERROR),
    8213                 :                          errmsg("conflicting or redundant options")));
    8214 CBC          13 :             generatedEl = defel;
    8215                 :         }
    8216 ECB             :         else
    8217 LBC           0 :             elog(ERROR, "option \"%s\" not recognized",
    8218                 :                  defel->defname);
    8219                 :     }
    8220 ECB             : 
    8221                 :     /*
    8222                 :      * Even if there is nothing to change here, we run all the checks.  There
    8223                 :      * will be a subsequent ALTER SEQUENCE that relies on everything being
    8224                 :      * there.
    8225                 :      */
    8226                 : 
    8227 GIC          19 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8228              19 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8229 CBC          19 :     if (!HeapTupleIsValid(tuple))
    8230 LBC           0 :         ereport(ERROR,
    8231                 :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8232                 :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8233                 :                         colName, RelationGetRelationName(rel))));
    8234                 : 
    8235 CBC          19 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8236              19 :     attnum = attTup->attnum;
    8237                 : 
    8238 GIC          19 :     if (attnum <= 0)
    8239 LBC           0 :         ereport(ERROR,
    8240 ECB             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8241                 :                  errmsg("cannot alter system column \"%s\"",
    8242                 :                         colName)));
    8243                 : 
    8244 GIC          19 :     if (!attTup->attidentity)
    8245 CBC           3 :         ereport(ERROR,
    8246                 :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8247                 :                  errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8248                 :                         colName, RelationGetRelationName(rel))));
    8249                 : 
    8250 GIC          16 :     if (generatedEl)
    8251                 :     {
    8252              13 :         attTup->attidentity = defGetInt32(generatedEl);
    8253              13 :         CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8254                 : 
    8255 CBC          13 :         InvokeObjectPostAlterHook(RelationRelationId,
    8256                 :                                   RelationGetRelid(rel),
    8257 ECB             :                                   attTup->attnum);
    8258 CBC          13 :         ObjectAddressSubSet(address, RelationRelationId,
    8259                 :                             RelationGetRelid(rel), attnum);
    8260 ECB             :     }
    8261                 :     else
    8262 GIC           3 :         address = InvalidObjectAddress;
    8263 ECB             : 
    8264 CBC          16 :     heap_freetuple(tuple);
    8265              16 :     table_close(attrelation, RowExclusiveLock);
    8266 ECB             : 
    8267 CBC          16 :     return address;
    8268 ECB             : }
    8269                 : 
    8270                 : /*
    8271                 :  * ALTER TABLE ALTER COLUMN DROP IDENTITY
    8272                 :  *
    8273                 :  * Return the address of the affected column.
    8274                 :  */
    8275                 : static ObjectAddress
    8276 CBC          19 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
    8277 ECB             : {
    8278                 :     HeapTuple   tuple;
    8279                 :     Form_pg_attribute attTup;
    8280                 :     AttrNumber  attnum;
    8281                 :     Relation    attrelation;
    8282                 :     ObjectAddress address;
    8283                 :     Oid         seqid;
    8284                 :     ObjectAddress seqaddress;
    8285                 : 
    8286 GIC          19 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8287              19 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8288 CBC          19 :     if (!HeapTupleIsValid(tuple))
    8289 UIC           0 :         ereport(ERROR,
    8290                 :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8291                 :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8292                 :                         colName, RelationGetRelationName(rel))));
    8293 ECB             : 
    8294 GIC          19 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8295              19 :     attnum = attTup->attnum;
    8296 ECB             : 
    8297 GIC          19 :     if (attnum <= 0)
    8298 UIC           0 :         ereport(ERROR,
    8299                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8300 ECB             :                  errmsg("cannot alter system column \"%s\"",
    8301                 :                         colName)));
    8302                 : 
    8303 GIC          19 :     if (!attTup->attidentity)
    8304                 :     {
    8305               6 :         if (!missing_ok)
    8306 CBC           3 :             ereport(ERROR,
    8307                 :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8308                 :                      errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8309                 :                             colName, RelationGetRelationName(rel))));
    8310 ECB             :         else
    8311                 :         {
    8312 GIC           3 :             ereport(NOTICE,
    8313                 :                     (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
    8314                 :                             colName, RelationGetRelationName(rel))));
    8315               3 :             heap_freetuple(tuple);
    8316               3 :             table_close(attrelation, RowExclusiveLock);
    8317               3 :             return InvalidObjectAddress;
    8318                 :         }
    8319                 :     }
    8320 ECB             : 
    8321 GIC          13 :     attTup->attidentity = '\0';
    8322              13 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8323                 : 
    8324              13 :     InvokeObjectPostAlterHook(RelationRelationId,
    8325 ECB             :                               RelationGetRelid(rel),
    8326                 :                               attTup->attnum);
    8327 CBC          13 :     ObjectAddressSubSet(address, RelationRelationId,
    8328                 :                         RelationGetRelid(rel), attnum);
    8329 GIC          13 :     heap_freetuple(tuple);
    8330                 : 
    8331              13 :     table_close(attrelation, RowExclusiveLock);
    8332 ECB             : 
    8333                 :     /* drop the internal sequence */
    8334 GIC          13 :     seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
    8335              13 :     deleteDependencyRecordsForClass(RelationRelationId, seqid,
    8336                 :                                     RelationRelationId, DEPENDENCY_INTERNAL);
    8337              13 :     CommandCounterIncrement();
    8338              13 :     seqaddress.classId = RelationRelationId;
    8339              13 :     seqaddress.objectId = seqid;
    8340              13 :     seqaddress.objectSubId = 0;
    8341              13 :     performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
    8342                 : 
    8343              13 :     return address;
    8344                 : }
    8345                 : 
    8346                 : /*
    8347                 :  * ALTER TABLE ALTER COLUMN DROP EXPRESSION
    8348                 :  */
    8349                 : static void
    8350              22 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
    8351 EUB             : {
    8352                 :     /*
    8353                 :      * Reject ONLY if there are child tables.  We could implement this, but it
    8354                 :      * is a bit complicated.  GENERATED clauses must be attached to the column
    8355                 :      * definition and cannot be added later like DEFAULT, so if a child table
    8356                 :      * has a generation expression that the parent does not have, the child
    8357                 :      * column will necessarily be an attlocal column.  So to implement ONLY
    8358                 :      * here, we'd need extra code to update attislocal of the direct child
    8359                 :      * tables, somewhat similar to how DROP COLUMN does it, so that the
    8360                 :      * resulting state can be properly dumped and restored.
    8361                 :      */
    8362 GIC          28 :     if (!recurse &&
    8363               6 :         find_inheritance_children(RelationGetRelid(rel), lockmode))
    8364 GBC           3 :         ereport(ERROR,
    8365 EUB             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8366                 :                  errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
    8367                 : 
    8368                 :     /*
    8369                 :      * Cannot drop generation expression from inherited columns.
    8370                 :      */
    8371 GIC          19 :     if (!recursing)
    8372 EUB             :     {
    8373                 :         HeapTuple   tuple;
    8374                 :         Form_pg_attribute attTup;
    8375                 : 
    8376 GIC          16 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
    8377              16 :         if (!HeapTupleIsValid(tuple))
    8378 UIC           0 :             ereport(ERROR,
    8379                 :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8380 ECB             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    8381                 :                             cmd->name, RelationGetRelationName(rel))));
    8382                 : 
    8383 GIC          16 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8384 ECB             : 
    8385 CBC          16 :         if (attTup->attinhcount > 0)
    8386 GIC           3 :             ereport(ERROR,
    8387                 :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8388                 :                      errmsg("cannot drop generation expression from inherited column")));
    8389                 :     }
    8390 CBC          16 : }
    8391                 : 
    8392                 : /*
    8393                 :  * Return the address of the affected column.
    8394                 :  */
    8395                 : static ObjectAddress
    8396 GIC          16 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
    8397 ECB             : {
    8398                 :     HeapTuple   tuple;
    8399                 :     Form_pg_attribute attTup;
    8400                 :     AttrNumber  attnum;
    8401                 :     Relation    attrelation;
    8402                 :     Oid         attrdefoid;
    8403                 :     ObjectAddress address;
    8404                 : 
    8405 CBC          16 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8406 GIC          16 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8407              16 :     if (!HeapTupleIsValid(tuple))
    8408 LBC           0 :         ereport(ERROR,
    8409                 :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8410                 :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8411                 :                         colName, RelationGetRelationName(rel))));
    8412                 : 
    8413 GIC          16 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8414              16 :     attnum = attTup->attnum;
    8415                 : 
    8416              16 :     if (attnum <= 0)
    8417 LBC           0 :         ereport(ERROR,
    8418                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8419                 :                  errmsg("cannot alter system column \"%s\"",
    8420 ECB             :                         colName)));
    8421                 : 
    8422 GIC          16 :     if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
    8423                 :     {
    8424               6 :         if (!missing_ok)
    8425               3 :             ereport(ERROR,
    8426                 :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8427 ECB             :                      errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
    8428                 :                             colName, RelationGetRelationName(rel))));
    8429                 :         else
    8430                 :         {
    8431 GIC           3 :             ereport(NOTICE,
    8432                 :                     (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
    8433                 :                             colName, RelationGetRelationName(rel))));
    8434               3 :             heap_freetuple(tuple);
    8435 CBC           3 :             table_close(attrelation, RowExclusiveLock);
    8436 GBC           3 :             return InvalidObjectAddress;
    8437                 :         }
    8438                 :     }
    8439                 : 
    8440                 :     /*
    8441 ECB             :      * Mark the column as no longer generated.  (The atthasdef flag needs to
    8442                 :      * get cleared too, but RemoveAttrDefault will handle that.)
    8443                 :      */
    8444 GIC          10 :     attTup->attgenerated = '\0';
    8445              10 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8446                 : 
    8447              10 :     InvokeObjectPostAlterHook(RelationRelationId,
    8448 ECB             :                               RelationGetRelid(rel),
    8449                 :                               attnum);
    8450 GIC          10 :     heap_freetuple(tuple);
    8451                 : 
    8452              10 :     table_close(attrelation, RowExclusiveLock);
    8453                 : 
    8454                 :     /*
    8455                 :      * Drop the dependency records of the GENERATED expression, in particular
    8456                 :      * its INTERNAL dependency on the column, which would otherwise cause
    8457                 :      * dependency.c to refuse to perform the deletion.
    8458                 :      */
    8459              10 :     attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    8460              10 :     if (!OidIsValid(attrdefoid))
    8461 UIC           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    8462                 :              RelationGetRelid(rel), attnum);
    8463 GIC          10 :     (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    8464                 : 
    8465 ECB             :     /* Make above changes visible */
    8466 GIC          10 :     CommandCounterIncrement();
    8467                 : 
    8468 ECB             :     /*
    8469                 :      * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    8470                 :      * safety, but at present we do not expect anything to depend on the
    8471                 :      * default.
    8472                 :      */
    8473 CBC          10 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    8474 ECB             :                       false, false);
    8475                 : 
    8476 CBC          10 :     ObjectAddressSubSet(address, RelationRelationId,
    8477 ECB             :                         RelationGetRelid(rel), attnum);
    8478 GIC          10 :     return address;
    8479                 : }
    8480                 : 
    8481                 : /*
    8482                 :  * ALTER TABLE ALTER COLUMN SET STATISTICS
    8483 ECB             :  *
    8484                 :  * Return value is the address of the modified column
    8485                 :  */
    8486                 : static ObjectAddress
    8487 CBC          82 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
    8488                 : {
    8489 ECB             :     int         newtarget;
    8490                 :     Relation    attrelation;
    8491                 :     HeapTuple   tuple;
    8492                 :     Form_pg_attribute attrtuple;
    8493                 :     AttrNumber  attnum;
    8494                 :     ObjectAddress address;
    8495                 : 
    8496                 :     /*
    8497                 :      * We allow referencing columns by numbers only for indexes, since table
    8498                 :      * column numbers could contain gaps if columns are later dropped.
    8499                 :      */
    8500 GIC          82 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    8501              50 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    8502                 :         !colName)
    8503 UIC           0 :         ereport(ERROR,
    8504                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8505                 :                  errmsg("cannot refer to non-index column by number")));
    8506                 : 
    8507 GIC          82 :     Assert(IsA(newValue, Integer));
    8508              82 :     newtarget = intVal(newValue);
    8509                 : 
    8510                 :     /*
    8511 ECB             :      * Limit target to a sane range
    8512                 :      */
    8513 GIC          82 :     if (newtarget < -1)
    8514 ECB             :     {
    8515 UIC           0 :         ereport(ERROR,
    8516 ECB             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    8517                 :                  errmsg("statistics target %d is too low",
    8518                 :                         newtarget)));
    8519                 :     }
    8520 GIC          82 :     else if (newtarget > 10000)
    8521                 :     {
    8522 UIC           0 :         newtarget = 10000;
    8523               0 :         ereport(WARNING,
    8524                 :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    8525                 :                  errmsg("lowering statistics target to %d",
    8526                 :                         newtarget)));
    8527 ECB             :     }
    8528                 : 
    8529 GIC          82 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8530                 : 
    8531              82 :     if (colName)
    8532                 :     {
    8533              50 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8534                 : 
    8535 CBC          50 :         if (!HeapTupleIsValid(tuple))
    8536 GIC           6 :             ereport(ERROR,
    8537 ECB             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8538                 :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    8539                 :                             colName, RelationGetRelationName(rel))));
    8540                 :     }
    8541 EUB             :     else
    8542                 :     {
    8543 GIC          32 :         tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), colNum);
    8544                 : 
    8545 CBC          32 :         if (!HeapTupleIsValid(tuple))
    8546               6 :             ereport(ERROR,
    8547                 :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8548                 :                      errmsg("column number %d of relation \"%s\" does not exist",
    8549 ECB             :                             colNum, RelationGetRelationName(rel))));
    8550 EUB             :     }
    8551                 : 
    8552 GIC          70 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8553                 : 
    8554              70 :     attnum = attrtuple->attnum;
    8555              70 :     if (attnum <= 0)
    8556 UIC           0 :         ereport(ERROR,
    8557                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8558                 :                  errmsg("cannot alter system column \"%s\"",
    8559                 :                         colName)));
    8560 ECB             : 
    8561 CBC          70 :     if (rel->rd_rel->relkind == RELKIND_INDEX ||
    8562 GIC          44 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    8563                 :     {
    8564              26 :         if (attnum > rel->rd_index->indnkeyatts)
    8565               3 :             ereport(ERROR,
    8566 ECB             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8567                 :                      errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
    8568                 :                             NameStr(attrtuple->attname), RelationGetRelationName(rel))));
    8569 GIC          23 :         else if (rel->rd_index->indkey.values[attnum - 1] != 0)
    8570               9 :             ereport(ERROR,
    8571                 :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8572 ECB             :                      errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
    8573                 :                             NameStr(attrtuple->attname), RelationGetRelationName(rel)),
    8574                 :                      errhint("Alter statistics on table column instead.")));
    8575                 :     }
    8576                 : 
    8577 GIC          58 :     attrtuple->attstattarget = newtarget;
    8578 ECB             : 
    8579 CBC          58 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8580                 : 
    8581              58 :     InvokeObjectPostAlterHook(RelationRelationId,
    8582                 :                               RelationGetRelid(rel),
    8583                 :                               attrtuple->attnum);
    8584              58 :     ObjectAddressSubSet(address, RelationRelationId,
    8585                 :                         RelationGetRelid(rel), attnum);
    8586              58 :     heap_freetuple(tuple);
    8587                 : 
    8588              58 :     table_close(attrelation, RowExclusiveLock);
    8589                 : 
    8590              58 :     return address;
    8591                 : }
    8592                 : 
    8593                 : /*
    8594                 :  * Return value is the address of the modified column
    8595                 :  */
    8596                 : static ObjectAddress
    8597 GIC          16 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
    8598                 :                  bool isReset, LOCKMODE lockmode)
    8599 ECB             : {
    8600                 :     Relation    attrelation;
    8601                 :     HeapTuple   tuple,
    8602                 :                 newtuple;
    8603                 :     Form_pg_attribute attrtuple;
    8604                 :     AttrNumber  attnum;
    8605                 :     Datum       datum,
    8606                 :                 newOptions;
    8607                 :     bool        isnull;
    8608                 :     ObjectAddress address;
    8609                 :     Datum       repl_val[Natts_pg_attribute];
    8610                 :     bool        repl_null[Natts_pg_attribute];
    8611                 :     bool        repl_repl[Natts_pg_attribute];
    8612                 : 
    8613 CBC          16 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8614                 : 
    8615              16 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8616 EUB             : 
    8617 GIC          16 :     if (!HeapTupleIsValid(tuple))
    8618 UIC           0 :         ereport(ERROR,
    8619 ECB             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8620                 :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8621                 :                         colName, RelationGetRelationName(rel))));
    8622 GBC          16 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8623                 : 
    8624 GIC          16 :     attnum = attrtuple->attnum;
    8625              16 :     if (attnum <= 0)
    8626 UIC           0 :         ereport(ERROR,
    8627                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8628                 :                  errmsg("cannot alter system column \"%s\"",
    8629                 :                         colName)));
    8630                 : 
    8631                 :     /* Generate new proposed attoptions (text array) */
    8632 CBC          16 :     datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
    8633 ECB             :                             &isnull);
    8634 CBC          16 :     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
    8635 GBC          16 :                                      castNode(List, options), NULL, NULL,
    8636                 :                                      false, isReset);
    8637                 :     /* Validate new options */
    8638 GIC          16 :     (void) attribute_reloptions(newOptions, true);
    8639                 : 
    8640 ECB             :     /* Build new tuple. */
    8641 CBC          16 :     memset(repl_null, false, sizeof(repl_null));
    8642 GIC          16 :     memset(repl_repl, false, sizeof(repl_repl));
    8643 CBC          16 :     if (newOptions != (Datum) 0)
    8644 GBC          16 :         repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
    8645                 :     else
    8646 UIC           0 :         repl_null[Anum_pg_attribute_attoptions - 1] = true;
    8647 GIC          16 :     repl_repl[Anum_pg_attribute_attoptions - 1] = true;
    8648              16 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    8649 ECB             :                                  repl_val, repl_null, repl_repl);
    8650                 : 
    8651                 :     /* Update system catalog. */
    8652 GIC          16 :     CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
    8653                 : 
    8654              16 :     InvokeObjectPostAlterHook(RelationRelationId,
    8655 ECB             :                               RelationGetRelid(rel),
    8656                 :                               attrtuple->attnum);
    8657 CBC          16 :     ObjectAddressSubSet(address, RelationRelationId,
    8658 ECB             :                         RelationGetRelid(rel), attnum);
    8659                 : 
    8660 CBC          16 :     heap_freetuple(newtuple);
    8661                 : 
    8662 GIC          16 :     ReleaseSysCache(tuple);
    8663 ECB             : 
    8664 GIC          16 :     table_close(attrelation, RowExclusiveLock);
    8665                 : 
    8666              16 :     return address;
    8667 ECB             : }
    8668                 : 
    8669                 : /*
    8670                 :  * Helper function for ATExecSetStorage and ATExecSetCompression
    8671                 :  *
    8672                 :  * Set the attstorage and/or attcompression fields for index columns
    8673                 :  * associated with the specified table column.
    8674                 :  */
    8675                 : static void
    8676 GIC         139 : SetIndexStorageProperties(Relation rel, Relation attrelation,
    8677                 :                           AttrNumber attnum,
    8678                 :                           bool setstorage, char newstorage,
    8679                 :                           bool setcompression, char newcompression,
    8680                 :                           LOCKMODE lockmode)
    8681 ECB             : {
    8682                 :     ListCell   *lc;
    8683                 : 
    8684 GIC         174 :     foreach(lc, RelationGetIndexList(rel))
    8685                 :     {
    8686              35 :         Oid         indexoid = lfirst_oid(lc);
    8687                 :         Relation    indrel;
    8688              35 :         AttrNumber  indattnum = 0;
    8689                 :         HeapTuple   tuple;
    8690                 : 
    8691 CBC          35 :         indrel = index_open(indexoid, lockmode);
    8692 ECB             : 
    8693 CBC          59 :         for (int i = 0; i < indrel->rd_index->indnatts; i++)
    8694 EUB             :         {
    8695 GIC          38 :             if (indrel->rd_index->indkey.values[i] == attnum)
    8696                 :             {
    8697              14 :                 indattnum = i + 1;
    8698              14 :                 break;
    8699 ECB             :             }
    8700                 :         }
    8701                 : 
    8702 CBC          35 :         if (indattnum == 0)
    8703 EUB             :         {
    8704 GIC          21 :             index_close(indrel, lockmode);
    8705              21 :             continue;
    8706                 :         }
    8707                 : 
    8708 CBC          14 :         tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
    8709                 : 
    8710              14 :         if (HeapTupleIsValid(tuple))
    8711 ECB             :         {
    8712 GIC          14 :             Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8713                 : 
    8714              14 :             if (setstorage)
    8715              11 :                 attrtuple->attstorage = newstorage;
    8716                 : 
    8717 CBC          14 :             if (setcompression)
    8718 GIC           3 :                 attrtuple->attcompression = newcompression;
    8719                 : 
    8720 CBC          14 :             CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8721 ECB             : 
    8722 CBC          14 :             InvokeObjectPostAlterHook(RelationRelationId,
    8723                 :                                       RelationGetRelid(rel),
    8724                 :                                       attrtuple->attnum);
    8725                 : 
    8726              14 :             heap_freetuple(tuple);
    8727 ECB             :         }
    8728                 : 
    8729 CBC          14 :         index_close(indrel, lockmode);
    8730                 :     }
    8731 GIC         139 : }
    8732 ECB             : 
    8733                 : /*
    8734                 :  * ALTER TABLE ALTER COLUMN SET STORAGE
    8735                 :  *
    8736                 :  * Return value is the address of the modified column
    8737                 :  */
    8738                 : static ObjectAddress
    8739 CBC         115 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
    8740 ECB             : {
    8741                 :     Relation    attrelation;
    8742                 :     HeapTuple   tuple;
    8743                 :     Form_pg_attribute attrtuple;
    8744                 :     AttrNumber  attnum;
    8745                 :     ObjectAddress address;
    8746                 : 
    8747 CBC         115 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8748                 : 
    8749 GIC         115 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8750                 : 
    8751             115 :     if (!HeapTupleIsValid(tuple))
    8752               6 :         ereport(ERROR,
    8753                 :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8754 ECB             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8755                 :                         colName, RelationGetRelationName(rel))));
    8756 GIC         109 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    8757                 : 
    8758             109 :     attnum = attrtuple->attnum;
    8759 CBC         109 :     if (attnum <= 0)
    8760 LBC           0 :         ereport(ERROR,
    8761 EUB             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8762                 :                  errmsg("cannot alter system column \"%s\"",
    8763                 :                         colName)));
    8764                 : 
    8765 GNC         109 :     attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
    8766                 : 
    8767 GIC         109 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8768                 : 
    8769 CBC         109 :     InvokeObjectPostAlterHook(RelationRelationId,
    8770                 :                               RelationGetRelid(rel),
    8771                 :                               attrtuple->attnum);
    8772                 : 
    8773                 :     /*
    8774                 :      * Apply the change to indexes as well (only for simple index columns,
    8775                 :      * matching behavior of index.c ConstructTupleDescriptor()).
    8776 ECB             :      */
    8777 CBC         109 :     SetIndexStorageProperties(rel, attrelation, attnum,
    8778 GNC         109 :                               true, attrtuple->attstorage,
    8779 EUB             :                               false, 0,
    8780                 :                               lockmode);
    8781                 : 
    8782 GNC         109 :     heap_freetuple(tuple);
    8783                 : 
    8784 GIC         109 :     table_close(attrelation, RowExclusiveLock);
    8785                 : 
    8786 CBC         109 :     ObjectAddressSubSet(address, RelationRelationId,
    8787 ECB             :                         RelationGetRelid(rel), attnum);
    8788 GIC         109 :     return address;
    8789 ECB             : }
    8790 EUB             : 
    8791                 : 
    8792                 : /*
    8793                 :  * ALTER TABLE DROP COLUMN
    8794                 :  *
    8795 ECB             :  * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
    8796                 :  * because we have to decide at runtime whether to recurse or not depending
    8797                 :  * on whether attinhcount goes to zero or not.  (We can't check this in a
    8798                 :  * static pre-pass because it won't handle multiple inheritance situations
    8799                 :  * correctly.)
    8800                 :  */
    8801                 : static void
    8802 GIC         778 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    8803                 :                  AlterTableCmd *cmd, LOCKMODE lockmode,
    8804 ECB             :                  AlterTableUtilityContext *context)
    8805                 : {
    8806 GIC         778 :     if (rel->rd_rel->reloftype && !recursing)
    8807 CBC           3 :         ereport(ERROR,
    8808 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    8809                 :                  errmsg("cannot drop column from typed table")));
    8810                 : 
    8811 GIC         775 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    8812              41 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    8813                 : 
    8814             772 :     if (recurse)
    8815 GNC         638 :         cmd->recurse = true;
    8816 GIC         772 : }
    8817 ECB             : 
    8818                 : /*
    8819                 :  * Drops column 'colName' from relation 'rel' and returns the address of the
    8820                 :  * dropped column.  The column is also dropped (or marked as no longer
    8821                 :  * inherited from relation) from the relation's inheritance children, if any.
    8822                 :  *
    8823                 :  * In the recursive invocations for inheritance child relations, instead of
    8824                 :  * dropping the column directly (if to be dropped at all), its object address
    8825                 :  * is added to 'addrs', which must be non-NULL in such invocations.  All
    8826                 :  * columns are dropped at the same time after all the children have been
    8827                 :  * checked recursively.
    8828                 :  */
    8829                 : static ObjectAddress
    8830 GIC        1050 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
    8831                 :                  DropBehavior behavior,
    8832 ECB             :                  bool recurse, bool recursing,
    8833                 :                  bool missing_ok, LOCKMODE lockmode,
    8834 EUB             :                  ObjectAddresses *addrs)
    8835                 : {
    8836 ECB             :     HeapTuple   tuple;
    8837                 :     Form_pg_attribute targetatt;
    8838                 :     AttrNumber  attnum;
    8839                 :     List       *children;
    8840                 :     ObjectAddress object;
    8841                 :     bool        is_expr;
    8842                 : 
    8843                 :     /* At top level, permission check was done in ATPrepCmd, else do it */
    8844 GIC        1050 :     if (recursing)
    8845             278 :         ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    8846 ECB             : 
    8847                 :     /* Initialize addrs on the first invocation */
    8848 GIC        1050 :     Assert(!recursing || addrs != NULL);
    8849 CBC        1050 :     if (!recursing)
    8850 GIC         772 :         addrs = new_object_addresses();
    8851 ECB             : 
    8852                 :     /*
    8853                 :      * get the number of the attribute
    8854                 :      */
    8855 GIC        1050 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8856            1050 :     if (!HeapTupleIsValid(tuple))
    8857                 :     {
    8858              27 :         if (!missing_ok)
    8859                 :         {
    8860 CBC          18 :             ereport(ERROR,
    8861                 :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8862                 :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    8863                 :                             colName, RelationGetRelationName(rel))));
    8864                 :         }
    8865                 :         else
    8866                 :         {
    8867 GIC           9 :             ereport(NOTICE,
    8868                 :                     (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
    8869                 :                             colName, RelationGetRelationName(rel))));
    8870               9 :             return InvalidObjectAddress;
    8871                 :         }
    8872                 :     }
    8873 CBC        1023 :     targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
    8874 ECB             : 
    8875 GIC        1023 :     attnum = targetatt->attnum;
    8876 EUB             : 
    8877                 :     /* Can't drop a system attribute */
    8878 GIC        1023 :     if (attnum <= 0)
    8879               3 :         ereport(ERROR,
    8880 ECB             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8881                 :                  errmsg("cannot drop system column \"%s\"",
    8882                 :                         colName)));
    8883                 : 
    8884                 :     /*
    8885                 :      * Don't drop inherited columns, unless recursing (presumably from a drop
    8886                 :      * of the parent column)
    8887                 :      */
    8888 GBC        1020 :     if (targetatt->attinhcount > 0 && !recursing)
    8889 GIC          24 :         ereport(ERROR,
    8890                 :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8891                 :                  errmsg("cannot drop inherited column \"%s\"",
    8892                 :                         colName)));
    8893 ECB             : 
    8894                 :     /*
    8895 EUB             :      * Don't drop columns used in the partition key, either.  (If we let this
    8896                 :      * go through, the key column's dependencies would cause a cascaded drop
    8897                 :      * of the whole table, which is surely not what the user expected.)
    8898                 :      */
    8899 GIC         996 :     if (has_partition_attrs(rel,
    8900                 :                             bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
    8901                 :                             &is_expr))
    8902 CBC          15 :         ereport(ERROR,
    8903                 :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8904 ECB             :                  errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
    8905                 :                         colName, RelationGetRelationName(rel))));
    8906                 : 
    8907 GIC         981 :     ReleaseSysCache(tuple);
    8908 ECB             : 
    8909                 :     /*
    8910                 :      * Propagate to children as appropriate.  Unlike most other ALTER
    8911                 :      * routines, we have to do this one level of recursion at a time; we can't
    8912                 :      * use find_all_inheritors to do it in one pass.
    8913                 :      */
    8914                 :     children =
    8915 GIC         981 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    8916 ECB             : 
    8917 GIC         981 :     if (children)
    8918 ECB             :     {
    8919                 :         Relation    attr_rel;
    8920                 :         ListCell   *child;
    8921                 : 
    8922                 :         /*
    8923                 :          * In case of a partitioned table, the column must be dropped from the
    8924                 :          * partitions as well.
    8925                 :          */
    8926 GIC         151 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
    8927 CBC           3 :             ereport(ERROR,
    8928 ECB             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8929 EUB             :                      errmsg("cannot drop column from only the partitioned table when partitions exist"),
    8930                 :                      errhint("Do not specify the ONLY keyword.")));
    8931                 : 
    8932 GIC         148 :         attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    8933             441 :         foreach(child, children)
    8934 ECB             :         {
    8935 CBC         296 :             Oid         childrelid = lfirst_oid(child);
    8936                 :             Relation    childrel;
    8937 ECB             :             Form_pg_attribute childatt;
    8938                 : 
    8939                 :             /* find_inheritance_children already got lock */
    8940 GIC         296 :             childrel = table_open(childrelid, NoLock);
    8941             296 :             CheckTableNotInUse(childrel, "ALTER TABLE");
    8942 ECB             : 
    8943 CBC         296 :             tuple = SearchSysCacheCopyAttName(childrelid, colName);
    8944 GIC         296 :             if (!HeapTupleIsValid(tuple))   /* shouldn't happen */
    8945 UIC           0 :                 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
    8946                 :                      colName, childrelid);
    8947 GIC         296 :             childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    8948                 : 
    8949             296 :             if (childatt->attinhcount <= 0) /* shouldn't happen */
    8950 LBC           0 :                 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
    8951                 :                      childrelid, colName);
    8952 ECB             : 
    8953 GIC         296 :             if (recurse)
    8954 ECB             :             {
    8955                 :                 /*
    8956                 :                  * If the child column has other definition sources, just
    8957                 :                  * decrement its inheritance count; if not, recurse to delete
    8958                 :                  * it.
    8959                 :                  */
    8960 GIC         284 :                 if (childatt->attinhcount == 1 && !childatt->attislocal)
    8961 ECB             :                 {
    8962                 :                     /* Time to delete this child column, too */
    8963 CBC         278 :                     ATExecDropColumn(wqueue, childrel, colName,
    8964                 :                                      behavior, true, true,
    8965                 :                                      false, lockmode, addrs);
    8966                 :                 }
    8967                 :                 else
    8968                 :                 {
    8969                 :                     /* Child column must survive my deletion */
    8970               6 :                     childatt->attinhcount--;
    8971                 : 
    8972 GIC           6 :                     CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    8973                 : 
    8974                 :                     /* Make update visible */
    8975               6 :                     CommandCounterIncrement();
    8976                 :                 }
    8977                 :             }
    8978                 :             else
    8979                 :             {
    8980                 :                 /*
    8981                 :                  * If we were told to drop ONLY in this table (no recursion),
    8982                 :                  * we need to mark the inheritors' attributes as locally
    8983                 :                  * defined rather than inherited.
    8984                 :                  */
    8985              12 :                 childatt->attinhcount--;
    8986 CBC          12 :                 childatt->attislocal = true;
    8987                 : 
    8988              12 :                 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    8989                 : 
    8990 ECB             :                 /* Make update visible */
    8991 GBC          12 :                 CommandCounterIncrement();
    8992                 :             }
    8993                 : 
    8994 GIC         293 :             heap_freetuple(tuple);
    8995 ECB             : 
    8996 GIC         293 :             table_close(childrel, NoLock);
    8997 ECB             :         }
    8998 CBC         145 :         table_close(attr_rel, RowExclusiveLock);
    8999 EUB             :     }
    9000                 : 
    9001                 :     /* Add object to delete */
    9002 GIC         975 :     object.classId = RelationRelationId;
    9003             975 :     object.objectId = RelationGetRelid(rel);
    9004             975 :     object.objectSubId = attnum;
    9005 CBC         975 :     add_exact_object_address(&object, addrs);
    9006                 : 
    9007             975 :     if (!recursing)
    9008 ECB             :     {
    9009                 :         /* Recursion has ended, drop everything that was collected */
    9010 GIC         700 :         performMultipleDeletions(addrs, behavior, 0);
    9011 CBC         679 :         free_object_addresses(addrs);
    9012                 :     }
    9013                 : 
    9014             954 :     return object;
    9015 ECB             : }
    9016                 : 
    9017                 : /*
    9018                 :  * ALTER TABLE ADD INDEX
    9019 EUB             :  *
    9020 ECB             :  * There is no such command in the grammar, but parse_utilcmd.c converts
    9021                 :  * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands.  This lets
    9022                 :  * us schedule creation of the index at the appropriate time during ALTER.
    9023                 :  *
    9024                 :  * Return value is the address of the new index.
    9025                 :  */
    9026                 : static ObjectAddress
    9027 CBC         634 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
    9028                 :                IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9029                 : {
    9030 ECB             :     bool        check_rights;
    9031                 :     bool        skip_build;
    9032                 :     bool        quiet;
    9033                 :     ObjectAddress address;
    9034                 : 
    9035 CBC         634 :     Assert(IsA(stmt, IndexStmt));
    9036 GIC         634 :     Assert(!stmt->concurrent);
    9037 ECB             : 
    9038                 :     /* The IndexStmt has already been through transformIndexStmt */
    9039 CBC         634 :     Assert(stmt->transformed);
    9040                 : 
    9041                 :     /* suppress schema rights check when rebuilding existing index */
    9042 GIC         634 :     check_rights = !is_rebuild;
    9043                 :     /* skip index build if phase 3 will do it or we're reusing an old one */
    9044 GNC         634 :     skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
    9045                 :     /* suppress notices when rebuilding existing index */
    9046 GIC         634 :     quiet = is_rebuild;
    9047                 : 
    9048             634 :     address = DefineIndex(RelationGetRelid(rel),
    9049 ECB             :                           stmt,
    9050                 :                           InvalidOid,   /* no predefined OID */
    9051                 :                           InvalidOid,   /* no parent index */
    9052                 :                           InvalidOid,   /* no parent constraint */
    9053                 :                           -1,   /* total_parts unknown */
    9054                 :                           true, /* is_alter_table */
    9055                 :                           check_rights,
    9056                 :                           false,    /* check_not_in_use - we did it already */
    9057                 :                           skip_build,
    9058                 :                           quiet);
    9059                 : 
    9060                 :     /*
    9061                 :      * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
    9062                 :      * new index instead of building from scratch.  Restore associated fields.
    9063                 :      * This may store InvalidSubTransactionId in both fields, in which case
    9064                 :      * relcache.c will assume it can rebuild the relcache entry.  Hence, do
    9065                 :      * this after the CCI that made catalog rows visible to any rebuild.  The
    9066                 :      * DROP of the old edition of this index will have scheduled the storage
    9067                 :      * for deletion at commit, so cancel that pending deletion.
    9068                 :      */
    9069 GNC         582 :     if (RelFileNumberIsValid(stmt->oldNumber))
    9070                 :     {
    9071 CBC          36 :         Relation    irel = index_open(address.objectId, NoLock);
    9072 ECB             : 
    9073 GIC          36 :         irel->rd_createSubid = stmt->oldCreateSubid;
    9074 GNC          36 :         irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
    9075              36 :         RelationPreserveStorage(irel->rd_locator, true);
    9076 CBC          36 :         index_close(irel, NoLock);
    9077                 :     }
    9078 ECB             : 
    9079 CBC         582 :     return address;
    9080                 : }
    9081                 : 
    9082 ECB             : /*
    9083                 :  * ALTER TABLE ADD STATISTICS
    9084                 :  *
    9085                 :  * This is no such command in the grammar, but we use this internally to add
    9086                 :  * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
    9087                 :  * column type change.
    9088                 :  */
    9089                 : static ObjectAddress
    9090 GIC           7 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
    9091 ECB             :                     CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9092                 : {
    9093                 :     ObjectAddress address;
    9094                 : 
    9095 GIC           7 :     Assert(IsA(stmt, CreateStatsStmt));
    9096 ECB             : 
    9097                 :     /* The CreateStatsStmt has already been through transformStatsStmt */
    9098 GIC           7 :     Assert(stmt->transformed);
    9099                 : 
    9100 CBC           7 :     address = CreateStatistics(stmt);
    9101                 : 
    9102 GIC           7 :     return address;
    9103 ECB             : }
    9104                 : 
    9105                 : /*
    9106                 :  * ALTER TABLE ADD CONSTRAINT USING INDEX
    9107                 :  *
    9108                 :  * Returns the address of the new constraint.
    9109                 :  */
    9110                 : static ObjectAddress
    9111 GIC       33361 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
    9112                 :                          IndexStmt *stmt, LOCKMODE lockmode)
    9113 ECB             : {
    9114 GIC       33361 :     Oid         index_oid = stmt->indexOid;
    9115                 :     Relation    indexRel;
    9116                 :     char       *indexName;
    9117                 :     IndexInfo  *indexInfo;
    9118                 :     char       *constraintName;
    9119                 :     char        constraintType;
    9120                 :     ObjectAddress address;
    9121 ECB             :     bits16      flags;
    9122                 : 
    9123 CBC       33361 :     Assert(IsA(stmt, IndexStmt));
    9124 GIC       33361 :     Assert(OidIsValid(index_oid));
    9125 CBC       33361 :     Assert(stmt->isconstraint);
    9126 ECB             : 
    9127                 :     /*
    9128                 :      * Doing this on partitioned tables is not a simple feature to implement,
    9129                 :      * so let's punt for now.
    9130                 :      */
    9131 GIC       33361 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    9132 CBC           3 :         ereport(ERROR,
    9133 ECB             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9134 EUB             :                  errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
    9135                 : 
    9136 GIC       33358 :     indexRel = index_open(index_oid, AccessShareLock);
    9137                 : 
    9138           33358 :     indexName = pstrdup(RelationGetRelationName(indexRel));
    9139 ECB             : 
    9140 GIC       33358 :     indexInfo = BuildIndexInfo(indexRel);
    9141 ECB             : 
    9142                 :     /* this should have been checked at parse time */
    9143 CBC       33358 :     if (!indexInfo->ii_Unique)
    9144 UIC           0 :         elog(ERROR, "index \"%s\" is not unique", indexName);
    9145                 : 
    9146                 :     /*
    9147                 :      * Determine name to assign to constraint.  We require a constraint to
    9148                 :      * have the same name as the underlying index; therefore, use the index's
    9149                 :      * existing name as the default constraint name, and if the user
    9150                 :      * explicitly gives some other name for the constraint, rename the index
    9151 ECB             :      * to match.
    9152                 :      */
    9153 GIC       33358 :     constraintName = stmt->idxname;
    9154           33358 :     if (constraintName == NULL)
    9155           33351 :         constraintName = indexName;
    9156 CBC           7 :     else if (strcmp(constraintName, indexName) != 0)
    9157                 :     {
    9158               4 :         ereport(NOTICE,
    9159                 :                 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
    9160 ECB             :                         indexName, constraintName)));
    9161 GIC           4 :         RenameRelationInternal(index_oid, constraintName, false, true);
    9162 ECB             :     }
    9163                 : 
    9164                 :     /* Extra checks needed if making primary key */
    9165 GIC       33358 :     if (stmt->primary)
    9166           18810 :         index_check_primary_key(rel, indexInfo, true, stmt);
    9167                 : 
    9168                 :     /* Note we currently don't support EXCLUSION constraints here */
    9169           33355 :     if (stmt->primary)
    9170           18807 :         constraintType = CONSTRAINT_PRIMARY;
    9171                 :     else
    9172           14548 :         constraintType = CONSTRAINT_UNIQUE;
    9173                 : 
    9174                 :     /* Create the catalog entries for the constraint */
    9175           33355 :     flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
    9176 ECB             :         INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
    9177 GIC       66710 :         (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
    9178           33355 :         (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
    9179           33355 :         (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
    9180 ECB             : 
    9181 CBC       33355 :     address = index_constraint_create(rel,
    9182                 :                                       index_oid,
    9183                 :                                       InvalidOid,
    9184                 :                                       indexInfo,
    9185 ECB             :                                       constraintName,
    9186                 :                                       constraintType,
    9187                 :                                       flags,
    9188                 :                                       allowSystemTableMods,
    9189                 :                                       false);   /* is_internal */
    9190                 : 
    9191 GIC       33355 :     index_close(indexRel, NoLock);
    9192                 : 
    9193           33355 :     return address;
    9194                 : }
    9195                 : 
    9196                 : /*
    9197                 :  * ALTER TABLE ADD CONSTRAINT
    9198                 :  *
    9199                 :  * Return value is the address of the new constraint; if no constraint was
    9200                 :  * added, InvalidObjectAddress is returned.
    9201                 :  */
    9202                 : static ObjectAddress
    9203            1626 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9204 ECB             :                     Constraint *newConstraint, bool recurse, bool is_readd,
    9205                 :                     LOCKMODE lockmode)
    9206                 : {
    9207 GIC        1626 :     ObjectAddress address = InvalidObjectAddress;
    9208                 : 
    9209            1626 :     Assert(IsA(newConstraint, Constraint));
    9210                 : 
    9211                 :     /*
    9212                 :      * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
    9213                 :      * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
    9214                 :      * parse_utilcmd.c).
    9215                 :      */
    9216            1626 :     switch (newConstraint->contype)
    9217                 :     {
    9218 CBC         593 :         case CONSTR_CHECK:
    9219                 :         case CONSTR_NOTNULL:
    9220 ECB             :             address =
    9221 GIC         593 :                 ATAddCheckConstraint(wqueue, tab, rel,
    9222                 :                                      newConstraint, recurse, false, is_readd,
    9223 ECB             :                                      lockmode);
    9224 CBC         560 :             break;
    9225 ECB             : 
    9226 GIC        1033 :         case CONSTR_FOREIGN:
    9227                 : 
    9228                 :             /*
    9229                 :              * Assign or validate constraint name
    9230 ECB             :              */
    9231 CBC        1033 :             if (newConstraint->conname)
    9232                 :             {
    9233             392 :                 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
    9234                 :                                          RelationGetRelid(rel),
    9235             392 :                                          newConstraint->conname))
    9236 UIC           0 :                     ereport(ERROR,
    9237                 :                             (errcode(ERRCODE_DUPLICATE_OBJECT),
    9238                 :                              errmsg("constraint \"%s\" for relation \"%s\" already exists",
    9239                 :                                     newConstraint->conname,
    9240                 :                                     RelationGetRelationName(rel))));
    9241                 :             }
    9242 ECB             :             else
    9243 GIC         641 :                 newConstraint->conname =
    9244             641 :                     ChooseConstraintName(RelationGetRelationName(rel),
    9245 CBC         641 :                                          ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
    9246                 :                                          "fkey",
    9247 GIC         641 :                                          RelationGetNamespace(rel),
    9248 ECB             :                                          NIL);
    9249                 : 
    9250 CBC        1033 :             address = ATAddForeignKeyConstraint(wqueue, tab, rel,
    9251                 :                                                 newConstraint,
    9252                 :                                                 recurse, false,
    9253 ECB             :                                                 lockmode);
    9254 CBC         873 :             break;
    9255                 : 
    9256 UIC           0 :         default:
    9257               0 :             elog(ERROR, "unrecognized constraint type: %d",
    9258                 :                  (int) newConstraint->contype);
    9259                 :     }
    9260                 : 
    9261 GIC        1433 :     return address;
    9262                 : }
    9263 ECB             : 
    9264                 : /*
    9265                 :  * Generate the column-name portion of the constraint name for a new foreign
    9266                 :  * key given the list of column names that reference the referenced
    9267                 :  * table.  This will be passed to ChooseConstraintName along with the parent
    9268                 :  * table name and the "fkey" suffix.
    9269                 :  *
    9270                 :  * We know that less than NAMEDATALEN characters will actually be used, so we
    9271                 :  * can truncate the result once we've generated that many.
    9272                 :  *
    9273                 :  * XXX see also ChooseExtendedStatisticNameAddition and
    9274                 :  * ChooseIndexNameAddition.
    9275                 :  */
    9276                 : static char *
    9277 CBC         973 : ChooseForeignKeyConstraintNameAddition(List *colnames)
    9278                 : {
    9279                 :     char        buf[NAMEDATALEN * 2];
    9280 GIC         973 :     int         buflen = 0;
    9281                 :     ListCell   *lc;
    9282 ECB             : 
    9283 GIC         973 :     buf[0] = '\0';
    9284            2145 :     foreach(lc, colnames)
    9285                 :     {
    9286            1172 :         const char *name = strVal(lfirst(lc));
    9287                 : 
    9288            1172 :         if (buflen > 0)
    9289             199 :             buf[buflen++] = '_';    /* insert _ between names */
    9290 ECB             : 
    9291                 :         /*
    9292                 :          * At this point we have buflen <= NAMEDATALEN.  name should be less
    9293                 :          * than NAMEDATALEN already, but use strlcpy for paranoia.
    9294                 :          */
    9295 GIC        1172 :         strlcpy(buf + buflen, name, NAMEDATALEN);
    9296            1172 :         buflen += strlen(buf + buflen);
    9297            1172 :         if (buflen >= NAMEDATALEN)
    9298 UIC           0 :             break;
    9299                 :     }
    9300 GIC         973 :     return pstrdup(buf);
    9301 ECB             : }
    9302                 : 
    9303                 : /*
    9304                 :  * Add a check or NOT NULL constraint to a single table and its children.
    9305                 :  * Returns the address of the constraint added to the parent relation,
    9306                 :  * if one gets added, or InvalidObjectAddress otherwise.
    9307                 :  *
    9308                 :  * Subroutine for ATExecAddConstraint.
    9309                 :  *
    9310                 :  * We must recurse to child tables during execution, rather than using
    9311                 :  * ALTER TABLE's normal prep-time recursion.  The reason is that all the
    9312                 :  * constraints *must* be given the same name, else they won't be seen as
    9313                 :  * related later.  If the user didn't explicitly specify a name, then
    9314                 :  * AddRelationNewConstraints would normally assign different names to the
    9315                 :  * child constraints.  To fix that, we must capture the name assigned at
    9316                 :  * the parent table and pass that down.
    9317                 :  */
    9318                 : static ObjectAddress
    9319 CBC         876 : ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9320 EUB             :                      Constraint *constr, bool recurse, bool recursing,
    9321                 :                      bool is_readd, LOCKMODE lockmode)
    9322 ECB             : {
    9323                 :     List       *newcons;
    9324                 :     ListCell   *lcon;
    9325 EUB             :     List       *children;
    9326                 :     ListCell   *child;
    9327 GIC         876 :     ObjectAddress address = InvalidObjectAddress;
    9328 ECB             : 
    9329                 :     /* At top level, permission check was done in ATPrepCmd, else do it */
    9330 GIC         876 :     if (recursing)
    9331             216 :         ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
    9332                 : 
    9333                 :     /*
    9334                 :      * Call AddRelationNewConstraints to do the work, making sure it works on
    9335 ECB             :      * a copy of the Constraint so transformExpr can't modify the original. It
    9336                 :      * returns a list of cooked constraints.
    9337                 :      *
    9338                 :      * If the constraint ends up getting merged with a pre-existing one, it's
    9339                 :      * omitted from the returned list, which is what we want: we do not need
    9340                 :      * to do any validation work.  That can only happen at child tables,
    9341                 :      * though, since we disallow merging at the top level.
    9342                 :      */
    9343 GIC         876 :     newcons = AddRelationNewConstraints(rel, NIL,
    9344             876 :                                         list_make1(copyObject(constr)),
    9345 GNC         876 :                                         recursing || is_readd,  /* allow_merge */
    9346 GIC         876 :                                         !recursing, /* is_local */
    9347 ECB             :                                         is_readd,   /* is_internal */
    9348 GIC         876 :                                         NULL);  /* queryString not available
    9349                 :                                                  * here */
    9350 ECB             : 
    9351                 :     /* we don't expect more than one constraint here */
    9352 GIC         846 :     Assert(list_length(newcons) <= 1);
    9353                 : 
    9354                 :     /* Add each to-be-validated constraint to Phase 3's queue */
    9355            1663 :     foreach(lcon, newcons)
    9356                 :     {
    9357             817 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
    9358                 : 
    9359 GNC         817 :         if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
    9360 ECB             :         {
    9361                 :             NewConstraint *newcon;
    9362                 : 
    9363 CBC         392 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
    9364 GIC         392 :             newcon->name = ccon->name;
    9365             392 :             newcon->contype = ccon->contype;
    9366 CBC         392 :             newcon->qual = ccon->expr;
    9367                 : 
    9368 GIC         392 :             tab->constraints = lappend(tab->constraints, newcon);
    9369 ECB             :         }
    9370                 : 
    9371                 :         /* Save the actually assigned name if it was defaulted */
    9372 GIC         817 :         if (constr->conname == NULL)
    9373 CBC         243 :             constr->conname = ccon->name;
    9374                 : 
    9375                 :         /*
    9376                 :          * If adding a NOT NULL constraint, set the pg_attribute flag and
    9377                 :          * tell phase 3 to verify existing rows, if needed.
    9378                 :          */
    9379 GNC         817 :         if (constr->contype == CONSTR_NOTNULL)
    9380             237 :             set_attnotnull(wqueue, rel, ccon->attnum,
    9381             237 :                            !ccon->is_no_inherit, lockmode);
    9382                 : 
    9383 GIC         817 :         ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
    9384                 :     }
    9385 ECB             : 
    9386                 :     /* At this point we must have a locked-down name to use */
    9387 CBC         846 :     Assert(constr->conname != NULL);
    9388 ECB             : 
    9389                 :     /* Advance command counter in case same table is visited multiple times */
    9390 CBC         846 :     CommandCounterIncrement();
    9391                 : 
    9392                 :     /*
    9393 ECB             :      * If the constraint got merged with an existing constraint, we're done.
    9394                 :      * We mustn't recurse to child tables in this case, because they've
    9395                 :      * already got the constraint, and visiting them again would lead to an
    9396                 :      * incorrect value for coninhcount.
    9397                 :      */
    9398 GIC         846 :     if (newcons == NIL)
    9399              29 :         return address;
    9400                 : 
    9401                 :     /*
    9402                 :      * If adding a NO INHERIT constraint, no need to find our children.
    9403                 :      */
    9404             817 :     if (constr->is_no_inherit)
    9405              33 :         return address;
    9406                 : 
    9407                 :     /*
    9408                 :      * Propagate to children as appropriate.  Unlike most other ALTER
    9409                 :      * routines, we have to do this one level of recursion at a time; we can't
    9410 ECB             :      * use find_all_inheritors to do it in one pass.
    9411                 :      */
    9412                 :     children =
    9413 GIC         784 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    9414                 : 
    9415                 :     /*
    9416                 :      * Check if ONLY was specified with ALTER TABLE.  If so, allow the
    9417                 :      * constraint creation only if there are no children currently.  Error out
    9418 ECB             :      * otherwise.
    9419                 :      */
    9420 GIC         784 :     if (!recurse && children != NIL)
    9421               3 :         ereport(ERROR,
    9422 ECB             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9423                 :                  errmsg("constraint must be added to child tables too")));
    9424                 : 
    9425 CBC         994 :     foreach(child, children)
    9426                 :     {
    9427             216 :         Oid         childrelid = lfirst_oid(child);
    9428                 :         Relation    childrel;
    9429 ECB             :         AlteredTableInfo *childtab;
    9430                 : 
    9431                 :         /* find_inheritance_children already got lock */
    9432 GIC         216 :         childrel = table_open(childrelid, NoLock);
    9433             216 :         CheckTableNotInUse(childrel, "ALTER TABLE");
    9434                 : 
    9435                 :         /* Find or create work queue entry for this table */
    9436             216 :         childtab = ATGetQueueEntry(wqueue, childrel);
    9437                 : 
    9438                 :         /* Recurse to child */
    9439             216 :         ATAddCheckConstraint(wqueue, childtab, childrel,
    9440                 :                              constr, recurse, true, is_readd, lockmode);
    9441                 : 
    9442             213 :         table_close(childrel, NoLock);
    9443                 :     }
    9444                 : 
    9445             778 :     return address;
    9446                 : }
    9447                 : 
    9448                 : /*
    9449                 :  * Add a foreign-key constraint to a single table; return the new constraint's
    9450                 :  * address.
    9451                 :  *
    9452 ECB             :  * Subroutine for ATExecAddConstraint.  Must already hold exclusive
    9453                 :  * lock on the rel, and have done appropriate validity checks for it.
    9454                 :  * We do permissions checks here, however.
    9455                 :  *
    9456                 :  * When the referenced or referencing tables (or both) are partitioned,
    9457                 :  * multiple pg_constraint rows are required -- one for each partitioned table
    9458                 :  * and each partition on each side (fortunately, not one for every combination
    9459                 :  * thereof).  We also need action triggers on each leaf partition on the
    9460                 :  * referenced side, and check triggers on each leaf partition on the
    9461                 :  * referencing side.
    9462                 :  */
    9463                 : static ObjectAddress
    9464 GIC        1033 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9465                 :                           Constraint *fkconstraint,
    9466                 :                           bool recurse, bool recursing, LOCKMODE lockmode)
    9467                 : {
    9468                 :     Relation    pkrel;
    9469 GNC        1033 :     int16       pkattnum[INDEX_MAX_KEYS] = {0};
    9470            1033 :     int16       fkattnum[INDEX_MAX_KEYS] = {0};
    9471            1033 :     Oid         pktypoid[INDEX_MAX_KEYS] = {0};
    9472            1033 :     Oid         fktypoid[INDEX_MAX_KEYS] = {0};
    9473            1033 :     Oid         opclasses[INDEX_MAX_KEYS] = {0};
    9474            1033 :     Oid         pfeqoperators[INDEX_MAX_KEYS] = {0};
    9475            1033 :     Oid         ppeqoperators[INDEX_MAX_KEYS] = {0};
    9476            1033 :     Oid         ffeqoperators[INDEX_MAX_KEYS] = {0};
    9477            1033 :     int16       fkdelsetcols[INDEX_MAX_KEYS] = {0};
    9478 ECB             :     int         i;
    9479                 :     int         numfks,
    9480                 :                 numpks,
    9481                 :                 numfkdelsetcols;
    9482                 :     Oid         indexOid;
    9483                 :     bool        old_check_ok;
    9484                 :     ObjectAddress address;
    9485 CBC        1033 :     ListCell   *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
    9486                 : 
    9487                 :     /*
    9488                 :      * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
    9489                 :      * delete rows out from under us.
    9490                 :      */
    9491 GIC        1033 :     if (OidIsValid(fkconstraint->old_pktable_oid))
    9492              30 :         pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
    9493                 :     else
    9494 CBC        1003 :         pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
    9495                 : 
    9496                 :     /*
    9497 ECB             :      * Validity checks (permission checks wait till we have the column
    9498                 :      * numbers)
    9499                 :      */
    9500 GIC        1033 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    9501                 :     {
    9502             145 :         if (!recurse)
    9503               3 :             ereport(ERROR,
    9504                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9505                 :                      errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
    9506 ECB             :                             RelationGetRelationName(rel),
    9507                 :                             RelationGetRelationName(pkrel))));
    9508 CBC         142 :         if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
    9509 GIC           3 :             ereport(ERROR,
    9510                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9511                 :                      errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
    9512                 :                             RelationGetRelationName(rel),
    9513                 :                             RelationGetRelationName(pkrel)),
    9514 ECB             :                      errdetail("This feature is not yet supported on partitioned tables.")));
    9515                 :     }
    9516                 : 
    9517 GIC        1027 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
    9518             128 :         pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    9519 LBC           0 :         ereport(ERROR,
    9520                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9521 ECB             :                  errmsg("referenced relation \"%s\" is not a table",
    9522                 :                         RelationGetRelationName(pkrel))));
    9523                 : 
    9524 GIC        1027 :     if (!allowSystemTableMods && IsSystemRelation(pkrel))
    9525               1 :         ereport(ERROR,
    9526 ECB             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    9527 EUB             :                  errmsg("permission denied: \"%s\" is a system catalog",
    9528                 :                         RelationGetRelationName(pkrel))));
    9529                 : 
    9530                 :     /*
    9531                 :      * References from permanent or unlogged tables to temp tables, and from
    9532                 :      * permanent tables to unlogged tables, are disallowed because the
    9533                 :      * referenced data can vanish out from under us.  References from temp
    9534                 :      * tables to any other table type are also disallowed, because other
    9535                 :      * backends might need to run the RI triggers on the perm table, but they
    9536 ECB             :      * can't reliably see tuples in the local buffers of other backends.
    9537                 :      */
    9538 CBC        1026 :     switch (rel->rd_rel->relpersistence)
    9539 ECB             :     {
    9540 GIC         871 :         case RELPERSISTENCE_PERMANENT:
    9541 CBC         871 :             if (!RelationIsPermanent(pkrel))
    9542 UIC           0 :                 ereport(ERROR,
    9543                 :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9544 ECB             :                          errmsg("constraints on permanent tables may reference only permanent tables")));
    9545 GIC         871 :             break;
    9546              16 :         case RELPERSISTENCE_UNLOGGED:
    9547              16 :             if (!RelationIsPermanent(pkrel)
    9548 CBC          16 :                 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
    9549 LBC           0 :                 ereport(ERROR,
    9550                 :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9551                 :                          errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
    9552 CBC          16 :             break;
    9553             139 :         case RELPERSISTENCE_TEMP:
    9554 GIC         139 :             if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
    9555 LBC           0 :                 ereport(ERROR,
    9556                 :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9557                 :                          errmsg("constraints on temporary tables may reference only temporary tables")));
    9558 CBC         139 :             if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
    9559 UIC           0 :                 ereport(ERROR,
    9560 ECB             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9561                 :                          errmsg("constraints on temporary tables must involve temporary tables of this session")));
    9562 CBC         139 :             break;
    9563                 :     }
    9564 ECB             : 
    9565                 :     /*
    9566                 :      * Look up the referencing attributes to make sure they exist, and record
    9567                 :      * their attnums and type OIDs.
    9568                 :      */
    9569 GIC        1026 :     numfks = transformColumnNameList(RelationGetRelid(rel),
    9570                 :                                      fkconstraint->fk_attrs,
    9571                 :                                      fkattnum, fktypoid);
    9572                 : 
    9573            1011 :     numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
    9574                 :                                               fkconstraint->fk_del_set_cols,
    9575                 :                                               fkdelsetcols, NULL);
    9576 CBC        1008 :     validateFkOnDeleteSetColumns(numfks, fkattnum,
    9577                 :                                  numfkdelsetcols, fkdelsetcols,
    9578                 :                                  fkconstraint->fk_del_set_cols);
    9579                 : 
    9580 ECB             :     /*
    9581                 :      * If the attribute list for the referenced table was omitted, lookup the
    9582                 :      * definition of the primary key and use it.  Otherwise, validate the
    9583                 :      * supplied attribute list.  In either case, discover the index OID and
    9584                 :      * index opclasses, and the attnums and type OIDs of the attributes.
    9585                 :      */
    9586 GIC        1005 :     if (fkconstraint->pk_attrs == NIL)
    9587                 :     {
    9588             458 :         numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
    9589 ECB             :                                             &fkconstraint->pk_attrs,
    9590                 :                                             pkattnum, pktypoid,
    9591                 :                                             opclasses);
    9592                 :     }
    9593                 :     else
    9594                 :     {
    9595 GIC         547 :         numpks = transformColumnNameList(RelationGetRelid(pkrel),
    9596                 :                                          fkconstraint->pk_attrs,
    9597 ECB             :                                          pkattnum, pktypoid);
    9598                 :         /* Look for an index matching the column list */
    9599 CBC         532 :         indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
    9600                 :                                            opclasses);
    9601                 :     }
    9602                 : 
    9603                 :     /*
    9604 ECB             :      * Now we can check permissions.
    9605                 :      */
    9606 CBC         984 :     checkFkeyPermissions(pkrel, pkattnum, numpks);
    9607                 : 
    9608 ECB             :     /*
    9609 EUB             :      * Check some things for generated columns.
    9610                 :      */
    9611 GIC        2220 :     for (i = 0; i < numfks; i++)
    9612                 :     {
    9613            1242 :         char        attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
    9614                 : 
    9615            1242 :         if (attgenerated)
    9616 ECB             :         {
    9617                 :             /*
    9618                 :              * Check restrictions on UPDATE/DELETE actions, per SQL standard
    9619                 :              */
    9620 CBC           9 :             if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
    9621 GIC           9 :                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
    9622               9 :                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
    9623 CBC           3 :                 ereport(ERROR,
    9624                 :                         (errcode(ERRCODE_SYNTAX_ERROR),
    9625                 :                          errmsg("invalid %s action for foreign key constraint containing generated column",
    9626                 :                                 "ON UPDATE")));
    9627               6 :             if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
    9628 GIC           3 :                 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
    9629 GBC           3 :                 ereport(ERROR,
    9630 EUB             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    9631                 :                          errmsg("invalid %s action for foreign key constraint containing generated column",
    9632                 :                                 "ON DELETE")));
    9633                 :         }
    9634 ECB             :     }
    9635                 : 
    9636                 :     /*
    9637                 :      * Look up the equality operators to use in the constraint.
    9638                 :      *
    9639                 :      * Note that we have to be careful about the difference between the actual
    9640                 :      * PK column type and the opclass' declared input type, which might be
    9641                 :      * only binary-compatible with it.  The declared opcintype is the right
    9642                 :      * thing to probe pg_amop with.
    9643                 :      */
    9644 GIC         978 :     if (numfks != numpks)
    9645 UIC           0 :         ereport(ERROR,
    9646                 :                 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
    9647                 :                  errmsg("number of referencing and referenced columns for foreign key disagree")));
    9648                 : 
    9649                 :     /*
    9650 ECB             :      * On the strength of a previous constraint, we might avoid scanning
    9651                 :      * tables to validate this one.  See below.
    9652                 :      */
    9653 CBC         978 :     old_check_ok = (fkconstraint->old_conpfeqop != NIL);
    9654 GIC         978 :     Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
    9655                 : 
    9656 CBC        2037 :     for (i = 0; i < numpks; i++)
    9657 ECB             :     {
    9658 GIC        1164 :         Oid         pktype = pktypoid[i];
    9659 CBC        1164 :         Oid         fktype = fktypoid[i];
    9660                 :         Oid         fktyped;
    9661 ECB             :         HeapTuple   cla_ht;
    9662                 :         Form_pg_opclass cla_tup;
    9663                 :         Oid         amid;
    9664                 :         Oid         opfamily;
    9665                 :         Oid         opcintype;
    9666                 :         Oid         pfeqop;
    9667                 :         Oid         ppeqop;
    9668                 :         Oid         ffeqop;
    9669                 :         int16       eqstrategy;
    9670                 :         Oid         pfeqop_right;
    9671 EUB             : 
    9672                 :         /* We need several fields out of the pg_opclass entry */
    9673 CBC        1164 :         cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
    9674 GIC        1164 :         if (!HeapTupleIsValid(cla_ht))
    9675 UIC           0 :             elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
    9676 GIC        1164 :         cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
    9677            1164 :         amid = cla_tup->opcmethod;
    9678            1164 :         opfamily = cla_tup->opcfamily;
    9679            1164 :         opcintype = cla_tup->opcintype;
    9680            1164 :         ReleaseSysCache(cla_ht);
    9681                 : 
    9682                 :         /*
    9683                 :          * Check it's a btree; currently this can never fail since no other
    9684                 :          * index AMs support unique indexes.  If we ever did have other types
    9685                 :          * of unique indexes, we'd need a way to determine which operator
    9686                 :          * strategy number is equality.  (Is it reasonable to insist that
    9687                 :          * every such index AM use btree's number for equality?)
    9688                 :          */
    9689            1164 :         if (amid != BTREE_AM_OID)
    9690 UIC           0 :             elog(ERROR, "only b-tree indexes are supported for foreign keys");
    9691 GIC        1164 :         eqstrategy = BTEqualStrategyNumber;
    9692 ECB             : 
    9693                 :         /*
    9694                 :          * There had better be a primary equality operator for the index.
    9695                 :          * We'll use it for PK = PK comparisons.
    9696                 :          */
    9697 GIC        1164 :         ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
    9698                 :                                      eqstrategy);
    9699                 : 
    9700 CBC        1164 :         if (!OidIsValid(ppeqop))
    9701 UIC           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
    9702                 :                  eqstrategy, opcintype, opcintype, opfamily);
    9703 ECB             : 
    9704                 :         /*
    9705                 :          * Are there equality operators that take exactly the FK type? Assume
    9706                 :          * we should look through any domain here.
    9707                 :          */
    9708 GIC        1164 :         fktyped = getBaseType(fktype);
    9709                 : 
    9710            1164 :         pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
    9711                 :                                      eqstrategy);
    9712            1164 :         if (OidIsValid(pfeqop))
    9713                 :         {
    9714            1035 :             pfeqop_right = fktyped;
    9715            1035 :             ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
    9716 ECB             :                                          eqstrategy);
    9717                 :         }
    9718                 :         else
    9719                 :         {
    9720                 :             /* keep compiler quiet */
    9721 CBC         129 :             pfeqop_right = InvalidOid;
    9722 GIC         129 :             ffeqop = InvalidOid;
    9723                 :         }
    9724                 : 
    9725 CBC        1164 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
    9726                 :         {
    9727                 :             /*
    9728 ECB             :              * Otherwise, look for an implicit cast from the FK type to the
    9729                 :              * opcintype, and if found, use the primary equality operator.
    9730                 :              * This is a bit tricky because opcintype might be a polymorphic
    9731                 :              * type such as ANYARRAY or ANYENUM; so what we have to test is
    9732                 :              * whether the two actual column types can be concurrently cast to
    9733                 :              * that type.  (Otherwise, we'd fail to reject combinations such
    9734                 :              * as int[] and point[].)
    9735                 :              */
    9736                 :             Oid         input_typeids[2];
    9737                 :             Oid         target_typeids[2];
    9738                 : 
    9739 CBC         129 :             input_typeids[0] = pktype;
    9740 GIC         129 :             input_typeids[1] = fktype;
    9741 CBC         129 :             target_typeids[0] = opcintype;
    9742 GIC         129 :             target_typeids[1] = opcintype;
    9743             129 :             if (can_coerce_type(2, input_typeids, target_typeids,
    9744                 :                                 COERCION_IMPLICIT))
    9745 ECB             :             {
    9746 CBC          24 :                 pfeqop = ffeqop = ppeqop;
    9747 GIC          24 :                 pfeqop_right = opcintype;
    9748                 :             }
    9749                 :         }
    9750                 : 
    9751            1164 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
    9752 CBC         105 :             ereport(ERROR,
    9753 ECB             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    9754                 :                      errmsg("foreign key constraint \"%s\" cannot be implemented",
    9755                 :                             fkconstraint->conname),
    9756                 :                      errdetail("Key columns \"%s\" and \"%s\" "
    9757                 :                                "are of incompatible types: %s and %s.",
    9758                 :                                strVal(list_nth(fkconstraint->fk_attrs, i)),
    9759                 :                                strVal(list_nth(fkconstraint->pk_attrs, i)),
    9760                 :                                format_type_be(fktype),
    9761                 :                                format_type_be(pktype))));
    9762                 : 
    9763 CBC        1059 :         if (old_check_ok)
    9764                 :         {
    9765                 :             /*
    9766                 :              * When a pfeqop changes, revalidate the constraint.  We could
    9767                 :              * permit intra-opfamily changes, but that adds subtle complexity
    9768                 :              * without any concrete benefit for core types.  We need not
    9769                 :              * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
    9770                 :              */
    9771               3 :             old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
    9772               3 :             old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
    9773                 :                                     old_pfeqop_item);
    9774                 :         }
    9775 GIC        1059 :         if (old_check_ok)
    9776                 :         {
    9777 ECB             :             Oid         old_fktype;
    9778                 :             Oid         new_fktype;
    9779                 :             CoercionPathType old_pathtype;
    9780                 :             CoercionPathType new_pathtype;
    9781                 :             Oid         old_castfunc;
    9782                 :             Oid         new_castfunc;
    9783 GIC           3 :             Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
    9784                 :                                                    fkattnum[i] - 1);
    9785                 : 
    9786 ECB             :             /*
    9787                 :              * Identify coercion pathways from each of the old and new FK-side
    9788                 :              * column types to the right (foreign) operand type of the pfeqop.
    9789                 :              * We may assume that pg_constraint.conkey is not changing.
    9790                 :              */
    9791 GIC           3 :             old_fktype = attr->atttypid;
    9792               3 :             new_fktype = fktype;
    9793 CBC           3 :             old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
    9794 ECB             :                                         &old_castfunc);
    9795 GIC           3 :             new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
    9796                 :                                         &new_castfunc);
    9797                 : 
    9798 ECB             :             /*
    9799                 :              * Upon a change to the cast from the FK column to its pfeqop
    9800                 :              * operand, revalidate the constraint.  For this evaluation, a
    9801                 :              * binary coercion cast is equivalent to no cast at all.  While
    9802                 :              * type implementors should design implicit casts with an eye
    9803                 :              * toward consistency of operations like equality, we cannot
    9804                 :              * assume here that they have done so.
    9805                 :              *
    9806                 :              * A function with a polymorphic argument could change behavior
    9807                 :              * arbitrarily in response to get_fn_expr_argtype().  Therefore,
    9808                 :              * when the cast destination is polymorphic, we only avoid
    9809                 :              * revalidation if the input type has not changed at all.  Given
    9810                 :              * just the core data types and operator classes, this requirement
    9811                 :              * prevents no would-be optimizations.
    9812                 :              *
    9813                 :              * If the cast converts from a base type to a domain thereon, then
    9814                 :              * that domain type must be the opcintype of the unique index.
    9815                 :              * Necessarily, the primary key column must then be of the domain
    9816                 :              * type.  Since the constraint was previously valid, all values on
    9817                 :              * the foreign side necessarily exist on the primary side and in
    9818                 :              * turn conform to the domain.  Consequently, we need not treat
    9819                 :              * domains specially here.
    9820                 :              *
    9821                 :              * Since we require that all collations share the same notion of
    9822                 :              * equality (which they do, because texteq reduces to bitwise
    9823                 :              * equality), we don't compare collation here.
    9824                 :              *
    9825                 :              * We need not directly consider the PK type.  It's necessarily
    9826                 :              * binary coercible to the opcintype of the unique index column,
    9827                 :              * and ri_triggers.c will only deal with PK datums in terms of
    9828                 :              * that opcintype.  Changing the opcintype also changes pfeqop.
    9829                 :              */
    9830 GIC           3 :             old_check_ok = (new_pathtype == old_pathtype &&
    9831               6 :                             new_castfunc == old_castfunc &&
    9832               3 :                             (!IsPolymorphicType(pfeqop_right) ||
    9833                 :                              new_fktype == old_fktype));
    9834                 :         }
    9835                 : 
    9836            1059 :         pfeqoperators[i] = pfeqop;
    9837 CBC        1059 :         ppeqoperators[i] = ppeqop;
    9838 GIC        1059 :         ffeqoperators[i] = ffeqop;
    9839                 :     }
    9840                 : 
    9841                 :     /*
    9842 ECB             :      * Create all the constraint and trigger objects, recursing to partitions
    9843                 :      * as necessary.  First handle the referenced side.
    9844                 :      */
    9845 CBC         873 :     address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
    9846 ECB             :                                      indexOid,
    9847                 :                                      InvalidOid,    /* no parent constraint */
    9848                 :                                      numfks,
    9849                 :                                      pkattnum,
    9850                 :                                      fkattnum,
    9851                 :                                      pfeqoperators,
    9852                 :                                      ppeqoperators,
    9853                 :                                      ffeqoperators,
    9854                 :                                      numfkdelsetcols,
    9855                 :                                      fkdelsetcols,
    9856                 :                                      old_check_ok,
    9857                 :                                      InvalidOid, InvalidOid);
    9858                 : 
    9859                 :     /* Now handle the referencing side. */
    9860 GIC         873 :     addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
    9861                 :                             indexOid,
    9862                 :                             address.objectId,
    9863                 :                             numfks,
    9864 ECB             :                             pkattnum,
    9865                 :                             fkattnum,
    9866                 :                             pfeqoperators,
    9867                 :                             ppeqoperators,
    9868                 :                             ffeqoperators,
    9869                 :                             numfkdelsetcols,
    9870                 :                             fkdelsetcols,
    9871                 :                             old_check_ok,
    9872                 :                             lockmode,
    9873                 :                             InvalidOid, InvalidOid);
    9874                 : 
    9875                 :     /*
    9876                 :      * Done.  Close pk table, but keep lock until we've committed.
    9877                 :      */
    9878 GIC         873 :     table_close(pkrel, NoLock);
    9879                 : 
    9880             873 :     return address;
    9881 ECB             : }
    9882                 : 
    9883                 : /*
    9884                 :  * validateFkOnDeleteSetColumns
    9885                 :  *      Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
    9886                 :  *      column lists are valid.
    9887                 :  */
    9888                 : void
    9889 GIC        1008 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
    9890 ECB             :                              int numfksetcols, const int16 *fksetcolsattnums,
    9891                 :                              List *fksetcols)
    9892 EUB             : {
    9893 GIC        1020 :     for (int i = 0; i < numfksetcols; i++)
    9894                 :     {
    9895              15 :         int16       setcol_attnum = fksetcolsattnums[i];
    9896              15 :         bool        seen = false;
    9897 ECB             : 
    9898 CBC          27 :         for (int j = 0; j < numfks; j++)
    9899                 :         {
    9900 GIC          24 :             if (fkattnums[j] == setcol_attnum)
    9901                 :             {
    9902              12 :                 seen = true;
    9903              12 :                 break;
    9904                 :             }
    9905                 :         }
    9906                 : 
    9907              15 :         if (!seen)
    9908                 :         {
    9909               3 :             char       *col = strVal(list_nth(fksetcols, i));
    9910                 : 
    9911 CBC           3 :             ereport(ERROR,
    9912                 :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
    9913 ECB             :                      errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
    9914                 :         }
    9915 EUB             :     }
    9916 GIC        1005 : }
    9917                 : 
    9918 ECB             : /*
    9919                 :  * addFkRecurseReferenced
    9920                 :  *      subroutine for ATAddForeignKeyConstraint; recurses on the referenced
    9921                 :  *      side of the constraint
    9922 EUB             :  *
    9923                 :  * Create pg_constraint rows for the referenced side of the constraint,
    9924                 :  * referencing the parent of the referencing side; also create action triggers
    9925 ECB             :  * on leaf partitions.  If the table is partitioned, recurse to handle each
    9926                 :  * partition.
    9927                 :  *
    9928 EUB             :  * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
    9929                 :  * of an ALTER TABLE sequence.
    9930                 :  * fkconstraint is the constraint being added.
    9931 ECB             :  * rel is the root referencing relation.
    9932 EUB             :  * pkrel is the referenced relation; might be a partition, if recursing.
    9933                 :  * indexOid is the OID of the index (on pkrel) implementing this constraint.
    9934                 :  * parentConstr is the OID of a parent constraint; InvalidOid if this is a
    9935 ECB             :  * top-level constraint.
    9936                 :  * numfks is the number of columns in the foreign key
    9937                 :  * pkattnum is the attnum array of referenced attributes.
    9938                 :  * fkattnum is the attnum array of referencing attributes.
    9939                 :  * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
    9940                 :  *      (...) clause
    9941                 :  * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
    9942                 :  *      NULL/DEFAULT clause
    9943                 :  * pf/pp/ffeqoperators are OID array of operators between columns.
    9944                 :  * old_check_ok signals that this constraint replaces an existing one that
    9945                 :  * was already validated (thus this one doesn't need validation).
    9946                 :  * parentDelTrigger and parentUpdTrigger, when being recursively called on
    9947                 :  * a partition, are the OIDs of the parent action triggers for DELETE and
    9948                 :  * UPDATE respectively.
    9949                 :  */
    9950                 : static ObjectAddress
    9951 GIC        1202 : addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
    9952                 :                        Relation pkrel, Oid indexOid, Oid parentConstr,
    9953                 :                        int numfks,
    9954                 :                        int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
    9955                 :                        Oid *ppeqoperators, Oid *ffeqoperators,
    9956                 :                        int numfkdelsetcols, int16 *fkdelsetcols,
    9957                 :                        bool old_check_ok,
    9958                 :                        Oid parentDelTrigger, Oid parentUpdTrigger)
    9959 ECB             : {
    9960                 :     ObjectAddress address;
    9961                 :     Oid         constrOid;
    9962                 :     char       *conname;
    9963                 :     bool        conislocal;
    9964                 :     int         coninhcount;
    9965                 :     bool        connoinherit;
    9966                 :     Oid         deleteTriggerOid,
    9967                 :                 updateTriggerOid;
    9968                 : 
    9969                 :     /*
    9970                 :      * Verify relkind for each referenced partition.  At the top level, this
    9971                 :      * is redundant with a previous check, but we need it when recursing.
    9972                 :      */
    9973 GIC        1202 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
    9974             178 :         pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    9975 UIC           0 :         ereport(ERROR,
    9976                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9977                 :                  errmsg("referenced relation \"%s\" is not a table",
    9978                 :                         RelationGetRelationName(pkrel))));
    9979 ECB             : 
    9980                 :     /*
    9981                 :      * Caller supplies us with a constraint name; however, it may be used in
    9982                 :      * this partition, so come up with a different one in that case.
    9983                 :      */
    9984 CBC        1202 :     if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
    9985                 :                              RelationGetRelid(rel),
    9986            1202 :                              fkconstraint->conname))
    9987 GIC         329 :         conname = ChooseConstraintName(RelationGetRelationName(rel),
    9988 CBC         329 :                                        ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
    9989                 :                                        "fkey",
    9990 GIC         329 :                                        RelationGetNamespace(rel), NIL);
    9991                 :     else
    9992             873 :         conname = fkconstraint->conname;
    9993 ECB             : 
    9994 CBC        1202 :     if (OidIsValid(parentConstr))
    9995 ECB             :     {
    9996 CBC         329 :         conislocal = false;
    9997 GIC         329 :         coninhcount = 1;
    9998             329 :         connoinherit = false;
    9999                 :     }
   10000 ECB             :     else
   10001                 :     {
   10002 CBC         873 :         conislocal = true;
   10003 GIC         873 :         coninhcount = 0;
   10004                 : 
   10005                 :         /*
   10006                 :          * always inherit for partitioned tables, never for legacy inheritance
   10007                 :          */
   10008             873 :         connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
   10009                 :     }
   10010                 : 
   10011                 :     /*
   10012                 :      * Record the FK constraint in pg_constraint.
   10013                 :      */
   10014            1202 :     constrOid = CreateConstraintEntry(conname,
   10015            1202 :                                       RelationGetNamespace(rel),
   10016                 :                                       CONSTRAINT_FOREIGN,
   10017 CBC        1202 :                                       fkconstraint->deferrable,
   10018 GBC        1202 :                                       fkconstraint->initdeferred,
   10019 GIC        1202 :                                       fkconstraint->initially_valid,
   10020                 :                                       parentConstr,
   10021                 :                                       RelationGetRelid(rel),
   10022                 :                                       fkattnum,
   10023                 :                                       numfks,
   10024                 :                                       numfks,
   10025                 :                                       InvalidOid,   /* not a domain constraint */
   10026 ECB             :                                       indexOid,
   10027                 :                                       RelationGetRelid(pkrel),
   10028                 :                                       pkattnum,
   10029                 :                                       pfeqoperators,
   10030                 :                                       ppeqoperators,
   10031                 :                                       ffeqoperators,
   10032                 :                                       numfks,
   10033 GIC        1202 :                                       fkconstraint->fk_upd_action,
   10034            1202 :                                       fkconstraint->fk_del_action,
   10035                 :                                       fkdelsetcols,
   10036                 :                                       numfkdelsetcols,
   10037            1202 :                                       fkconstraint->fk_matchtype,
   10038                 :                                       NULL, /* no exclusion constraint */
   10039                 :                                       NULL, /* no check constraint */
   10040                 :                                       NULL,
   10041                 :                                       conislocal,   /* islocal */
   10042                 :                                       coninhcount,  /* inhcount */
   10043                 :                                       connoinherit, /* conNoInherit */
   10044                 :                                       false);   /* is_internal */
   10045                 : 
   10046 CBC        1202 :     ObjectAddressSet(address, ConstraintRelationId, constrOid);
   10047 ECB             : 
   10048 EUB             :     /*
   10049 ECB             :      * Mark the child constraint as part of the parent constraint; it must not
   10050                 :      * be dropped on its own.  (This constraint is deleted when the partition
   10051                 :      * is detached, but a special check needs to occur that the partition
   10052                 :      * contains no referenced values.)
   10053                 :      */
   10054 GIC        1202 :     if (OidIsValid(parentConstr))
   10055                 :     {
   10056                 :         ObjectAddress referenced;
   10057                 : 
   10058             329 :         ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
   10059             329 :         recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
   10060                 :     }
   10061                 : 
   10062 ECB             :     /* make new constraint visible, in case we add more */
   10063 GBC        1202 :     CommandCounterIncrement();
   10064 ECB             : 
   10065                 :     /*
   10066                 :      * Create the action triggers that enforce the constraint.
   10067                 :      */
   10068 GIC        1202 :     createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
   10069                 :                                    fkconstraint,
   10070 ECB             :                                    constrOid, indexOid,
   10071                 :                                    parentDelTrigger, parentUpdTrigger,
   10072                 :                                    &deleteTriggerOid, &updateTriggerOid);
   10073                 : 
   10074 EUB             :     /*
   10075                 :      * If the referenced table is partitioned, recurse on ourselves to handle
   10076                 :      * each partition.  We need one pg_constraint row created for each
   10077                 :      * partition in addition to the pg_constraint row for the parent table.
   10078                 :      */
   10079 GIC        1202 :     if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10080                 :     {
   10081 CBC         178 :         PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
   10082                 : 
   10083             447 :         for (int i = 0; i < pd->nparts; i++)
   10084                 :         {
   10085 ECB             :             Relation    partRel;
   10086                 :             AttrMap    *map;
   10087                 :             AttrNumber *mapped_pkattnum;
   10088                 :             Oid         partIndexId;
   10089                 : 
   10090 GIC         269 :             partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
   10091                 : 
   10092                 :             /*
   10093                 :              * Map the attribute numbers in the referenced side of the FK
   10094 ECB             :              * definition to match the partition's column layout.
   10095                 :              */
   10096 GIC         269 :             map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
   10097                 :                                                RelationGetDescr(pkrel),
   10098                 :                                                false);
   10099 CBC         269 :             if (map)
   10100                 :             {
   10101 GIC          23 :                 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
   10102              46 :                 for (int j = 0; j < numfks; j++)
   10103              23 :                     mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
   10104                 :             }
   10105                 :             else
   10106             246 :                 mapped_pkattnum = pkattnum;
   10107                 : 
   10108                 :             /* do the deed */
   10109             269 :             partIndexId = index_get_partition(partRel, indexOid);
   10110             269 :             if (!OidIsValid(partIndexId))
   10111 UIC           0 :                 elog(ERROR, "index for %u not found in partition %s",
   10112                 :                      indexOid, RelationGetRelationName(partRel));
   10113 CBC         269 :             addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
   10114 ECB             :                                    partIndexId, constrOid, numfks,
   10115                 :                                    mapped_pkattnum, fkattnum,
   10116                 :                                    pfeqoperators, ppeqoperators, ffeqoperators,
   10117                 :                                    numfkdelsetcols, fkdelsetcols,
   10118                 :                                    old_check_ok,
   10119                 :                                    deleteTriggerOid, updateTriggerOid);
   10120                 : 
   10121                 :             /* Done -- clean up (but keep the lock) */
   10122 GIC         269 :             table_close(partRel, NoLock);
   10123             269 :             if (map)
   10124                 :             {
   10125 CBC          23 :                 pfree(mapped_pkattnum);
   10126              23 :                 free_attrmap(map);
   10127                 :             }
   10128                 :         }
   10129                 :     }
   10130                 : 
   10131 GIC        1202 :     return address;
   10132                 : }
   10133                 : 
   10134                 : /*
   10135                 :  * addFkRecurseReferencing
   10136                 :  *      subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
   10137 ECB             :  *
   10138                 :  * If the referencing relation is a plain relation, create the necessary check
   10139                 :  * triggers that implement the constraint, and set up for Phase 3 constraint
   10140                 :  * verification.  If the referencing relation is a partitioned table, then
   10141                 :  * we create a pg_constraint row for it and recurse on this routine for each
   10142                 :  * partition.
   10143                 :  *
   10144                 :  * We assume that the referenced relation is locked against concurrent
   10145                 :  * deletions.  If it's a partitioned relation, every partition must be so
   10146                 :  * locked.
   10147                 :  *
   10148                 :  * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
   10149                 :  * of an ALTER TABLE sequence.
   10150                 :  * fkconstraint is the constraint being added.
   10151                 :  * rel is the referencing relation; might be a partition, if recursing.
   10152                 :  * pkrel is the root referenced relation.
   10153                 :  * indexOid is the OID of the index (on pkrel) implementing this constraint.
   10154                 :  * parentConstr is the OID of the parent constraint (there is always one).
   10155                 :  * numfks is the number of columns in the foreign key
   10156                 :  * pkattnum is the attnum array of referenced attributes.
   10157                 :  * fkattnum is the attnum array of referencing attributes.
   10158                 :  * pf/pp/ffeqoperators are OID array of operators between columns.
   10159                 :  * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
   10160                 :  *      (...) clause
   10161                 :  * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
   10162                 :  *      NULL/DEFAULT clause
   10163                 :  * old_check_ok signals that this constraint replaces an existing one that
   10164                 :  *      was already validated (thus this one doesn't need validation).
   10165                 :  * lockmode is the lockmode to acquire on partitions when recursing.
   10166                 :  * parentInsTrigger and parentUpdTrigger, when being recursively called on
   10167                 :  * a partition, are the OIDs of the parent check triggers for INSERT and
   10168                 :  * UPDATE respectively.
   10169                 :  */
   10170                 : static void
   10171 GIC        1224 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
   10172                 :                         Relation pkrel, Oid indexOid, Oid parentConstr,
   10173                 :                         int numfks, int16 *pkattnum, int16 *fkattnum,
   10174                 :                         Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
   10175                 :                         int numfkdelsetcols, int16 *fkdelsetcols,
   10176                 :                         bool old_check_ok, LOCKMODE lockmode,
   10177                 :                         Oid parentInsTrigger, Oid parentUpdTrigger)
   10178                 : {
   10179                 :     Oid         insertTriggerOid,
   10180                 :                 updateTriggerOid;
   10181                 : 
   10182 GNC        1224 :     Assert(OidIsValid(parentConstr));
   10183                 : 
   10184 GIC        1224 :     if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   10185 UIC           0 :         ereport(ERROR,
   10186                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10187                 :                  errmsg("foreign key constraints are not supported on foreign tables")));
   10188                 : 
   10189                 :     /*
   10190                 :      * Add the check triggers to it and, if necessary, schedule it to be
   10191                 :      * checked in Phase 3.
   10192                 :      *
   10193                 :      * If the relation is partitioned, drill down to do it to its partitions.
   10194                 :      */
   10195 GIC        1224 :     createForeignKeyCheckTriggers(RelationGetRelid(rel),
   10196                 :                                   RelationGetRelid(pkrel),
   10197                 :                                   fkconstraint,
   10198                 :                                   parentConstr,
   10199                 :                                   indexOid,
   10200                 :                                   parentInsTrigger, parentUpdTrigger,
   10201                 :                                   &insertTriggerOid, &updateTriggerOid);
   10202                 : 
   10203            1224 :     if (rel->rd_rel->relkind == RELKIND_RELATION)
   10204 ECB             :     {
   10205                 :         /*
   10206                 :          * Tell Phase 3 to check that the constraint is satisfied by existing
   10207                 :          * rows. We can skip this during table creation, when requested
   10208                 :          * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
   10209                 :          * and when we're recreating a constraint following a SET DATA TYPE
   10210                 :          * operation that did not impugn its validity.
   10211                 :          */
   10212 CBC        1029 :         if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
   10213                 :         {
   10214                 :             NewConstraint *newcon;
   10215                 :             AlteredTableInfo *tab;
   10216                 : 
   10217 GIC         320 :             tab = ATGetQueueEntry(wqueue, rel);
   10218                 : 
   10219 CBC         320 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
   10220 GIC         320 :             newcon->name = get_constraint_name(parentConstr);
   10221             320 :             newcon->contype = CONSTR_FOREIGN;
   10222             320 :             newcon->refrelid = RelationGetRelid(pkrel);
   10223             320 :             newcon->refindid = indexOid;
   10224             320 :             newcon->conid = parentConstr;
   10225             320 :             newcon->qual = (Node *) fkconstraint;
   10226                 : 
   10227             320 :             tab->constraints = lappend(tab->constraints, newcon);
   10228                 :         }
   10229                 :     }
   10230             195 :     else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10231                 :     {
   10232             195 :         PartitionDesc pd = RelationGetPartitionDesc(rel, true);
   10233                 :         Relation    trigrel;
   10234 ECB             : 
   10235                 :         /*
   10236                 :          * Triggers of the foreign keys will be manipulated a bunch of times
   10237                 :          * in the loop below.  To avoid repeatedly opening/closing the trigger
   10238                 :          * catalog relation, we open it here and pass it to the subroutines
   10239                 :          * called below.
   10240                 :          */
   10241 GIC         195 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   10242                 : 
   10243                 :         /*
   10244                 :          * Recurse to take appropriate action on each partition; either we
   10245                 :          * find an existing constraint to reparent to ours, or we create a new
   10246                 :          * one.
   10247                 :          */
   10248             377 :         for (int i = 0; i < pd->nparts; i++)
   10249                 :         {
   10250             182 :             Oid         partitionId = pd->oids[i];
   10251             182 :             Relation    partition = table_open(partitionId, lockmode);
   10252 ECB             :             List       *partFKs;
   10253                 :             AttrMap    *attmap;
   10254                 :             AttrNumber  mapped_fkattnum[INDEX_MAX_KEYS];
   10255                 :             bool        attached;
   10256                 :             char       *conname;
   10257                 :             Oid         constrOid;
   10258                 :             ObjectAddress address,
   10259                 :                         referenced;
   10260                 :             ListCell   *cell;
   10261                 : 
   10262 GIC         182 :             CheckTableNotInUse(partition, "ALTER TABLE");
   10263 ECB             : 
   10264 GIC         182 :             attmap = build_attrmap_by_name(RelationGetDescr(partition),
   10265                 :                                            RelationGetDescr(rel),
   10266                 :                                            false);
   10267             466 :             for (int j = 0; j < numfks; j++)
   10268 CBC         284 :                 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
   10269                 : 
   10270 ECB             :             /* Check whether an existing constraint can be repurposed */
   10271 CBC         182 :             partFKs = copyObject(RelationGetFKeyList(partition));
   10272 GIC         182 :             attached = false;
   10273 CBC         191 :             foreach(cell, partFKs)
   10274                 :             {
   10275 ECB             :                 ForeignKeyCacheInfo *fk;
   10276                 : 
   10277 CBC          15 :                 fk = lfirst_node(ForeignKeyCacheInfo, cell);
   10278              15 :                 if (tryAttachPartitionForeignKey(fk,
   10279                 :                                                  partitionId,
   10280                 :                                                  parentConstr,
   10281                 :                                                  numfks,
   10282 ECB             :                                                  mapped_fkattnum,
   10283                 :                                                  pkattnum,
   10284                 :                                                  pfeqoperators,
   10285                 :                                                  insertTriggerOid,
   10286                 :                                                  updateTriggerOid,
   10287                 :                                                  trigrel))
   10288                 :                 {
   10289 GIC           6 :                     attached = true;
   10290               6 :                     break;
   10291 ECB             :                 }
   10292                 :             }
   10293 GIC         182 :             if (attached)
   10294                 :             {
   10295               6 :                 table_close(partition, NoLock);
   10296               6 :                 continue;
   10297                 :             }
   10298                 : 
   10299                 :             /*
   10300                 :              * No luck finding a good constraint to reuse; create our own.
   10301                 :              */
   10302             176 :             if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
   10303                 :                                      RelationGetRelid(partition),
   10304             176 :                                      fkconstraint->conname))
   10305 UIC           0 :                 conname = ChooseConstraintName(RelationGetRelationName(partition),
   10306               0 :                                                ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
   10307                 :                                                "fkey",
   10308               0 :                                                RelationGetNamespace(partition), NIL);
   10309                 :             else
   10310 GIC         176 :                 conname = fkconstraint->conname;
   10311                 :             constrOid =
   10312             176 :                 CreateConstraintEntry(conname,
   10313             176 :                                       RelationGetNamespace(partition),
   10314                 :                                       CONSTRAINT_FOREIGN,
   10315             176 :                                       fkconstraint->deferrable,
   10316             176 :                                       fkconstraint->initdeferred,
   10317             176 :                                       fkconstraint->initially_valid,
   10318                 :                                       parentConstr,
   10319                 :                                       partitionId,
   10320                 :                                       mapped_fkattnum,
   10321                 :                                       numfks,
   10322                 :                                       numfks,
   10323                 :                                       InvalidOid,
   10324                 :                                       indexOid,
   10325                 :                                       RelationGetRelid(pkrel),
   10326 ECB             :                                       pkattnum,
   10327                 :                                       pfeqoperators,
   10328                 :                                       ppeqoperators,
   10329                 :                                       ffeqoperators,
   10330                 :                                       numfks,
   10331 GIC         176 :                                       fkconstraint->fk_upd_action,
   10332             176 :                                       fkconstraint->fk_del_action,
   10333                 :                                       fkdelsetcols,
   10334                 :                                       numfkdelsetcols,
   10335             176 :                                       fkconstraint->fk_matchtype,
   10336                 :                                       NULL,
   10337                 :                                       NULL,
   10338                 :                                       NULL,
   10339                 :                                       false,
   10340                 :                                       1,
   10341                 :                                       false,
   10342                 :                                       false);
   10343                 : 
   10344                 :             /*
   10345                 :              * Give this constraint partition-type dependencies on the parent
   10346                 :              * constraint as well as the table.
   10347                 :              */
   10348 CBC         176 :             ObjectAddressSet(address, ConstraintRelationId, constrOid);
   10349             176 :             ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
   10350 GBC         176 :             recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
   10351 GIC         176 :             ObjectAddressSet(referenced, RelationRelationId, partitionId);
   10352             176 :             recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
   10353                 : 
   10354                 :             /* Make all this visible before recursing */
   10355             176 :             CommandCounterIncrement();
   10356                 : 
   10357                 :             /* call ourselves to finalize the creation and we're done */
   10358             176 :             addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
   10359 ECB             :                                     indexOid,
   10360                 :                                     constrOid,
   10361                 :                                     numfks,
   10362                 :                                     pkattnum,
   10363                 :                                     mapped_fkattnum,
   10364                 :                                     pfeqoperators,
   10365                 :                                     ppeqoperators,
   10366                 :                                     ffeqoperators,
   10367                 :                                     numfkdelsetcols,
   10368                 :                                     fkdelsetcols,
   10369                 :                                     old_check_ok,
   10370                 :                                     lockmode,
   10371                 :                                     insertTriggerOid,
   10372                 :                                     updateTriggerOid);
   10373                 : 
   10374 GIC         176 :             table_close(partition, NoLock);
   10375                 :         }
   10376                 : 
   10377 CBC         195 :         table_close(trigrel, RowExclusiveLock);
   10378 ECB             :     }
   10379 GIC        1224 : }
   10380                 : 
   10381                 : /*
   10382                 :  * CloneForeignKeyConstraints
   10383 ECB             :  *      Clone foreign keys from a partitioned table to a newly acquired
   10384                 :  *      partition.
   10385                 :  *
   10386                 :  * partitionRel is a partition of parentRel, so we can be certain that it has
   10387                 :  * the same columns with the same datatypes.  The columns may be in different
   10388                 :  * order, though.
   10389                 :  *
   10390                 :  * wqueue must be passed to set up phase 3 constraint checking, unless the
   10391                 :  * referencing-side partition is known to be empty (such as in CREATE TABLE /
   10392                 :  * PARTITION OF).
   10393                 :  */
   10394                 : static void
   10395 GIC        4312 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
   10396                 :                            Relation partitionRel)
   10397                 : {
   10398                 :     /* This only works for declarative partitioning */
   10399            4312 :     Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   10400                 : 
   10401                 :     /*
   10402                 :      * Clone constraints for which the parent is on the referenced side.
   10403                 :      */
   10404            4312 :     CloneFkReferenced(parentRel, partitionRel);
   10405                 : 
   10406                 :     /*
   10407                 :      * Now clone constraints where the parent is on the referencing side.
   10408 ECB             :      */
   10409 CBC        4312 :     CloneFkReferencing(wqueue, parentRel, partitionRel);
   10410 GIC        4312 : }
   10411                 : 
   10412 ECB             : /*
   10413                 :  * CloneFkReferenced
   10414                 :  *      Subroutine for CloneForeignKeyConstraints
   10415                 :  *
   10416                 :  * Find all the FKs that have the parent relation on the referenced side;
   10417                 :  * clone those constraints to the given partition.  This is to be called
   10418                 :  * when the partition is being created or attached.
   10419                 :  *
   10420                 :  * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
   10421                 :  *
   10422                 :  * This recurses to partitions, if the relation being attached is partitioned.
   10423                 :  * Recursion is done by calling addFkRecurseReferenced.
   10424                 :  */
   10425                 : static void
   10426 GIC        4312 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
   10427                 : {
   10428                 :     Relation    pg_constraint;
   10429 ECB             :     AttrMap    *attmap;
   10430                 :     ListCell   *cell;
   10431                 :     SysScanDesc scan;
   10432                 :     ScanKeyData key[2];
   10433                 :     HeapTuple   tuple;
   10434 CBC        4312 :     List       *clone = NIL;
   10435                 :     Relation    trigrel;
   10436                 : 
   10437                 :     /*
   10438 ECB             :      * Search for any constraints where this partition's parent is in the
   10439                 :      * referenced side.  However, we must not clone any constraint whose
   10440                 :      * parent constraint is also going to be cloned, to avoid duplicates.  So
   10441                 :      * do it in two steps: first construct the list of constraints to clone,
   10442                 :      * then go over that list cloning those whose parents are not in the list.
   10443                 :      * (We must not rely on the parent being seen first, since the catalog
   10444                 :      * scan could return children first.)
   10445                 :      */
   10446 GIC        4312 :     pg_constraint = table_open(ConstraintRelationId, RowShareLock);
   10447            4312 :     ScanKeyInit(&key[0],
   10448                 :                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   10449                 :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
   10450            4312 :     ScanKeyInit(&key[1],
   10451                 :                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   10452                 :                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   10453                 :     /* This is a seqscan, as we don't have a usable index ... */
   10454 CBC        4312 :     scan = systable_beginscan(pg_constraint, InvalidOid, true,
   10455                 :                               NULL, 2, key);
   10456            4462 :     while ((tuple = systable_getnext(scan)) != NULL)
   10457                 :     {
   10458             150 :         Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   10459                 : 
   10460 GIC         150 :         clone = lappend_oid(clone, constrForm->oid);
   10461                 :     }
   10462            4312 :     systable_endscan(scan);
   10463            4312 :     table_close(pg_constraint, RowShareLock);
   10464                 : 
   10465 ECB             :     /*
   10466                 :      * Triggers of the foreign keys will be manipulated a bunch of times in
   10467                 :      * the loop below.  To avoid repeatedly opening/closing the trigger
   10468                 :      * catalog relation, we open it here and pass it to the subroutines called
   10469                 :      * below.
   10470                 :      */
   10471 CBC        4312 :     trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   10472                 : 
   10473 GIC        4312 :     attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
   10474                 :                                    RelationGetDescr(parentRel),
   10475                 :                                    false);
   10476            4462 :     foreach(cell, clone)
   10477 ECB             :     {
   10478 CBC         150 :         Oid         constrOid = lfirst_oid(cell);
   10479 ECB             :         Form_pg_constraint constrForm;
   10480                 :         Relation    fkRel;
   10481                 :         Oid         indexOid;
   10482                 :         Oid         partIndexId;
   10483                 :         int         numfks;
   10484                 :         AttrNumber  conkey[INDEX_MAX_KEYS];
   10485                 :         AttrNumber  mapped_confkey[INDEX_MAX_KEYS];
   10486                 :         AttrNumber  confkey[INDEX_MAX_KEYS];
   10487 EUB             :         Oid         conpfeqop[INDEX_MAX_KEYS];
   10488                 :         Oid         conppeqop[INDEX_MAX_KEYS];
   10489 ECB             :         Oid         conffeqop[INDEX_MAX_KEYS];
   10490                 :         int         numfkdelsetcols;
   10491                 :         AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   10492                 :         Constraint *fkconstraint;
   10493                 :         Oid         deleteTriggerOid,
   10494                 :                     updateTriggerOid;
   10495                 : 
   10496 GIC         150 :         tuple = SearchSysCache1(CONSTROID, constrOid);
   10497             150 :         if (!HeapTupleIsValid(tuple))
   10498 LBC           0 :             elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   10499 CBC         150 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   10500                 : 
   10501 ECB             :         /*
   10502                 :          * As explained above: don't try to clone a constraint for which we're
   10503                 :          * going to clone the parent.
   10504                 :          */
   10505 GIC         150 :         if (list_member_oid(clone, constrForm->conparentid))
   10506                 :         {
   10507 CBC          63 :             ReleaseSysCache(tuple);
   10508 GIC          90 :             continue;
   10509                 :         }
   10510                 : 
   10511                 :         /*
   10512                 :          * Don't clone self-referencing foreign keys, which can be in the
   10513                 :          * partitioned table or in the partition-to-be.
   10514                 :          */
   10515              87 :         if (constrForm->conrelid == RelationGetRelid(parentRel) ||
   10516              66 :             constrForm->conrelid == RelationGetRelid(partitionRel))
   10517                 :         {
   10518              27 :             ReleaseSysCache(tuple);
   10519              27 :             continue;
   10520                 :         }
   10521                 : 
   10522                 :         /*
   10523                 :          * Because we're only expanding the key space at the referenced side,
   10524                 :          * we don't need to prevent any operation in the referencing table, so
   10525                 :          * AccessShareLock suffices (assumes that dropping the constraint
   10526                 :          * acquires AEL).
   10527                 :          */
   10528              60 :         fkRel = table_open(constrForm->conrelid, AccessShareLock);
   10529                 : 
   10530              60 :         indexOid = constrForm->conindid;
   10531              60 :         DeconstructFkConstraintRow(tuple,
   10532                 :                                    &numfks,
   10533                 :                                    conkey,
   10534                 :                                    confkey,
   10535                 :                                    conpfeqop,
   10536                 :                                    conppeqop,
   10537                 :                                    conffeqop,
   10538                 :                                    &numfkdelsetcols,
   10539                 :                                    confdelsetcols);
   10540                 : 
   10541             120 :         for (int i = 0; i < numfks; i++)
   10542              60 :             mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
   10543                 : 
   10544              60 :         fkconstraint = makeNode(Constraint);
   10545              60 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   10546              60 :         fkconstraint->conname = NameStr(constrForm->conname);
   10547 CBC          60 :         fkconstraint->deferrable = constrForm->condeferrable;
   10548 GIC          60 :         fkconstraint->initdeferred = constrForm->condeferred;
   10549              60 :         fkconstraint->location = -1;
   10550              60 :         fkconstraint->pktable = NULL;
   10551                 :         /* ->fk_attrs determined below */
   10552              60 :         fkconstraint->pk_attrs = NIL;
   10553              60 :         fkconstraint->fk_matchtype = constrForm->confmatchtype;
   10554              60 :         fkconstraint->fk_upd_action = constrForm->confupdtype;
   10555              60 :         fkconstraint->fk_del_action = constrForm->confdeltype;
   10556              60 :         fkconstraint->fk_del_set_cols = NIL;
   10557              60 :         fkconstraint->old_conpfeqop = NIL;
   10558 CBC          60 :         fkconstraint->old_pktable_oid = InvalidOid;
   10559 GIC          60 :         fkconstraint->skip_validation = false;
   10560 CBC          60 :         fkconstraint->initially_valid = true;
   10561 EUB             : 
   10562                 :         /* set up colnames that are used to generate the constraint name */
   10563 GIC         120 :         for (int i = 0; i < numfks; i++)
   10564                 :         {
   10565                 :             Form_pg_attribute att;
   10566                 : 
   10567              60 :             att = TupleDescAttr(RelationGetDescr(fkRel),
   10568                 :                                 conkey[i] - 1);
   10569              60 :             fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   10570              60 :                                              makeString(NameStr(att->attname)));
   10571 ECB             :         }
   10572                 : 
   10573                 :         /*
   10574                 :          * Add the new foreign key constraint pointing to the new partition.
   10575                 :          * Because this new partition appears in the referenced side of the
   10576                 :          * constraint, we don't need to set up for Phase 3 check.
   10577                 :          */
   10578 GIC          60 :         partIndexId = index_get_partition(partitionRel, indexOid);
   10579 CBC          60 :         if (!OidIsValid(partIndexId))
   10580 UIC           0 :             elog(ERROR, "index for %u not found in partition %s",
   10581                 :                  indexOid, RelationGetRelationName(partitionRel));
   10582                 : 
   10583                 :         /*
   10584                 :          * Get the "action" triggers belonging to the constraint to pass as
   10585                 :          * parent OIDs for similar triggers that will be created on the
   10586                 :          * partition in addFkRecurseReferenced().
   10587                 :          */
   10588 CBC          60 :         GetForeignKeyActionTriggers(trigrel, constrOid,
   10589                 :                                     constrForm->confrelid, constrForm->conrelid,
   10590                 :                                     &deleteTriggerOid, &updateTriggerOid);
   10591                 : 
   10592 GIC          60 :         addFkRecurseReferenced(NULL,
   10593 ECB             :                                fkconstraint,
   10594                 :                                fkRel,
   10595                 :                                partitionRel,
   10596                 :                                partIndexId,
   10597                 :                                constrOid,
   10598                 :                                numfks,
   10599                 :                                mapped_confkey,
   10600                 :                                conkey,
   10601                 :                                conpfeqop,
   10602                 :                                conppeqop,
   10603                 :                                conffeqop,
   10604                 :                                numfkdelsetcols,
   10605                 :                                confdelsetcols,
   10606                 :                                true,
   10607                 :                                deleteTriggerOid,
   10608                 :                                updateTriggerOid);
   10609                 : 
   10610 GIC          60 :         table_close(fkRel, NoLock);
   10611              60 :         ReleaseSysCache(tuple);
   10612                 :     }
   10613                 : 
   10614            4312 :     table_close(trigrel, RowExclusiveLock);
   10615            4312 : }
   10616                 : 
   10617 ECB             : /*
   10618                 :  * CloneFkReferencing
   10619                 :  *      Subroutine for CloneForeignKeyConstraints
   10620                 :  *
   10621                 :  * For each FK constraint of the parent relation in the given list, find an
   10622                 :  * equivalent constraint in its partition relation that can be reparented;
   10623                 :  * if one cannot be found, create a new constraint in the partition as its
   10624                 :  * child.
   10625                 :  *
   10626                 :  * If wqueue is given, it is used to set up phase-3 verification for each
   10627                 :  * cloned constraint; if omitted, we assume that such verification is not
   10628                 :  * needed (example: the partition is being created anew).
   10629                 :  */
   10630                 : static void
   10631 GIC        4312 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
   10632                 : {
   10633                 :     AttrMap    *attmap;
   10634                 :     List       *partFKs;
   10635            4312 :     List       *clone = NIL;
   10636                 :     ListCell   *cell;
   10637                 :     Relation    trigrel;
   10638 ECB             : 
   10639                 :     /* obtain a list of constraints that we need to clone */
   10640 CBC        4720 :     foreach(cell, RelationGetFKeyList(parentRel))
   10641                 :     {
   10642 GIC         408 :         ForeignKeyCacheInfo *fk = lfirst(cell);
   10643 ECB             : 
   10644 CBC         408 :         clone = lappend_oid(clone, fk->conoid);
   10645                 :     }
   10646                 : 
   10647 ECB             :     /*
   10648                 :      * Silently do nothing if there's nothing to do.  In particular, this
   10649                 :      * avoids throwing a spurious error for foreign tables.
   10650                 :      */
   10651 GIC        4312 :     if (clone == NIL)
   10652            4116 :         return;
   10653 ECB             : 
   10654 CBC         196 :     if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   10655 UIC           0 :         ereport(ERROR,
   10656                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10657                 :                  errmsg("foreign key constraints are not supported on foreign tables")));
   10658                 : 
   10659                 :     /*
   10660                 :      * Triggers of the foreign keys will be manipulated a bunch of times in
   10661                 :      * the loop below.  To avoid repeatedly opening/closing the trigger
   10662                 :      * catalog relation, we open it here and pass it to the subroutines called
   10663                 :      * below.
   10664                 :      */
   10665 CBC         196 :     trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   10666 ECB             : 
   10667                 :     /*
   10668                 :      * The constraint key may differ, if the columns in the partition are
   10669                 :      * different.  This map is used to convert them.
   10670                 :      */
   10671 CBC         196 :     attmap = build_attrmap_by_name(RelationGetDescr(partRel),
   10672                 :                                    RelationGetDescr(parentRel),
   10673                 :                                    false);
   10674                 : 
   10675 GIC         196 :     partFKs = copyObject(RelationGetFKeyList(partRel));
   10676                 : 
   10677             604 :     foreach(cell, clone)
   10678                 :     {
   10679 CBC         408 :         Oid         parentConstrOid = lfirst_oid(cell);
   10680                 :         Form_pg_constraint constrForm;
   10681 ECB             :         Relation    pkrel;
   10682 EUB             :         HeapTuple   tuple;
   10683                 :         int         numfks;
   10684                 :         AttrNumber  conkey[INDEX_MAX_KEYS];
   10685                 :         AttrNumber  mapped_conkey[INDEX_MAX_KEYS];
   10686                 :         AttrNumber  confkey[INDEX_MAX_KEYS];
   10687 ECB             :         Oid         conpfeqop[INDEX_MAX_KEYS];
   10688                 :         Oid         conppeqop[INDEX_MAX_KEYS];
   10689                 :         Oid         conffeqop[INDEX_MAX_KEYS];
   10690                 :         int         numfkdelsetcols;
   10691                 :         AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   10692                 :         Constraint *fkconstraint;
   10693                 :         bool        attached;
   10694                 :         Oid         indexOid;
   10695                 :         Oid         constrOid;
   10696                 :         ObjectAddress address,
   10697                 :                     referenced;
   10698                 :         ListCell   *lc;
   10699                 :         Oid         insertTriggerOid,
   10700                 :                     updateTriggerOid;
   10701                 : 
   10702 GIC         408 :         tuple = SearchSysCache1(CONSTROID, parentConstrOid);
   10703             408 :         if (!HeapTupleIsValid(tuple))
   10704 UIC           0 :             elog(ERROR, "cache lookup failed for constraint %u",
   10705                 :                  parentConstrOid);
   10706 GIC         408 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   10707                 : 
   10708 ECB             :         /* Don't clone constraints whose parents are being cloned */
   10709 CBC         408 :         if (list_member_oid(clone, constrForm->conparentid))
   10710                 :         {
   10711 GIC         200 :             ReleaseSysCache(tuple);
   10712 CBC         233 :             continue;
   10713                 :         }
   10714                 : 
   10715                 :         /*
   10716                 :          * Need to prevent concurrent deletions.  If pkrel is a partitioned
   10717                 :          * relation, that means to lock all partitions.
   10718                 :          */
   10719 GIC         208 :         pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
   10720             208 :         if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10721              86 :             (void) find_all_inheritors(RelationGetRelid(pkrel),
   10722                 :                                        ShareRowExclusiveLock, NULL);
   10723                 : 
   10724             208 :         DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
   10725 ECB             :                                    conpfeqop, conppeqop, conffeqop,
   10726                 :                                    &numfkdelsetcols, confdelsetcols);
   10727 CBC         479 :         for (int i = 0; i < numfks; i++)
   10728             271 :             mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
   10729 ECB             : 
   10730                 :         /*
   10731                 :          * Get the "check" triggers belonging to the constraint to pass as
   10732                 :          * parent OIDs for similar triggers that will be created on the
   10733                 :          * partition in addFkRecurseReferencing().  They are also passed to
   10734                 :          * tryAttachPartitionForeignKey() below to simply assign as parents to
   10735                 :          * the partition's existing "check" triggers, that is, if the
   10736                 :          * corresponding constraints is deemed attachable to the parent
   10737                 :          * constraint.
   10738                 :          */
   10739 GIC         208 :         GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
   10740                 :                                    constrForm->confrelid, constrForm->conrelid,
   10741                 :                                    &insertTriggerOid, &updateTriggerOid);
   10742                 : 
   10743                 :         /*
   10744                 :          * Before creating a new constraint, see whether any existing FKs are
   10745                 :          * fit for the purpose.  If one is, attach the parent constraint to
   10746                 :          * it, and don't clone anything.  This way we avoid the expensive
   10747                 :          * verification step and don't end up with a duplicate FK, and we
   10748                 :          * don't need to recurse to partitions for this constraint.
   10749                 :          */
   10750             208 :         attached = false;
   10751 GNC         250 :         foreach(lc, partFKs)
   10752                 :         {
   10753              75 :             ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
   10754 ECB             : 
   10755 GIC          75 :             if (tryAttachPartitionForeignKey(fk,
   10756 ECB             :                                              RelationGetRelid(partRel),
   10757                 :                                              parentConstrOid,
   10758                 :                                              numfks,
   10759                 :                                              mapped_conkey,
   10760                 :                                              confkey,
   10761                 :                                              conpfeqop,
   10762                 :                                              insertTriggerOid,
   10763                 :                                              updateTriggerOid,
   10764                 :                                              trigrel))
   10765                 :             {
   10766 GIC          33 :                 attached = true;
   10767              33 :                 table_close(pkrel, NoLock);
   10768              33 :                 break;
   10769                 :             }
   10770                 :         }
   10771             208 :         if (attached)
   10772 ECB             :         {
   10773 GIC          33 :             ReleaseSysCache(tuple);
   10774              33 :             continue;
   10775                 :         }
   10776 ECB             : 
   10777                 :         /* No dice.  Set up to create our own constraint */
   10778 GIC         175 :         fkconstraint = makeNode(Constraint);
   10779             175 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   10780                 :         /* ->conname determined below */
   10781 CBC         175 :         fkconstraint->deferrable = constrForm->condeferrable;
   10782 GIC         175 :         fkconstraint->initdeferred = constrForm->condeferred;
   10783             175 :         fkconstraint->location = -1;
   10784             175 :         fkconstraint->pktable = NULL;
   10785                 :         /* ->fk_attrs determined below */
   10786 CBC         175 :         fkconstraint->pk_attrs = NIL;
   10787             175 :         fkconstraint->fk_matchtype = constrForm->confmatchtype;
   10788 GIC         175 :         fkconstraint->fk_upd_action = constrForm->confupdtype;
   10789             175 :         fkconstraint->fk_del_action = constrForm->confdeltype;
   10790             175 :         fkconstraint->fk_del_set_cols = NIL;
   10791             175 :         fkconstraint->old_conpfeqop = NIL;
   10792             175 :         fkconstraint->old_pktable_oid = InvalidOid;
   10793             175 :         fkconstraint->skip_validation = false;
   10794             175 :         fkconstraint->initially_valid = true;
   10795             389 :         for (int i = 0; i < numfks; i++)
   10796                 :         {
   10797                 :             Form_pg_attribute att;
   10798                 : 
   10799             214 :             att = TupleDescAttr(RelationGetDescr(partRel),
   10800                 :                                 mapped_conkey[i] - 1);
   10801             214 :             fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   10802             214 :                                              makeString(NameStr(att->attname)));
   10803 ECB             :         }
   10804 GIC         175 :         if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
   10805                 :                                  RelationGetRelid(partRel),
   10806             175 :                                  NameStr(constrForm->conname)))
   10807               3 :             fkconstraint->conname =
   10808               3 :                 ChooseConstraintName(RelationGetRelationName(partRel),
   10809               3 :                                      ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
   10810                 :                                      "fkey",
   10811 CBC           3 :                                      RelationGetNamespace(partRel), NIL);
   10812                 :         else
   10813 GIC         172 :             fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
   10814                 : 
   10815             175 :         indexOid = constrForm->conindid;
   10816                 :         constrOid =
   10817             175 :             CreateConstraintEntry(fkconstraint->conname,
   10818                 :                                   constrForm->connamespace,
   10819                 :                                   CONSTRAINT_FOREIGN,
   10820             175 :                                   fkconstraint->deferrable,
   10821             175 :                                   fkconstraint->initdeferred,
   10822             175 :                                   constrForm->convalidated,
   10823 ECB             :                                   parentConstrOid,
   10824                 :                                   RelationGetRelid(partRel),
   10825                 :                                   mapped_conkey,
   10826                 :                                   numfks,
   10827                 :                                   numfks,
   10828                 :                                   InvalidOid,   /* not a domain constraint */
   10829                 :                                   indexOid,
   10830                 :                                   constrForm->confrelid, /* same foreign rel */
   10831                 :                                   confkey,
   10832                 :                                   conpfeqop,
   10833                 :                                   conppeqop,
   10834                 :                                   conffeqop,
   10835                 :                                   numfks,
   10836 GIC         175 :                                   fkconstraint->fk_upd_action,
   10837 CBC         175 :                                   fkconstraint->fk_del_action,
   10838                 :                                   confdelsetcols,
   10839 ECB             :                                   numfkdelsetcols,
   10840 CBC         175 :                                   fkconstraint->fk_matchtype,
   10841                 :                                   NULL,
   10842                 :                                   NULL,
   10843                 :                                   NULL,
   10844                 :                                   false,    /* islocal */
   10845                 :                                   1,    /* inhcount */
   10846                 :                                   false,    /* conNoInherit */
   10847                 :                                   true);
   10848 ECB             : 
   10849                 :         /* Set up partition dependencies for the new constraint */
   10850 CBC         175 :         ObjectAddressSet(address, ConstraintRelationId, constrOid);
   10851 GIC         175 :         ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
   10852             175 :         recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
   10853 CBC         175 :         ObjectAddressSet(referenced, RelationRelationId,
   10854                 :                          RelationGetRelid(partRel));
   10855             175 :         recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
   10856                 : 
   10857                 :         /* Done with the cloned constraint's tuple */
   10858 GIC         175 :         ReleaseSysCache(tuple);
   10859                 : 
   10860                 :         /* Make all this visible before recursing */
   10861             175 :         CommandCounterIncrement();
   10862                 : 
   10863             175 :         addFkRecurseReferencing(wqueue,
   10864                 :                                 fkconstraint,
   10865                 :                                 partRel,
   10866                 :                                 pkrel,
   10867                 :                                 indexOid,
   10868                 :                                 constrOid,
   10869                 :                                 numfks,
   10870                 :                                 confkey,
   10871                 :                                 mapped_conkey,
   10872                 :                                 conpfeqop,
   10873 ECB             :                                 conppeqop,
   10874                 :                                 conffeqop,
   10875 EUB             :                                 numfkdelsetcols,
   10876 ECB             :                                 confdelsetcols,
   10877                 :                                 false,  /* no old check exists */
   10878                 :                                 AccessExclusiveLock,
   10879                 :                                 insertTriggerOid,
   10880                 :                                 updateTriggerOid);
   10881 GIC         175 :         table_close(pkrel, NoLock);
   10882 ECB             :     }
   10883                 : 
   10884 CBC         196 :     table_close(trigrel, RowExclusiveLock);
   10885 ECB             : }
   10886                 : 
   10887                 : /*
   10888                 :  * When the parent of a partition receives [the referencing side of] a foreign
   10889                 :  * key, we must propagate that foreign key to the partition.  However, the
   10890                 :  * partition might already have an equivalent foreign key; this routine
   10891                 :  * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
   10892                 :  * by the other parameters.  If they are equivalent, create the link between
   10893                 :  * the two constraints and return true.
   10894                 :  *
   10895                 :  * If the given FK does not match the one defined by rest of the params,
   10896                 :  * return false.
   10897                 :  */
   10898                 : static bool
   10899 GIC          90 : tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
   10900                 :                              Oid partRelid,
   10901                 :                              Oid parentConstrOid,
   10902                 :                              int numfks,
   10903                 :                              AttrNumber *mapped_conkey,
   10904                 :                              AttrNumber *confkey,
   10905 ECB             :                              Oid *conpfeqop,
   10906                 :                              Oid parentInsTrigger,
   10907                 :                              Oid parentUpdTrigger,
   10908                 :                              Relation trigrel)
   10909                 : {
   10910                 :     HeapTuple   parentConstrTup;
   10911                 :     Form_pg_constraint parentConstr;
   10912                 :     HeapTuple   partcontup;
   10913                 :     Form_pg_constraint partConstr;
   10914                 :     ScanKeyData key;
   10915                 :     SysScanDesc scan;
   10916                 :     HeapTuple   trigtup;
   10917                 :     Oid         insertTriggerOid,
   10918                 :                 updateTriggerOid;
   10919                 : 
   10920 GIC          90 :     parentConstrTup = SearchSysCache1(CONSTROID,
   10921 ECB             :                                       ObjectIdGetDatum(parentConstrOid));
   10922 CBC          90 :     if (!HeapTupleIsValid(parentConstrTup))
   10923 LBC           0 :         elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
   10924 CBC          90 :     parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
   10925 ECB             : 
   10926                 :     /*
   10927                 :      * Do some quick & easy initial checks.  If any of these fail, we cannot
   10928                 :      * use this constraint.
   10929                 :      */
   10930 CBC          90 :     if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
   10931 ECB             :     {
   10932 LBC           0 :         ReleaseSysCache(parentConstrTup);
   10933               0 :         return false;
   10934 ECB             :     }
   10935 CBC         255 :     for (int i = 0; i < numfks; i++)
   10936 ECB             :     {
   10937 CBC         165 :         if (fk->conkey[i] != mapped_conkey[i] ||
   10938 GIC         165 :             fk->confkey[i] != confkey[i] ||
   10939             165 :             fk->conpfeqop[i] != conpfeqop[i])
   10940 ECB             :         {
   10941 UIC           0 :             ReleaseSysCache(parentConstrTup);
   10942               0 :             return false;
   10943                 :         }
   10944 ECB             :     }
   10945                 : 
   10946                 :     /*
   10947                 :      * Looks good so far; do some more extensive checks.  Presumably the check
   10948                 :      * for 'convalidated' could be dropped, since we don't really care about
   10949                 :      * that, but let's be careful for now.
   10950                 :      */
   10951 GIC          90 :     partcontup = SearchSysCache1(CONSTROID,
   10952                 :                                  ObjectIdGetDatum(fk->conoid));
   10953              90 :     if (!HeapTupleIsValid(partcontup))
   10954 UIC           0 :         elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   10955 CBC          90 :     partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
   10956              90 :     if (OidIsValid(partConstr->conparentid) ||
   10957 GBC          78 :         !partConstr->convalidated ||
   10958 GIC          78 :         partConstr->condeferrable != parentConstr->condeferrable ||
   10959              64 :         partConstr->condeferred != parentConstr->condeferred ||
   10960              64 :         partConstr->confupdtype != parentConstr->confupdtype ||
   10961              46 :         partConstr->confdeltype != parentConstr->confdeltype ||
   10962              46 :         partConstr->confmatchtype != parentConstr->confmatchtype)
   10963                 :     {
   10964              51 :         ReleaseSysCache(parentConstrTup);
   10965 CBC          51 :         ReleaseSysCache(partcontup);
   10966 GIC          51 :         return false;
   10967                 :     }
   10968                 : 
   10969 CBC          39 :     ReleaseSysCache(partcontup);
   10970 GIC          39 :     ReleaseSysCache(parentConstrTup);
   10971                 : 
   10972                 :     /*
   10973                 :      * Looks good!  Attach this constraint.  The action triggers in the new
   10974                 :      * partition become redundant -- the parent table already has equivalent
   10975                 :      * ones, and those will be able to reach the partition.  Remove the ones
   10976                 :      * in the partition.  We identify them because they have our constraint
   10977                 :      * OID, as well as being on the referenced rel.
   10978                 :      */
   10979              39 :     ScanKeyInit(&key,
   10980                 :                 Anum_pg_trigger_tgconstraint,
   10981                 :                 BTEqualStrategyNumber, F_OIDEQ,
   10982                 :                 ObjectIdGetDatum(fk->conoid));
   10983              39 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   10984                 :                               NULL, 1, &key);
   10985             195 :     while ((trigtup = systable_getnext(scan)) != NULL)
   10986                 :     {
   10987 CBC         156 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   10988 ECB             :         ObjectAddress trigger;
   10989                 : 
   10990 GIC         156 :         if (trgform->tgconstrrelid != fk->conrelid)
   10991 CBC          78 :             continue;
   10992              78 :         if (trgform->tgrelid != fk->confrelid)
   10993 UIC           0 :             continue;
   10994                 : 
   10995                 :         /*
   10996                 :          * The constraint is originally set up to contain this trigger as an
   10997                 :          * implementation object, so there's a dependency record that links
   10998                 :          * the two; however, since the trigger is no longer needed, we remove
   10999                 :          * the dependency link in order to be able to drop the trigger while
   11000                 :          * keeping the constraint intact.
   11001                 :          */
   11002 GIC          78 :         deleteDependencyRecordsFor(TriggerRelationId,
   11003                 :                                    trgform->oid,
   11004                 :                                    false);
   11005                 :         /* make dependency deletion visible to performDeletion */
   11006              78 :         CommandCounterIncrement();
   11007              78 :         ObjectAddressSet(trigger, TriggerRelationId,
   11008 ECB             :                          trgform->oid);
   11009 GIC          78 :         performDeletion(&trigger, DROP_RESTRICT, 0);
   11010                 :         /* make trigger drop visible, in case the loop iterates */
   11011              78 :         CommandCounterIncrement();
   11012 ECB             :     }
   11013                 : 
   11014 GIC          39 :     systable_endscan(scan);
   11015                 : 
   11016              39 :     ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
   11017 ECB             : 
   11018                 :     /*
   11019                 :      * Like the constraint, attach partition's "check" triggers to the
   11020                 :      * corresponding parent triggers.
   11021                 :      */
   11022 GIC          39 :     GetForeignKeyCheckTriggers(trigrel,
   11023                 :                                fk->conoid, fk->confrelid, fk->conrelid,
   11024                 :                                &insertTriggerOid, &updateTriggerOid);
   11025              39 :     Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
   11026              39 :     TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
   11027                 :                             partRelid);
   11028 CBC          39 :     Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
   11029              39 :     TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
   11030                 :                             partRelid);
   11031 ECB             : 
   11032 GBC          39 :     CommandCounterIncrement();
   11033 GIC          39 :     return true;
   11034                 : }
   11035                 : 
   11036                 : /*
   11037                 :  * GetForeignKeyActionTriggers
   11038                 :  *      Returns delete and update "action" triggers of the given relation
   11039                 :  *      belonging to the given constraint
   11040                 :  */
   11041                 : static void
   11042 CBC          60 : GetForeignKeyActionTriggers(Relation trigrel,
   11043                 :                             Oid conoid, Oid confrelid, Oid conrelid,
   11044                 :                             Oid *deleteTriggerOid,
   11045                 :                             Oid *updateTriggerOid)
   11046                 : {
   11047                 :     ScanKeyData key;
   11048 ECB             :     SysScanDesc scan;
   11049                 :     HeapTuple   trigtup;
   11050                 : 
   11051 GIC          60 :     *deleteTriggerOid = *updateTriggerOid = InvalidOid;
   11052 CBC          60 :     ScanKeyInit(&key,
   11053                 :                 Anum_pg_trigger_tgconstraint,
   11054 ECB             :                 BTEqualStrategyNumber, F_OIDEQ,
   11055                 :                 ObjectIdGetDatum(conoid));
   11056                 : 
   11057 GIC          60 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   11058                 :                               NULL, 1, &key);
   11059             270 :     while ((trigtup = systable_getnext(scan)) != NULL)
   11060                 :     {
   11061             210 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   11062                 : 
   11063             210 :         if (trgform->tgconstrrelid != conrelid)
   11064              90 :             continue;
   11065             120 :         if (trgform->tgrelid != confrelid)
   11066 UIC           0 :             continue;
   11067                 :         /* Only ever look at "action" triggers on the PK side. */
   11068 GIC         120 :         if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
   11069 UIC           0 :             continue;
   11070 GIC         120 :         if (TRIGGER_FOR_DELETE(trgform->tgtype))
   11071                 :         {
   11072              60 :             Assert(*deleteTriggerOid == InvalidOid);
   11073              60 :             *deleteTriggerOid = trgform->oid;
   11074                 :         }
   11075              60 :         else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   11076                 :         {
   11077              60 :             Assert(*updateTriggerOid == InvalidOid);
   11078              60 :             *updateTriggerOid = trgform->oid;
   11079 ECB             :         }
   11080                 : #ifndef USE_ASSERT_CHECKING
   11081 EUB             :         /* In an assert-enabled build, continue looking to find duplicates */
   11082                 :         if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
   11083 ECB             :             break;
   11084                 : #endif
   11085                 :     }
   11086                 : 
   11087 GIC          60 :     if (!OidIsValid(*deleteTriggerOid))
   11088 LBC           0 :         elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
   11089 ECB             :              conoid);
   11090 GIC          60 :     if (!OidIsValid(*updateTriggerOid))
   11091 UIC           0 :         elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
   11092                 :              conoid);
   11093                 : 
   11094 GIC          60 :     systable_endscan(scan);
   11095              60 : }
   11096 ECB             : 
   11097                 : /*
   11098                 :  * GetForeignKeyCheckTriggers
   11099                 :  *      Returns insert and update "check" triggers of the given relation
   11100                 :  *      belonging to the given constraint
   11101                 :  */
   11102                 : static void
   11103 GIC         277 : GetForeignKeyCheckTriggers(Relation trigrel,
   11104 ECB             :                            Oid conoid, Oid confrelid, Oid conrelid,
   11105                 :                            Oid *insertTriggerOid,
   11106                 :                            Oid *updateTriggerOid)
   11107                 : {
   11108                 :     ScanKeyData key;
   11109                 :     SysScanDesc scan;
   11110                 :     HeapTuple   trigtup;
   11111                 : 
   11112 GIC         277 :     *insertTriggerOid = *updateTriggerOid = InvalidOid;
   11113             277 :     ScanKeyInit(&key,
   11114                 :                 Anum_pg_trigger_tgconstraint,
   11115                 :                 BTEqualStrategyNumber, F_OIDEQ,
   11116 ECB             :                 ObjectIdGetDatum(conoid));
   11117                 : 
   11118 GIC         277 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   11119                 :                               NULL, 1, &key);
   11120            1199 :     while ((trigtup = systable_getnext(scan)) != NULL)
   11121                 :     {
   11122             922 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   11123                 : 
   11124             922 :         if (trgform->tgconstrrelid != confrelid)
   11125             326 :             continue;
   11126             596 :         if (trgform->tgrelid != conrelid)
   11127 LBC           0 :             continue;
   11128 ECB             :         /* Only ever look at "check" triggers on the FK side. */
   11129 GIC         596 :         if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
   11130 CBC          42 :             continue;
   11131 GIC         554 :         if (TRIGGER_FOR_INSERT(trgform->tgtype))
   11132 ECB             :         {
   11133 GIC         277 :             Assert(*insertTriggerOid == InvalidOid);
   11134             277 :             *insertTriggerOid = trgform->oid;
   11135                 :         }
   11136             277 :         else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   11137                 :         {
   11138             277 :             Assert(*updateTriggerOid == InvalidOid);
   11139             277 :             *updateTriggerOid = trgform->oid;
   11140                 :         }
   11141                 : #ifndef USE_ASSERT_CHECKING
   11142                 :         /* In an assert-enabled build, continue looking to find duplicates. */
   11143 ECB             :         if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
   11144                 :             break;
   11145                 : #endif
   11146                 :     }
   11147                 : 
   11148 CBC         277 :     if (!OidIsValid(*insertTriggerOid))
   11149 UIC           0 :         elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
   11150 ECB             :              conoid);
   11151 CBC         277 :     if (!OidIsValid(*updateTriggerOid))
   11152 UIC           0 :         elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
   11153                 :              conoid);
   11154                 : 
   11155 CBC         277 :     systable_endscan(scan);
   11156             277 : }
   11157                 : 
   11158 ECB             : /*
   11159                 :  * ALTER TABLE ALTER CONSTRAINT
   11160                 :  *
   11161                 :  * Update the attributes of a constraint.
   11162                 :  *
   11163                 :  * Currently only works for Foreign Key constraints.
   11164                 :  *
   11165                 :  * If the constraint is modified, returns its address; otherwise, return
   11166                 :  * InvalidObjectAddress.
   11167                 :  */
   11168                 : static ObjectAddress
   11169 CBC          33 : ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
   11170 ECB             :                       bool recursing, LOCKMODE lockmode)
   11171                 : {
   11172                 :     Constraint *cmdcon;
   11173                 :     Relation    conrel;
   11174                 :     Relation    tgrel;
   11175                 :     SysScanDesc scan;
   11176                 :     ScanKeyData skey[3];
   11177                 :     HeapTuple   contuple;
   11178                 :     Form_pg_constraint currcon;
   11179                 :     ObjectAddress address;
   11180 GIC          33 :     List       *otherrelids = NIL;
   11181 ECB             :     ListCell   *lc;
   11182                 : 
   11183 CBC          33 :     cmdcon = castNode(Constraint, cmd->def);
   11184 ECB             : 
   11185 CBC          33 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   11186              33 :     tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   11187                 : 
   11188 ECB             :     /*
   11189                 :      * Find and check the target constraint
   11190                 :      */
   11191 GIC          33 :     ScanKeyInit(&skey[0],
   11192 ECB             :                 Anum_pg_constraint_conrelid,
   11193                 :                 BTEqualStrategyNumber, F_OIDEQ,
   11194                 :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   11195 GIC          33 :     ScanKeyInit(&skey[1],
   11196                 :                 Anum_pg_constraint_contypid,
   11197 ECB             :                 BTEqualStrategyNumber, F_OIDEQ,
   11198                 :                 ObjectIdGetDatum(InvalidOid));
   11199 CBC          33 :     ScanKeyInit(&skey[2],
   11200                 :                 Anum_pg_constraint_conname,
   11201                 :                 BTEqualStrategyNumber, F_NAMEEQ,
   11202 GIC          33 :                 CStringGetDatum(cmdcon->conname));
   11203              33 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   11204                 :                               true, NULL, 3, skey);
   11205                 : 
   11206                 :     /* There can be at most one matching row */
   11207              33 :     if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
   11208 UIC           0 :         ereport(ERROR,
   11209                 :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   11210                 :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   11211                 :                         cmdcon->conname, RelationGetRelationName(rel))));
   11212                 : 
   11213 CBC          33 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   11214              33 :     if (currcon->contype != CONSTRAINT_FOREIGN)
   11215 UIC           0 :         ereport(ERROR,
   11216                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11217 ECB             :                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
   11218                 :                         cmdcon->conname, RelationGetRelationName(rel))));
   11219                 : 
   11220                 :     /*
   11221                 :      * If it's not the topmost constraint, raise an error.
   11222                 :      *
   11223                 :      * Altering a non-topmost constraint leaves some triggers untouched, since
   11224                 :      * they are not directly connected to this constraint; also, pg_dump would
   11225                 :      * ignore the deferrability status of the individual constraint, since it
   11226                 :      * only dumps topmost constraints.  Avoid these problems by refusing this
   11227                 :      * operation and telling the user to alter the parent constraint instead.
   11228                 :      */
   11229 CBC          33 :     if (OidIsValid(currcon->conparentid))
   11230 ECB             :     {
   11231                 :         HeapTuple   tp;
   11232 CBC           6 :         Oid         parent = currcon->conparentid;
   11233 GIC           6 :         char       *ancestorname = NULL;
   11234               6 :         char       *ancestortable = NULL;
   11235 ECB             : 
   11236                 :         /* Loop to find the topmost constraint */
   11237 GIC          12 :         while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
   11238 ECB             :         {
   11239 GIC          12 :             Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
   11240 ECB             : 
   11241                 :             /* If no parent, this is the constraint we want */
   11242 GIC          12 :             if (!OidIsValid(contup->conparentid))
   11243                 :             {
   11244               6 :                 ancestorname = pstrdup(NameStr(contup->conname));
   11245               6 :                 ancestortable = get_rel_name(contup->conrelid);
   11246               6 :                 ReleaseSysCache(tp);
   11247               6 :                 break;
   11248                 :             }
   11249                 : 
   11250               6 :             parent = contup->conparentid;
   11251               6 :             ReleaseSysCache(tp);
   11252                 :         }
   11253                 : 
   11254               6 :         ereport(ERROR,
   11255                 :                 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
   11256                 :                         cmdcon->conname, RelationGetRelationName(rel)),
   11257                 :                  ancestorname && ancestortable ?
   11258 ECB             :                  errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
   11259                 :                            cmdcon->conname, ancestorname, ancestortable) : 0,
   11260                 :                  errhint("You may alter the constraint it derives from, instead.")));
   11261                 :     }
   11262                 : 
   11263                 :     /*
   11264                 :      * Do the actual catalog work.  We can skip changing if already in the
   11265                 :      * desired state, but not if a partitioned table: partitions need to be
   11266                 :      * processed regardless, in case they had the constraint locally changed.
   11267                 :      */
   11268 GIC          27 :     address = InvalidObjectAddress;
   11269              27 :     if (currcon->condeferrable != cmdcon->deferrable ||
   11270               3 :         currcon->condeferred != cmdcon->initdeferred ||
   11271 UIC           0 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11272                 :     {
   11273 GIC          27 :         if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
   11274                 :                                      &otherrelids, lockmode))
   11275              27 :             ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
   11276 ECB             :     }
   11277                 : 
   11278                 :     /*
   11279                 :      * ATExecConstrRecurse already invalidated relcache for the relations
   11280                 :      * having the constraint itself; here we also invalidate for relations
   11281                 :      * that have any triggers that are part of the constraint.
   11282                 :      */
   11283 GIC          69 :     foreach(lc, otherrelids)
   11284              42 :         CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
   11285                 : 
   11286              27 :     systable_endscan(scan);
   11287                 : 
   11288              27 :     table_close(tgrel, RowExclusiveLock);
   11289              27 :     table_close(conrel, RowExclusiveLock);
   11290                 : 
   11291              27 :     return address;
   11292                 : }
   11293                 : 
   11294                 : /*
   11295                 :  * Recursive subroutine of ATExecAlterConstraint.  Returns true if the
   11296                 :  * constraint is altered.
   11297 ECB             :  *
   11298                 :  * *otherrelids is appended OIDs of relations containing affected triggers.
   11299                 :  *
   11300 EUB             :  * Note that we must recurse even when the values are correct, in case
   11301 ECB             :  * indirect descendants have had their constraints altered locally.
   11302                 :  * (This could be avoided if we forbade altering constraints in partitions
   11303                 :  * but existing releases don't do that.)
   11304                 :  */
   11305                 : static bool
   11306 GIC          60 : ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
   11307 ECB             :                          Relation rel, HeapTuple contuple, List **otherrelids,
   11308                 :                          LOCKMODE lockmode)
   11309 EUB             : {
   11310                 :     Form_pg_constraint currcon;
   11311                 :     Oid         conoid;
   11312 ECB             :     Oid         refrelid;
   11313 GIC          60 :     bool        changed = false;
   11314 ECB             : 
   11315 CBC          60 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   11316              60 :     conoid = currcon->oid;
   11317 GIC          60 :     refrelid = currcon->confrelid;
   11318 EUB             : 
   11319                 :     /*
   11320                 :      * Update pg_constraint with the flags from cmdcon.
   11321                 :      *
   11322                 :      * If called to modify a constraint that's already in the desired state,
   11323                 :      * silently do nothing.
   11324                 :      */
   11325 GIC          60 :     if (currcon->condeferrable != cmdcon->deferrable ||
   11326               3 :         currcon->condeferred != cmdcon->initdeferred)
   11327                 :     {
   11328 ECB             :         HeapTuple   copyTuple;
   11329                 :         Form_pg_constraint copy_con;
   11330                 :         HeapTuple   tgtuple;
   11331 EUB             :         ScanKeyData tgkey;
   11332 ECB             :         SysScanDesc tgscan;
   11333                 : 
   11334 CBC          60 :         copyTuple = heap_copytuple(contuple);
   11335              60 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   11336              60 :         copy_con->condeferrable = cmdcon->deferrable;
   11337              60 :         copy_con->condeferred = cmdcon->initdeferred;
   11338              60 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   11339 ECB             : 
   11340 GIC          60 :         InvokeObjectPostAlterHook(ConstraintRelationId,
   11341 ECB             :                                   conoid, 0);
   11342                 : 
   11343 CBC          60 :         heap_freetuple(copyTuple);
   11344 GIC          60 :         changed = true;
   11345                 : 
   11346 ECB             :         /* Make new constraint flags visible to others */
   11347 CBC          60 :         CacheInvalidateRelcache(rel);
   11348                 : 
   11349                 :         /*
   11350                 :          * Now we need to update the multiple entries in pg_trigger that
   11351                 :          * implement the constraint.
   11352                 :          */
   11353 GIC          60 :         ScanKeyInit(&tgkey,
   11354                 :                     Anum_pg_trigger_tgconstraint,
   11355                 :                     BTEqualStrategyNumber, F_OIDEQ,
   11356 ECB             :                     ObjectIdGetDatum(conoid));
   11357 GIC          60 :         tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
   11358                 :                                     NULL, 1, &tgkey);
   11359             234 :         while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
   11360 ECB             :         {
   11361 GIC         174 :             Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
   11362 ECB             :             Form_pg_trigger copy_tg;
   11363                 :             HeapTuple   tgCopyTuple;
   11364                 : 
   11365                 :             /*
   11366                 :              * Remember OIDs of other relation(s) involved in FK constraint.
   11367                 :              * (Note: it's likely that we could skip forcing a relcache inval
   11368                 :              * for other rels that don't have a trigger whose properties
   11369                 :              * change, but let's be conservative.)
   11370 EUB             :              */
   11371 GIC         174 :             if (tgform->tgrelid != RelationGetRelid(rel))
   11372              84 :                 *otherrelids = list_append_unique_oid(*otherrelids,
   11373                 :                                                       tgform->tgrelid);
   11374                 : 
   11375                 :             /*
   11376                 :              * Update deferrability of RI_FKey_noaction_del,
   11377                 :              * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
   11378                 :              * triggers, but not others; see createForeignKeyActionTriggers
   11379 ECB             :              * and CreateFKCheckTrigger.
   11380                 :              */
   11381 GIC         174 :             if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
   11382             141 :                 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
   11383 CBC          99 :                 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
   11384              54 :                 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
   11385 GIC           9 :                 continue;
   11386 ECB             : 
   11387 GNC         165 :             tgCopyTuple = heap_copytuple(tgtuple);
   11388             165 :             copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
   11389                 : 
   11390 GIC         165 :             copy_tg->tgdeferrable = cmdcon->deferrable;
   11391 CBC         165 :             copy_tg->tginitdeferred = cmdcon->initdeferred;
   11392 GNC         165 :             CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
   11393 ECB             : 
   11394 GIC         165 :             InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
   11395                 : 
   11396 GNC         165 :             heap_freetuple(tgCopyTuple);
   11397                 :         }
   11398                 : 
   11399 CBC          60 :         systable_endscan(tgscan);
   11400                 :     }
   11401                 : 
   11402 ECB             :     /*
   11403                 :      * If the table at either end of the constraint is partitioned, we need to
   11404                 :      * recurse and handle every constraint that is a child of this one.
   11405                 :      *
   11406                 :      * (This assumes that the recurse flag is forcibly set for partitioned
   11407                 :      * tables, and not set for legacy inheritance, though we don't check for
   11408                 :      * that here.)
   11409                 :      */
   11410 CBC         108 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   11411 GIC          48 :         get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
   11412                 :     {
   11413                 :         ScanKeyData pkey;
   11414                 :         SysScanDesc pscan;
   11415                 :         HeapTuple   childtup;
   11416                 : 
   11417              21 :         ScanKeyInit(&pkey,
   11418                 :                     Anum_pg_constraint_conparentid,
   11419 ECB             :                     BTEqualStrategyNumber, F_OIDEQ,
   11420                 :                     ObjectIdGetDatum(conoid));
   11421                 : 
   11422 GIC          21 :         pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   11423                 :                                    true, NULL, 1, &pkey);
   11424                 : 
   11425              54 :         while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   11426                 :         {
   11427              33 :             Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   11428 ECB             :             Relation    childrel;
   11429                 : 
   11430 GIC          33 :             childrel = table_open(childcon->conrelid, lockmode);
   11431              33 :             ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
   11432                 :                                      otherrelids, lockmode);
   11433              33 :             table_close(childrel, NoLock);
   11434 ECB             :         }
   11435                 : 
   11436 CBC          21 :         systable_endscan(pscan);
   11437                 :     }
   11438 ECB             : 
   11439 GIC          60 :     return changed;
   11440 ECB             : }
   11441                 : 
   11442                 : /*
   11443 EUB             :  * ALTER TABLE VALIDATE CONSTRAINT
   11444                 :  *
   11445 ECB             :  * XXX The reason we handle recursion here rather than at Phase 1 is because
   11446 EUB             :  * there's no good way to skip recursing when handling foreign keys: there is
   11447 ECB             :  * no need to lock children in that case, yet we wouldn't be able to avoid
   11448                 :  * doing so at that level.
   11449                 :  *
   11450                 :  * Return value is the address of the validated constraint.  If the constraint
   11451                 :  * was already validated, InvalidObjectAddress is returned.
   11452                 :  */
   11453                 : static ObjectAddress
   11454 CBC         218 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
   11455 ECB             :                          bool recurse, bool recursing, LOCKMODE lockmode)
   11456                 : {
   11457                 :     Relation    conrel;
   11458                 :     SysScanDesc scan;
   11459                 :     ScanKeyData skey[3];
   11460                 :     HeapTuple   tuple;
   11461                 :     Form_pg_constraint con;
   11462                 :     ObjectAddress address;
   11463                 : 
   11464 CBC         218 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   11465 EUB             : 
   11466                 :     /*
   11467 ECB             :      * Find and check the target constraint
   11468 EUB             :      */
   11469 GIC         218 :     ScanKeyInit(&skey[0],
   11470                 :                 Anum_pg_constraint_conrelid,
   11471 ECB             :                 BTEqualStrategyNumber, F_OIDEQ,
   11472                 :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   11473 GIC         218 :     ScanKeyInit(&skey[1],
   11474                 :                 Anum_pg_constraint_contypid,
   11475                 :                 BTEqualStrategyNumber, F_OIDEQ,
   11476                 :                 ObjectIdGetDatum(InvalidOid));
   11477             218 :     ScanKeyInit(&skey[2],
   11478                 :                 Anum_pg_constraint_conname,
   11479                 :                 BTEqualStrategyNumber, F_NAMEEQ,
   11480 ECB             :                 CStringGetDatum(constrName));
   11481 GIC         218 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   11482                 :                               true, NULL, 3, skey);
   11483                 : 
   11484                 :     /* There can be at most one matching row */
   11485             218 :     if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
   11486 UIC           0 :         ereport(ERROR,
   11487                 :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   11488                 :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   11489 ECB             :                         constrName, RelationGetRelationName(rel))));
   11490                 : 
   11491 GIC         218 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
   11492             218 :     if (con->contype != CONSTRAINT_FOREIGN &&
   11493              66 :         con->contype != CONSTRAINT_CHECK)
   11494 UIC           0 :         ereport(ERROR,
   11495 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11496                 :                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
   11497                 :                         constrName, RelationGetRelationName(rel))));
   11498                 : 
   11499 CBC         218 :     if (!con->convalidated)
   11500                 :     {
   11501 ECB             :         AlteredTableInfo *tab;
   11502                 :         HeapTuple   copyTuple;
   11503                 :         Form_pg_constraint copy_con;
   11504 EUB             : 
   11505 GIC         209 :         if (con->contype == CONSTRAINT_FOREIGN)
   11506 ECB             :         {
   11507                 :             NewConstraint *newcon;
   11508                 :             Constraint *fkconstraint;
   11509                 : 
   11510                 :             /* Queue validation for phase 3 */
   11511 CBC         149 :             fkconstraint = makeNode(Constraint);
   11512                 :             /* for now this is all we need */
   11513             149 :             fkconstraint->conname = constrName;
   11514                 : 
   11515             149 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
   11516             149 :             newcon->name = constrName;
   11517 GIC         149 :             newcon->contype = CONSTR_FOREIGN;
   11518             149 :             newcon->refrelid = con->confrelid;
   11519             149 :             newcon->refindid = con->conindid;
   11520             149 :             newcon->conid = con->oid;
   11521             149 :             newcon->qual = (Node *) fkconstraint;
   11522                 : 
   11523                 :             /* Find or create work queue entry for this table */
   11524             149 :             tab = ATGetQueueEntry(wqueue, rel);
   11525 CBC         149 :             tab->constraints = lappend(tab->constraints, newcon);
   11526 EUB             : 
   11527                 :             /*
   11528 ECB             :              * We disallow creating invalid foreign keys to or from
   11529 EUB             :              * partitioned tables, so ignoring the recursion bit is okay.
   11530                 :              */
   11531                 :         }
   11532 CBC          60 :         else if (con->contype == CONSTRAINT_CHECK)
   11533 ECB             :         {
   11534 GIC          60 :             List       *children = NIL;
   11535                 :             ListCell   *child;
   11536                 :             NewConstraint *newcon;
   11537                 :             Datum       val;
   11538                 :             char       *conbin;
   11539                 : 
   11540                 :             /*
   11541                 :              * If we're recursing, the parent has already done this, so skip
   11542                 :              * it.  Also, if the constraint is a NO INHERIT constraint, we
   11543                 :              * shouldn't try to look for it in the children.
   11544                 :              */
   11545 CBC          60 :             if (!recursing && !con->connoinherit)
   11546 GIC          33 :                 children = find_all_inheritors(RelationGetRelid(rel),
   11547                 :                                                lockmode, NULL);
   11548                 : 
   11549                 :             /*
   11550                 :              * For CHECK constraints, we must ensure that we only mark the
   11551                 :              * constraint as validated on the parent if it's already validated
   11552                 :              * on the children.
   11553                 :              *
   11554                 :              * We recurse before validating on the parent, to reduce risk of
   11555                 :              * deadlocks.
   11556 ECB             :              */
   11557 GIC         117 :             foreach(child, children)
   11558                 :             {
   11559 CBC          57 :                 Oid         childoid = lfirst_oid(child);
   11560                 :                 Relation    childrel;
   11561 ECB             : 
   11562 CBC          57 :                 if (childoid == RelationGetRelid(rel))
   11563 GIC          33 :                     continue;
   11564                 : 
   11565                 :                 /*
   11566                 :                  * If we are told not to recurse, there had better not be any
   11567 ECB             :                  * child tables, because we can't mark the constraint on the
   11568                 :                  * parent valid unless it is valid for all child tables.
   11569                 :                  */
   11570 GIC          24 :                 if (!recurse)
   11571 LBC           0 :                     ereport(ERROR,
   11572                 :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   11573                 :                              errmsg("constraint must be validated on child tables too")));
   11574                 : 
   11575 ECB             :                 /* find_all_inheritors already got lock */
   11576 GIC          24 :                 childrel = table_open(childoid, NoLock);
   11577                 : 
   11578 CBC          24 :                 ATExecValidateConstraint(wqueue, childrel, constrName, false,
   11579 ECB             :                                          true, lockmode);
   11580 GIC          24 :                 table_close(childrel, NoLock);
   11581                 :             }
   11582                 : 
   11583 ECB             :             /* Queue validation for phase 3 */
   11584 GBC          60 :             newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
   11585 GIC          60 :             newcon->name = constrName;
   11586              60 :             newcon->contype = CONSTR_CHECK;
   11587              60 :             newcon->refrelid = InvalidOid;
   11588              60 :             newcon->refindid = InvalidOid;
   11589 CBC          60 :             newcon->conid = con->oid;
   11590 ECB             : 
   11591 GNC          60 :             val = SysCacheGetAttrNotNull(CONSTROID, tuple,
   11592                 :                                          Anum_pg_constraint_conbin);
   11593 GIC          60 :             conbin = TextDatumGetCString(val);
   11594              60 :             newcon->qual = (Node *) stringToNode(conbin);
   11595                 : 
   11596                 :             /* Find or create work queue entry for this table */
   11597              60 :             tab = ATGetQueueEntry(wqueue, rel);
   11598              60 :             tab->constraints = lappend(tab->constraints, newcon);
   11599                 : 
   11600                 :             /*
   11601                 :              * Invalidate relcache so that others see the new validated
   11602 ECB             :              * constraint.
   11603                 :              */
   11604 GIC          60 :             CacheInvalidateRelcache(rel);
   11605 ECB             :         }
   11606                 : 
   11607                 :         /*
   11608                 :          * Now update the catalog, while we have the door open.
   11609                 :          */
   11610 CBC         209 :         copyTuple = heap_copytuple(tuple);
   11611 GIC         209 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   11612 CBC         209 :         copy_con->convalidated = true;
   11613 GIC         209 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   11614                 : 
   11615 CBC         209 :         InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   11616                 : 
   11617             209 :         heap_freetuple(copyTuple);
   11618 ECB             : 
   11619 CBC         209 :         ObjectAddressSet(address, ConstraintRelationId, con->oid);
   11620 ECB             :     }
   11621                 :     else
   11622 GIC           9 :         address = InvalidObjectAddress; /* already validated */
   11623 ECB             : 
   11624 CBC         218 :     systable_endscan(scan);
   11625                 : 
   11626 GIC         218 :     table_close(conrel, RowExclusiveLock);
   11627 ECB             : 
   11628 GIC         218 :     return address;
   11629                 : }
   11630                 : 
   11631                 : 
   11632                 : /*
   11633                 :  * transformColumnNameList - transform list of column names
   11634                 :  *
   11635                 :  * Lookup each name and return its attnum and, optionally, type OID
   11636                 :  *
   11637                 :  * Note: the name of this function suggests that it's general-purpose,
   11638                 :  * but actually it's only used to look up names appearing in foreign-key
   11639                 :  * clauses.  The error messages would need work to use it in other cases,
   11640                 :  * and perhaps the validity checks as well.
   11641 ECB             :  */
   11642                 : static int
   11643 CBC        2584 : transformColumnNameList(Oid relId, List *colList,
   11644 EUB             :                         int16 *attnums, Oid *atttypids)
   11645                 : {
   11646 ECB             :     ListCell   *l;
   11647                 :     int         attnum;
   11648                 : 
   11649 GIC        2584 :     attnum = 0;
   11650            4548 :     foreach(l, colList)
   11651                 :     {
   11652            1997 :         char       *attname = strVal(lfirst(l));
   11653                 :         HeapTuple   atttuple;
   11654                 :         Form_pg_attribute attform;
   11655                 : 
   11656 CBC        1997 :         atttuple = SearchSysCacheAttName(relId, attname);
   11657            1997 :         if (!HeapTupleIsValid(atttuple))
   11658 GIC          27 :             ereport(ERROR,
   11659 ECB             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
   11660                 :                      errmsg("column \"%s\" referenced in foreign key constraint does not exist",
   11661                 :                             attname)));
   11662 CBC        1970 :         attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   11663 GIC        1970 :         if (attform->attnum < 0)
   11664 CBC           6 :             ereport(ERROR,
   11665                 :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   11666                 :                      errmsg("system columns cannot be used in foreign keys")));
   11667 GIC        1964 :         if (attnum >= INDEX_MAX_KEYS)
   11668 UIC           0 :             ereport(ERROR,
   11669                 :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
   11670                 :                      errmsg("cannot have more than %d keys in a foreign key",
   11671                 :                             INDEX_MAX_KEYS)));
   11672 GIC        1964 :         attnums[attnum] = attform->attnum;
   11673            1964 :         if (atttypids != NULL)
   11674            1949 :             atttypids[attnum] = attform->atttypid;
   11675            1964 :         ReleaseSysCache(atttuple);
   11676            1964 :         attnum++;
   11677                 :     }
   11678                 : 
   11679 CBC        2551 :     return attnum;
   11680                 : }
   11681                 : 
   11682                 : /*
   11683                 :  * transformFkeyGetPrimaryKey -
   11684                 :  *
   11685                 :  *  Look up the names, attnums, and types of the primary key attributes
   11686 ECB             :  *  for the pkrel.  Also return the index OID and index opclasses of the
   11687                 :  *  index supporting the primary key.
   11688                 :  *
   11689                 :  *  All parameters except pkrel are output parameters.  Also, the function
   11690                 :  *  return value is the number of attributes in the primary key.
   11691                 :  *
   11692                 :  *  Used when the column list in the REFERENCES specification is omitted.
   11693                 :  */
   11694                 : static int
   11695 GIC         458 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
   11696                 :                            List **attnamelist,
   11697                 :                            int16 *attnums, Oid *atttypids,
   11698 ECB             :                            Oid *opclasses)
   11699                 : {
   11700                 :     List       *indexoidlist;
   11701                 :     ListCell   *indexoidscan;
   11702 GIC         458 :     HeapTuple   indexTuple = NULL;
   11703             458 :     Form_pg_index indexStruct = NULL;
   11704                 :     Datum       indclassDatum;
   11705                 :     oidvector  *indclass;
   11706 ECB             :     int         i;
   11707                 : 
   11708                 :     /*
   11709                 :      * Get the list of index OIDs for the table from the relcache, and look up
   11710                 :      * each one in the pg_index syscache until we find one marked primary key
   11711                 :      * (hopefully there isn't more than one such).  Insist it's valid, too.
   11712                 :      */
   11713 GIC         458 :     *indexOid = InvalidOid;
   11714                 : 
   11715 CBC         458 :     indexoidlist = RelationGetIndexList(pkrel);
   11716 ECB             : 
   11717 GIC         461 :     foreach(indexoidscan, indexoidlist)
   11718                 :     {
   11719 CBC         461 :         Oid         indexoid = lfirst_oid(indexoidscan);
   11720                 : 
   11721 GIC         461 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   11722             461 :         if (!HeapTupleIsValid(indexTuple))
   11723 UIC           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
   11724 GIC         461 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   11725 CBC         461 :         if (indexStruct->indisprimary && indexStruct->indisvalid)
   11726                 :         {
   11727                 :             /*
   11728                 :              * Refuse to use a deferrable primary key.  This is per SQL spec,
   11729 ECB             :              * and there would be a lot of interesting semantic problems if we
   11730                 :              * tried to allow it.
   11731                 :              */
   11732 GIC         458 :             if (!indexStruct->indimmediate)
   11733 LBC           0 :                 ereport(ERROR,
   11734                 :                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   11735                 :                          errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
   11736                 :                                 RelationGetRelationName(pkrel))));
   11737                 : 
   11738 GIC         458 :             *indexOid = indexoid;
   11739             458 :             break;
   11740                 :         }
   11741               3 :         ReleaseSysCache(indexTuple);
   11742                 :     }
   11743 ECB             : 
   11744 CBC         458 :     list_free(indexoidlist);
   11745                 : 
   11746                 :     /*
   11747                 :      * Check that we found it
   11748                 :      */
   11749 GIC         458 :     if (!OidIsValid(*indexOid))
   11750 UIC           0 :         ereport(ERROR,
   11751                 :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   11752                 :                  errmsg("there is no primary key for referenced table \"%s\"",
   11753 ECB             :                         RelationGetRelationName(pkrel))));
   11754                 : 
   11755                 :     /* Must get indclass the hard way */
   11756 GNC         458 :     indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   11757                 :                                            Anum_pg_index_indclass);
   11758 CBC         458 :     indclass = (oidvector *) DatumGetPointer(indclassDatum);
   11759 ECB             : 
   11760                 :     /*
   11761                 :      * Now build the list of PK attributes from the indkey definition (we
   11762                 :      * assume a primary key cannot have expressional elements)
   11763                 :      */
   11764 GIC         458 :     *attnamelist = NIL;
   11765 CBC        1032 :     for (i = 0; i < indexStruct->indnkeyatts; i++)
   11766                 :     {
   11767             574 :         int         pkattno = indexStruct->indkey.values[i];
   11768                 : 
   11769 GIC         574 :         attnums[i] = pkattno;
   11770 CBC         574 :         atttypids[i] = attnumTypeId(pkrel, pkattno);
   11771 GIC         574 :         opclasses[i] = indclass->values[i];
   11772             574 :         *attnamelist = lappend(*attnamelist,
   11773             574 :                                makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
   11774                 :     }
   11775                 : 
   11776             458 :     ReleaseSysCache(indexTuple);
   11777                 : 
   11778             458 :     return i;
   11779                 : }
   11780                 : 
   11781 ECB             : /*
   11782                 :  * transformFkeyCheckAttrs -
   11783                 :  *
   11784                 :  *  Make sure that the attributes of a referenced table belong to a unique
   11785                 :  *  (or primary key) constraint.  Return the OID of the index supporting
   11786                 :  *  the constraint, as well as the opclasses associated with the index
   11787                 :  *  columns.
   11788                 :  */
   11789                 : static Oid
   11790 GIC         532 : transformFkeyCheckAttrs(Relation pkrel,
   11791                 :                         int numattrs, int16 *attnums,
   11792                 :                         Oid *opclasses) /* output parameter */
   11793 ECB             : {
   11794 GIC         532 :     Oid         indexoid = InvalidOid;
   11795             532 :     bool        found = false;
   11796 CBC         532 :     bool        found_deferrable = false;
   11797                 :     List       *indexoidlist;
   11798 ECB             :     ListCell   *indexoidscan;
   11799                 :     int         i,
   11800                 :                 j;
   11801                 : 
   11802                 :     /*
   11803                 :      * Reject duplicate appearances of columns in the referenced-columns list.
   11804                 :      * Such a case is forbidden by the SQL standard, and even if we thought it
   11805                 :      * useful to allow it, there would be ambiguity about how to match the
   11806                 :      * list to unique indexes (in particular, it'd be unclear which index
   11807                 :      * opclass goes with which FK column).
   11808                 :      */
   11809 GIC        1206 :     for (i = 0; i < numattrs; i++)
   11810 ECB             :     {
   11811 GIC         837 :         for (j = i + 1; j < numattrs; j++)
   11812                 :         {
   11813             163 :             if (attnums[i] == attnums[j])
   11814 UIC           0 :                 ereport(ERROR,
   11815                 :                         (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   11816                 :                          errmsg("foreign key referenced-columns list must not contain duplicates")));
   11817                 :         }
   11818                 :     }
   11819                 : 
   11820                 :     /*
   11821                 :      * Get the list of index OIDs for the table from the relcache, and look up
   11822                 :      * each one in the pg_index syscache, and match unique indexes to the list
   11823                 :      * of attnums we are given.
   11824                 :      */
   11825 CBC         532 :     indexoidlist = RelationGetIndexList(pkrel);
   11826                 : 
   11827 GIC         622 :     foreach(indexoidscan, indexoidlist)
   11828                 :     {
   11829                 :         HeapTuple   indexTuple;
   11830                 :         Form_pg_index indexStruct;
   11831                 : 
   11832             616 :         indexoid = lfirst_oid(indexoidscan);
   11833             616 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   11834             616 :         if (!HeapTupleIsValid(indexTuple))
   11835 LBC           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
   11836 GIC         616 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   11837                 : 
   11838                 :         /*
   11839                 :          * Must have the right number of columns; must be unique and not a
   11840 ECB             :          * partial index; forget it if there are any expressions, too. Invalid
   11841                 :          * indexes are out as well.
   11842                 :          */
   11843 GIC         616 :         if (indexStruct->indnkeyatts == numattrs &&
   11844 CBC         562 :             indexStruct->indisunique &&
   11845 GIC        1110 :             indexStruct->indisvalid &&
   11846            1110 :             heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
   11847             555 :             heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
   11848 ECB             :         {
   11849                 :             Datum       indclassDatum;
   11850                 :             oidvector  *indclass;
   11851                 : 
   11852                 :             /* Must get indclass the hard way */
   11853 GNC         555 :             indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   11854                 :                                                    Anum_pg_index_indclass);
   11855 GBC         555 :             indclass = (oidvector *) DatumGetPointer(indclassDatum);
   11856                 : 
   11857                 :             /*
   11858                 :              * The given attnum list may match the index columns in any order.
   11859                 :              * Check for a match, and extract the appropriate opclasses while
   11860 ECB             :              * we're at it.
   11861                 :              *
   11862                 :              * We know that attnums[] is duplicate-free per the test at the
   11863 EUB             :              * start of this function, and we checked above that the number of
   11864                 :              * index columns agrees, so if we find a match for each attnums[]
   11865                 :              * entry then we must have a one-to-one match in some order.
   11866                 :              */
   11867 GIC        1223 :             for (i = 0; i < numattrs; i++)
   11868 ECB             :             {
   11869 GIC         697 :                 found = false;
   11870             889 :                 for (j = 0; j < numattrs; j++)
   11871                 :                 {
   11872             860 :                     if (attnums[i] == indexStruct->indkey.values[j])
   11873                 :                     {
   11874 CBC         668 :                         opclasses[i] = indclass->values[j];
   11875 GIC         668 :                         found = true;
   11876             668 :                         break;
   11877                 :                     }
   11878                 :                 }
   11879             697 :                 if (!found)
   11880 CBC          29 :                     break;
   11881                 :             }
   11882 ECB             : 
   11883                 :             /*
   11884                 :              * Refuse to use a deferrable unique/primary key.  This is per SQL
   11885                 :              * spec, and there would be a lot of interesting semantic problems
   11886                 :              * if we tried to allow it.
   11887                 :              */
   11888 CBC         555 :             if (found && !indexStruct->indimmediate)
   11889 ECB             :             {
   11890                 :                 /*
   11891                 :                  * Remember that we found an otherwise matching index, so that
   11892                 :                  * we can generate a more appropriate error message.
   11893                 :                  */
   11894 LBC           0 :                 found_deferrable = true;
   11895 UIC           0 :                 found = false;
   11896                 :             }
   11897                 :         }
   11898 GIC         616 :         ReleaseSysCache(indexTuple);
   11899             616 :         if (found)
   11900             526 :             break;
   11901 ECB             :     }
   11902                 : 
   11903 CBC         532 :     if (!found)
   11904                 :     {
   11905 GIC           6 :         if (found_deferrable)
   11906 UIC           0 :             ereport(ERROR,
   11907                 :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   11908                 :                      errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
   11909                 :                             RelationGetRelationName(pkrel))));
   11910                 :         else
   11911 GIC           6 :             ereport(ERROR,
   11912                 :                     (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   11913                 :                      errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
   11914 ECB             :                             RelationGetRelationName(pkrel))));
   11915                 :     }
   11916                 : 
   11917 GIC         526 :     list_free(indexoidlist);
   11918                 : 
   11919             526 :     return indexoid;
   11920                 : }
   11921                 : 
   11922                 : /*
   11923                 :  * findFkeyCast -
   11924                 :  *
   11925                 :  *  Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
   11926 ECB             :  *  Caller has equal regard for binary coercibility and for an exact match.
   11927                 : */
   11928                 : static CoercionPathType
   11929 GIC           6 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
   11930                 : {
   11931 ECB             :     CoercionPathType ret;
   11932                 : 
   11933 GIC           6 :     if (targetTypeId == sourceTypeId)
   11934                 :     {
   11935               6 :         ret = COERCION_PATH_RELABELTYPE;
   11936               6 :         *funcid = InvalidOid;
   11937                 :     }
   11938                 :     else
   11939 ECB             :     {
   11940 UBC           0 :         ret = find_coercion_pathway(targetTypeId, sourceTypeId,
   11941                 :                                     COERCION_IMPLICIT, funcid);
   11942 UIC           0 :         if (ret == COERCION_PATH_NONE)
   11943                 :             /* A previously-relied-upon cast is now gone. */
   11944               0 :             elog(ERROR, "could not find cast from %u to %u",
   11945 ECB             :                  sourceTypeId, targetTypeId);
   11946                 :     }
   11947                 : 
   11948 GIC           6 :     return ret;
   11949 ECB             : }
   11950                 : 
   11951                 : /*
   11952                 :  * Permissions checks on the referenced table for ADD FOREIGN KEY
   11953                 :  *
   11954                 :  * Note: we have already checked that the user owns the referencing table,
   11955                 :  * else we'd have failed much earlier; no additional checks are needed for it.
   11956                 :  */
   11957                 : static void
   11958 CBC         984 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
   11959                 : {
   11960             984 :     Oid         roleid = GetUserId();
   11961                 :     AclResult   aclresult;
   11962 ECB             :     int         i;
   11963                 : 
   11964                 :     /* Okay if we have relation-level REFERENCES permission */
   11965 GIC         984 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
   11966 ECB             :                                   ACL_REFERENCES);
   11967 CBC         984 :     if (aclresult == ACLCHECK_OK)
   11968 GIC         984 :         return;
   11969                 :     /* Else we must have REFERENCES on each column */
   11970 UIC           0 :     for (i = 0; i < natts; i++)
   11971                 :     {
   11972               0 :         aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
   11973 ECB             :                                           roleid, ACL_REFERENCES);
   11974 UIC           0 :         if (aclresult != ACLCHECK_OK)
   11975               0 :             aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
   11976               0 :                            RelationGetRelationName(rel));
   11977                 :     }
   11978                 : }
   11979 ECB             : 
   11980                 : /*
   11981                 :  * Scan the existing rows in a table to verify they meet a proposed FK
   11982                 :  * constraint.
   11983                 :  *
   11984                 :  * Caller must have opened and locked both relations appropriately.
   11985                 :  */
   11986                 : static void
   11987 GIC         466 : validateForeignKeyConstraint(char *conname,
   11988 ECB             :                              Relation rel,
   11989                 :                              Relation pkrel,
   11990                 :                              Oid pkindOid,
   11991                 :                              Oid constraintOid)
   11992                 : {
   11993                 :     TupleTableSlot *slot;
   11994                 :     TableScanDesc scan;
   11995 GNC         466 :     Trigger     trig = {0};
   11996                 :     Snapshot    snapshot;
   11997 ECB             :     MemoryContext oldcxt;
   11998                 :     MemoryContext perTupCxt;
   11999                 : 
   12000 GIC         466 :     ereport(DEBUG1,
   12001                 :             (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
   12002                 : 
   12003                 :     /*
   12004                 :      * Build a trigger call structure; we'll need it either way.
   12005                 :      */
   12006             466 :     trig.tgoid = InvalidOid;
   12007             466 :     trig.tgname = conname;
   12008             466 :     trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   12009             466 :     trig.tgisinternal = true;
   12010             466 :     trig.tgconstrrelid = RelationGetRelid(pkrel);
   12011 CBC         466 :     trig.tgconstrindid = pkindOid;
   12012 GIC         466 :     trig.tgconstraint = constraintOid;
   12013             466 :     trig.tgdeferrable = false;
   12014             466 :     trig.tginitdeferred = false;
   12015                 :     /* we needn't fill in remaining fields */
   12016                 : 
   12017 ECB             :     /*
   12018                 :      * See if we can do it with a single LEFT JOIN query.  A false result
   12019                 :      * indicates we must proceed with the fire-the-trigger method.
   12020                 :      */
   12021 GIC         466 :     if (RI_Initial_Check(&trig, rel, pkrel))
   12022             432 :         return;
   12023                 : 
   12024 ECB             :     /*
   12025                 :      * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
   12026                 :      * if that tuple had just been inserted.  If any of those fail, it should
   12027                 :      * ereport(ERROR) and that's that.
   12028                 :      */
   12029 GIC           6 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
   12030 CBC           6 :     slot = table_slot_create(rel, NULL);
   12031               6 :     scan = table_beginscan(rel, snapshot, 0, NULL);
   12032 ECB             : 
   12033 GIC           6 :     perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   12034                 :                                       "validateForeignKeyConstraint",
   12035 ECB             :                                       ALLOCSET_SMALL_SIZES);
   12036 GBC           6 :     oldcxt = MemoryContextSwitchTo(perTupCxt);
   12037                 : 
   12038 GIC          36 :     while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
   12039                 :     {
   12040 CBC          33 :         LOCAL_FCINFO(fcinfo, 0);
   12041              33 :         TriggerData trigdata = {0};
   12042 ECB             : 
   12043 CBC          33 :         CHECK_FOR_INTERRUPTS();
   12044 ECB             : 
   12045                 :         /*
   12046                 :          * Make a call to the trigger function
   12047                 :          *
   12048                 :          * No parameters are passed, but we do set a context
   12049                 :          */
   12050 GIC         165 :         MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
   12051                 : 
   12052                 :         /*
   12053                 :          * We assume RI_FKey_check_ins won't look at flinfo...
   12054                 :          */
   12055              33 :         trigdata.type = T_TriggerData;
   12056              33 :         trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
   12057              33 :         trigdata.tg_relation = rel;
   12058              33 :         trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
   12059              33 :         trigdata.tg_trigslot = slot;
   12060              33 :         trigdata.tg_trigger = &trig;
   12061                 : 
   12062              33 :         fcinfo->context = (Node *) &trigdata;
   12063 ECB             : 
   12064 GIC          33 :         RI_FKey_check_ins(fcinfo);
   12065                 : 
   12066              30 :         MemoryContextReset(perTupCxt);
   12067                 :     }
   12068                 : 
   12069               3 :     MemoryContextSwitchTo(oldcxt);
   12070 CBC           3 :     MemoryContextDelete(perTupCxt);
   12071               3 :     table_endscan(scan);
   12072 GIC           3 :     UnregisterSnapshot(snapshot);
   12073               3 :     ExecDropSingleTupleTableSlot(slot);
   12074                 : }
   12075                 : 
   12076                 : /*
   12077                 :  * CreateFKCheckTrigger
   12078                 :  *      Creates the insert (on_insert=true) or update "check" trigger that
   12079                 :  *      implements a given foreign key
   12080                 :  *
   12081 ECB             :  * Returns the OID of the so created trigger.
   12082                 :  */
   12083                 : static Oid
   12084 GIC        2448 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
   12085 ECB             :                      Oid constraintOid, Oid indexOid, Oid parentTrigOid,
   12086                 :                      bool on_insert)
   12087                 : {
   12088                 :     ObjectAddress trigAddress;
   12089                 :     CreateTrigStmt *fk_trigger;
   12090                 : 
   12091 EUB             :     /*
   12092 ECB             :      * Note: for a self-referential FK (referencing and referenced tables are
   12093                 :      * the same), it is important that the ON UPDATE action fires before the
   12094                 :      * CHECK action, since both triggers will fire on the same row during an
   12095                 :      * UPDATE event; otherwise the CHECK trigger will be checking a non-final
   12096                 :      * state of the row.  Triggers fire in name order, so we ensure this by
   12097                 :      * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
   12098                 :      * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
   12099                 :      */
   12100 CBC        2448 :     fk_trigger = makeNode(CreateTrigStmt);
   12101 GBC        2448 :     fk_trigger->replace = false;
   12102 GIC        2448 :     fk_trigger->isconstraint = true;
   12103            2448 :     fk_trigger->trigname = "RI_ConstraintTrigger_c";
   12104            2448 :     fk_trigger->relation = NULL;
   12105                 : 
   12106 ECB             :     /* Either ON INSERT or ON UPDATE */
   12107 CBC        2448 :     if (on_insert)
   12108                 :     {
   12109            1224 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
   12110 GIC        1224 :         fk_trigger->events = TRIGGER_TYPE_INSERT;
   12111                 :     }
   12112 ECB             :     else
   12113                 :     {
   12114 GIC        1224 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
   12115            1224 :         fk_trigger->events = TRIGGER_TYPE_UPDATE;
   12116                 :     }
   12117 ECB             : 
   12118 GBC        2448 :     fk_trigger->args = NIL;
   12119 GIC        2448 :     fk_trigger->row = true;
   12120            2448 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   12121            2448 :     fk_trigger->columns = NIL;
   12122            2448 :     fk_trigger->whenClause = NULL;
   12123            2448 :     fk_trigger->transitionRels = NIL;
   12124 CBC        2448 :     fk_trigger->deferrable = fkconstraint->deferrable;
   12125 GIC        2448 :     fk_trigger->initdeferred = fkconstraint->initdeferred;
   12126 CBC        2448 :     fk_trigger->constrrel = NULL;
   12127                 : 
   12128 GIC        2448 :     trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
   12129                 :                                 constraintOid, indexOid, InvalidOid,
   12130                 :                                 parentTrigOid, NULL, true, false);
   12131                 : 
   12132 ECB             :     /* Make changes-so-far visible */
   12133 CBC        2448 :     CommandCounterIncrement();
   12134                 : 
   12135            2448 :     return trigAddress.objectId;
   12136                 : }
   12137 ECB             : 
   12138                 : /*
   12139                 :  * createForeignKeyActionTriggers
   12140                 :  *      Create the referenced-side "action" triggers that implement a foreign
   12141                 :  *      key.
   12142                 :  *
   12143                 :  * Returns the OIDs of the so created triggers in *deleteTrigOid and
   12144                 :  * *updateTrigOid.
   12145                 :  */
   12146                 : static void
   12147 GIC        1232 : createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
   12148                 :                                Oid constraintOid, Oid indexOid,
   12149                 :                                Oid parentDelTrigger, Oid parentUpdTrigger,
   12150                 :                                Oid *deleteTrigOid, Oid *updateTrigOid)
   12151                 : {
   12152                 :     CreateTrigStmt *fk_trigger;
   12153                 :     ObjectAddress trigAddress;
   12154                 : 
   12155                 :     /*
   12156                 :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   12157                 :      * DELETE action on the referenced table.
   12158 ECB             :      */
   12159 GIC        1232 :     fk_trigger = makeNode(CreateTrigStmt);
   12160            1232 :     fk_trigger->replace = false;
   12161            1232 :     fk_trigger->isconstraint = true;
   12162 CBC        1232 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
   12163            1232 :     fk_trigger->relation = NULL;
   12164            1232 :     fk_trigger->args = NIL;
   12165 GIC        1232 :     fk_trigger->row = true;
   12166            1232 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   12167            1232 :     fk_trigger->events = TRIGGER_TYPE_DELETE;
   12168            1232 :     fk_trigger->columns = NIL;
   12169            1232 :     fk_trigger->whenClause = NULL;
   12170            1232 :     fk_trigger->transitionRels = NIL;
   12171            1232 :     fk_trigger->constrrel = NULL;
   12172            1232 :     switch (fkconstraint->fk_del_action)
   12173                 :     {
   12174             927 :         case FKCONSTR_ACTION_NOACTION:
   12175             927 :             fk_trigger->deferrable = fkconstraint->deferrable;
   12176             927 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
   12177 CBC         927 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
   12178 GIC         927 :             break;
   12179 CBC          15 :         case FKCONSTR_ACTION_RESTRICT:
   12180 GIC          15 :             fk_trigger->deferrable = false;
   12181 CBC          15 :             fk_trigger->initdeferred = false;
   12182 GBC          15 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
   12183 GIC          15 :             break;
   12184             218 :         case FKCONSTR_ACTION_CASCADE:
   12185             218 :             fk_trigger->deferrable = false;
   12186             218 :             fk_trigger->initdeferred = false;
   12187             218 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
   12188             218 :             break;
   12189              42 :         case FKCONSTR_ACTION_SETNULL:
   12190              42 :             fk_trigger->deferrable = false;
   12191              42 :             fk_trigger->initdeferred = false;
   12192              42 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
   12193 CBC          42 :             break;
   12194 GIC          30 :         case FKCONSTR_ACTION_SETDEFAULT:
   12195 CBC          30 :             fk_trigger->deferrable = false;
   12196 GIC          30 :             fk_trigger->initdeferred = false;
   12197              30 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
   12198              30 :             break;
   12199 UIC           0 :         default:
   12200 LBC           0 :             elog(ERROR, "unrecognized FK action type: %d",
   12201 ECB             :                  (int) fkconstraint->fk_del_action);
   12202                 :             break;
   12203 EUB             :     }
   12204 ECB             : 
   12205 GIC        1232 :     trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
   12206                 :                                 RelationGetRelid(rel),
   12207                 :                                 constraintOid, indexOid, InvalidOid,
   12208                 :                                 parentDelTrigger, NULL, true, false);
   12209            1232 :     if (deleteTrigOid)
   12210            1202 :         *deleteTrigOid = trigAddress.objectId;
   12211 ECB             : 
   12212                 :     /* Make changes-so-far visible */
   12213 CBC        1232 :     CommandCounterIncrement();
   12214 ECB             : 
   12215                 :     /*
   12216                 :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   12217                 :      * UPDATE action on the referenced table.
   12218                 :      */
   12219 GIC        1232 :     fk_trigger = makeNode(CreateTrigStmt);
   12220            1232 :     fk_trigger->replace = false;
   12221 CBC        1232 :     fk_trigger->isconstraint = true;
   12222 GIC        1232 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
   12223 CBC        1232 :     fk_trigger->relation = NULL;
   12224 GIC        1232 :     fk_trigger->args = NIL;
   12225            1232 :     fk_trigger->row = true;
   12226            1232 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   12227            1232 :     fk_trigger->events = TRIGGER_TYPE_UPDATE;
   12228            1232 :     fk_trigger->columns = NIL;
   12229            1232 :     fk_trigger->whenClause = NULL;
   12230            1232 :     fk_trigger->transitionRels = NIL;
   12231            1232 :     fk_trigger->constrrel = NULL;
   12232            1232 :     switch (fkconstraint->fk_upd_action)
   12233                 :     {
   12234            1021 :         case FKCONSTR_ACTION_NOACTION:
   12235 CBC        1021 :             fk_trigger->deferrable = fkconstraint->deferrable;
   12236 GIC        1021 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
   12237 CBC        1021 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
   12238            1021 :             break;
   12239 GIC          15 :         case FKCONSTR_ACTION_RESTRICT:
   12240 CBC          15 :             fk_trigger->deferrable = false;
   12241 GIC          15 :             fk_trigger->initdeferred = false;
   12242 CBC          15 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
   12243              15 :             break;
   12244             144 :         case FKCONSTR_ACTION_CASCADE:
   12245 GIC         144 :             fk_trigger->deferrable = false;
   12246             144 :             fk_trigger->initdeferred = false;
   12247 CBC         144 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
   12248             144 :             break;
   12249 GIC          31 :         case FKCONSTR_ACTION_SETNULL:
   12250              31 :             fk_trigger->deferrable = false;
   12251              31 :             fk_trigger->initdeferred = false;
   12252              31 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
   12253              31 :             break;
   12254              21 :         case FKCONSTR_ACTION_SETDEFAULT:
   12255              21 :             fk_trigger->deferrable = false;
   12256 CBC          21 :             fk_trigger->initdeferred = false;
   12257 GIC          21 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
   12258              21 :             break;
   12259 UIC           0 :         default:
   12260               0 :             elog(ERROR, "unrecognized FK action type: %d",
   12261                 :                  (int) fkconstraint->fk_upd_action);
   12262 EUB             :             break;
   12263                 :     }
   12264                 : 
   12265 GIC        1232 :     trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
   12266 ECB             :                                 RelationGetRelid(rel),
   12267                 :                                 constraintOid, indexOid, InvalidOid,
   12268                 :                                 parentUpdTrigger, NULL, true, false);
   12269 GIC        1232 :     if (updateTrigOid)
   12270            1202 :         *updateTrigOid = trigAddress.objectId;
   12271 CBC        1232 : }
   12272                 : 
   12273 ECB             : /*
   12274 EUB             :  * createForeignKeyCheckTriggers
   12275                 :  *      Create the referencing-side "check" triggers that implement a foreign
   12276                 :  *      key.
   12277                 :  *
   12278                 :  * Returns the OIDs of the so created triggers in *insertTrigOid and
   12279 ECB             :  * *updateTrigOid.
   12280                 :  */
   12281                 : static void
   12282 GIC        1224 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
   12283                 :                               Constraint *fkconstraint, Oid constraintOid,
   12284                 :                               Oid indexOid,
   12285 ECB             :                               Oid parentInsTrigger, Oid parentUpdTrigger,
   12286                 :                               Oid *insertTrigOid, Oid *updateTrigOid)
   12287                 : {
   12288 GIC        1224 :     *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   12289                 :                                           constraintOid, indexOid,
   12290                 :                                           parentInsTrigger, true);
   12291            1224 :     *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   12292                 :                                           constraintOid, indexOid,
   12293                 :                                           parentUpdTrigger, false);
   12294            1224 : }
   12295                 : 
   12296                 : /*
   12297 ECB             :  * ALTER TABLE DROP CONSTRAINT
   12298                 :  *
   12299                 :  * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
   12300                 :  */
   12301                 : static void
   12302 GIC         261 : ATExecDropConstraint(Relation rel, const char *constrName,
   12303 ECB             :                      DropBehavior behavior,
   12304                 :                      bool recurse, bool recursing,
   12305                 :                      bool missing_ok, LOCKMODE lockmode)
   12306                 : {
   12307                 :     Relation    conrel;
   12308                 :     SysScanDesc scan;
   12309 EUB             :     ScanKeyData skey[3];
   12310                 :     HeapTuple   tuple;
   12311 GIC         261 :     bool        found = false;
   12312                 : 
   12313                 :     /* At top level, permission check was done in ATPrepCmd, else do it */
   12314             261 :     if (recursing)
   12315 UIC           0 :         ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
   12316                 : 
   12317 GIC         261 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12318                 : 
   12319                 :     /*
   12320                 :      * Find and drop the target constraint
   12321 ECB             :      */
   12322 GIC         261 :     ScanKeyInit(&skey[0],
   12323 ECB             :                 Anum_pg_constraint_conrelid,
   12324                 :                 BTEqualStrategyNumber, F_OIDEQ,
   12325                 :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12326 GIC         261 :     ScanKeyInit(&skey[1],
   12327                 :                 Anum_pg_constraint_contypid,
   12328 ECB             :                 BTEqualStrategyNumber, F_OIDEQ,
   12329                 :                 ObjectIdGetDatum(InvalidOid));
   12330 CBC         261 :     ScanKeyInit(&skey[2],
   12331 ECB             :                 Anum_pg_constraint_conname,
   12332                 :                 BTEqualStrategyNumber, F_NAMEEQ,
   12333 EUB             :                 CStringGetDatum(constrName));
   12334 GIC         261 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12335 EUB             :                               true, NULL, 3, skey);
   12336                 : 
   12337                 :     /* There can be at most one matching row */
   12338 GBC         261 :     if (HeapTupleIsValid(tuple = systable_getnext(scan)))
   12339 EUB             :     {
   12340 GNC         246 :         dropconstraint_internal(rel, tuple, behavior, recurse, recursing,
   12341                 :                                 missing_ok, NULL, lockmode);
   12342 GIC         168 :         found = true;
   12343                 :     }
   12344                 : 
   12345 CBC         183 :     systable_endscan(scan);
   12346 ECB             : 
   12347 GIC         183 :     if (!found)
   12348                 :     {
   12349              15 :         if (!missing_ok)
   12350               9 :             ereport(ERROR,
   12351                 :                     errcode(ERRCODE_UNDEFINED_OBJECT),
   12352                 :                     errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12353                 :                            constrName, RelationGetRelationName(rel)));
   12354                 :         else
   12355               6 :             ereport(NOTICE,
   12356                 :                     errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
   12357                 :                            constrName, RelationGetRelationName(rel)));
   12358                 :     }
   12359                 : 
   12360 GNC         174 :     table_close(conrel, RowExclusiveLock);
   12361             174 : }
   12362                 : 
   12363                 : /*
   12364                 :  * Remove a constraint, using its pg_constraint tuple
   12365                 :  *
   12366                 :  * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
   12367                 :  * DROP NOT NULL.
   12368                 :  *
   12369                 :  * Returns the address of the constraint being removed.
   12370                 :  */
   12371                 : static ObjectAddress
   12372             401 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
   12373                 :                         bool recurse, bool recursing, bool missing_ok, List **readyRels,
   12374                 :                         LOCKMODE lockmode)
   12375                 : {
   12376                 :     Relation    conrel;
   12377                 :     Form_pg_constraint con;
   12378                 :     ObjectAddress conobj;
   12379                 :     List       *children;
   12380                 :     ListCell   *child;
   12381             401 :     bool        is_no_inherit_constraint = false;
   12382             401 :     bool        dropping_pk = false;
   12383                 :     char       *constrName;
   12384             401 :     List       *unconstrained_cols = NIL;
   12385                 :     char       *colname;    /* to match NOT NULL constraints when recursing */
   12386             401 :     List       *ready = NIL;
   12387                 : 
   12388             401 :     if (readyRels == NULL)
   12389             323 :         readyRels = &ready;
   12390             401 :     if (list_member_oid(*readyRels, RelationGetRelid(rel)))
   12391 UNC           0 :         return InvalidObjectAddress;
   12392 GNC         401 :     *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
   12393                 : 
   12394             401 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12395                 : 
   12396             401 :     con = (Form_pg_constraint) GETSTRUCT(constraintTup);
   12397             401 :     constrName = NameStr(con->conname);
   12398                 : 
   12399                 :     /*
   12400                 :      * If the constraint is marked conislocal and is also inherited, then we
   12401                 :      * just set conislocal false and we're done.  The constraint doesn't go
   12402                 :      * away, and we don't modify any children.
   12403                 :      */
   12404             401 :     if (con->conislocal && con->coninhcount > 0)
   12405                 :     {
   12406                 :         HeapTuple   copytup;
   12407                 : 
   12408                 :         /* make a copy we can scribble on */
   12409               6 :         copytup = heap_copytuple(constraintTup);
   12410               6 :         con = (Form_pg_constraint) GETSTRUCT(copytup);
   12411               6 :         con->conislocal = false;
   12412               6 :         CatalogTupleUpdate(conrel, &copytup->t_self, copytup);
   12413                 : 
   12414               6 :         table_close(conrel, RowExclusiveLock);
   12415                 : 
   12416               6 :         ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
   12417               6 :         return conobj;
   12418                 :     }
   12419                 : 
   12420                 :     /* Don't drop inherited constraints */
   12421             395 :     if (con->coninhcount > 0 && !recursing)
   12422              60 :         ereport(ERROR,
   12423                 :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12424                 :                  errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
   12425                 :                         constrName, RelationGetRelationName(rel))));
   12426                 : 
   12427                 :     /*
   12428                 :      * See if we have a NOT NULL constraint or a PRIMARY KEY.  If so, we have
   12429                 :      * more checks and actions below, so obtain the list of columns that are
   12430                 :      * constrained by the constraint being dropped.
   12431                 :      */
   12432             335 :     if (con->contype == CONSTRAINT_NOTNULL)
   12433                 :     {
   12434              88 :         AttrNumber  colnum = extractNotNullColumn(constraintTup);
   12435                 : 
   12436              88 :         if (colnum != InvalidAttrNumber)
   12437              88 :             unconstrained_cols = list_make1_int(colnum);
   12438                 :     }
   12439             247 :     else if (con->contype == CONSTRAINT_PRIMARY)
   12440                 :     {
   12441                 :         Datum       adatum;
   12442                 :         ArrayType  *arr;
   12443                 :         int         numkeys;
   12444                 :         bool        isNull;
   12445                 :         int16      *attnums;
   12446                 : 
   12447              61 :         dropping_pk = true;
   12448                 : 
   12449              61 :         adatum = heap_getattr(constraintTup, Anum_pg_constraint_conkey,
   12450                 :                               RelationGetDescr(conrel), &isNull);
   12451              61 :         if (isNull)
   12452 UNC           0 :             elog(ERROR, "null conkey for constraint %u", con->oid);
   12453 GNC          61 :         arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
   12454              61 :         numkeys = ARR_DIMS(arr)[0];
   12455              61 :         if (ARR_NDIM(arr) != 1 ||
   12456              61 :             numkeys < 0 ||
   12457              61 :             ARR_HASNULL(arr) ||
   12458              61 :             ARR_ELEMTYPE(arr) != INT2OID)
   12459 UNC           0 :             elog(ERROR, "conkey is not a 1-D smallint array");
   12460 GNC          61 :         attnums = (int16 *) ARR_DATA_PTR(arr);
   12461                 : 
   12462             138 :         for (int i = 0; i < numkeys; i++)
   12463              77 :             unconstrained_cols = lappend_int(unconstrained_cols, attnums[i]);
   12464                 :     }
   12465                 : 
   12466             335 :     is_no_inherit_constraint = con->connoinherit;
   12467                 : 
   12468                 :     /*
   12469                 :      * If it's a foreign-key constraint, we'd better lock the referenced table
   12470                 :      * and check that that's not in use, just as we've already done for the
   12471                 :      * constrained table (else we might, eg, be dropping a trigger that has
   12472                 :      * unfired events).  But we can/must skip that in the self-referential
   12473                 :      * case.
   12474                 :      */
   12475             335 :     if (con->contype == CONSTRAINT_FOREIGN &&
   12476              36 :         con->confrelid != RelationGetRelid(rel))
   12477                 :     {
   12478                 :         Relation    frel;
   12479                 : 
   12480                 :         /* Must match lock taken by RemoveTriggerById: */
   12481              36 :         frel = table_open(con->confrelid, AccessExclusiveLock);
   12482              36 :         CheckTableNotInUse(frel, "ALTER TABLE");
   12483              33 :         table_close(frel, NoLock);
   12484                 :     }
   12485                 : 
   12486                 :     /*
   12487                 :      * Perform the actual constraint deletion
   12488                 :      */
   12489             332 :     ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
   12490             332 :     performDeletion(&conobj, behavior, 0);
   12491                 : 
   12492                 :     /*
   12493                 :      * If this was a NOT NULL or the primary key, the constrained columns must
   12494                 :      * have had pg_attribute.attnotnull set.  See if we need to reset it, and
   12495                 :      * do so.
   12496                 :      */
   12497             314 :     if (unconstrained_cols)
   12498                 :     {
   12499                 :         Relation    attrel;
   12500                 :         Bitmapset  *pkcols;
   12501                 :         Bitmapset  *ircols;
   12502                 :         ListCell   *lc;
   12503                 : 
   12504                 :         /* Make the above deletion visible */
   12505             131 :         CommandCounterIncrement();
   12506                 : 
   12507             131 :         attrel = table_open(AttributeRelationId, RowExclusiveLock);
   12508                 : 
   12509                 :         /*
   12510                 :          * We want to test columns for their presence in the primary key, but
   12511                 :          * only if we're not dropping it.
   12512                 :          */
   12513             131 :         pkcols = dropping_pk ? NULL :
   12514              88 :             RelationGetIndexAttrBitmap(rel,
   12515                 :                                        INDEX_ATTR_BITMAP_PRIMARY_KEY);
   12516             131 :         ircols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
   12517                 : 
   12518             263 :         foreach(lc, unconstrained_cols)
   12519                 :         {
   12520             141 :             AttrNumber  attnum = lfirst_int(lc);
   12521                 :             HeapTuple   atttup;
   12522                 :             HeapTuple   contup;
   12523                 :             Form_pg_attribute attForm;
   12524                 : 
   12525                 :             /*
   12526                 :              * Obtain pg_attribute tuple and verify conditions on it.  We use
   12527                 :              * a copy we can scribble on.
   12528                 :              */
   12529             141 :             atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
   12530             141 :             if (!HeapTupleIsValid(atttup))
   12531 UNC           0 :                 elog(ERROR, "cache lookup failed for column %d", attnum);
   12532 GNC         141 :             attForm = (Form_pg_attribute) GETSTRUCT(atttup);
   12533                 : 
   12534                 :             /*
   12535                 :              * Since the above deletion has been made visible, we can now
   12536                 :              * search for any remaining constraints on this column (or these
   12537                 :              * columns, in the case we're dropping a multicol primary key.)
   12538                 :              * Then, verify whether any further NOT NULL or primary key
   12539                 :              * exists, and reset attnotnull if none.
   12540                 :              *
   12541                 :              * However, if this is a generated identity column, abort the
   12542                 :              * whole thing with a specific error message, because the
   12543                 :              * constraint is required in that case.
   12544                 :              */
   12545             141 :             contup = findNotNullConstraintAttnum(rel, attnum);
   12546             273 :             if (contup ||
   12547             132 :                 bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
   12548                 :                               pkcols))
   12549              12 :                 continue;
   12550                 : 
   12551                 :             /*
   12552                 :              * It's not valid to drop the last NOT NULL constraint for a
   12553                 :              * GENERATED AS IDENTITY column.
   12554                 :              */
   12555             129 :             if (attForm->attidentity)
   12556 UNC           0 :                 ereport(ERROR,
   12557                 :                         errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12558                 :                         errmsg("column \"%s\" of relation \"%s\" is an identity column",
   12559                 :                                get_attname(RelationGetRelid(rel), attnum,
   12560                 :                                            false),
   12561                 :                                RelationGetRelationName(rel)));
   12562                 : 
   12563                 :             /*
   12564                 :              * It's not valid to drop the last NOT NULL constraint for the
   12565                 :              * replica identity either.  XXX make exception for FULL?
   12566                 :              */
   12567 GNC         129 :             if (bms_is_member(lfirst_int(lc) - FirstLowInvalidHeapAttributeNumber, ircols))
   12568               9 :                 ereport(ERROR,
   12569                 :                         errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12570                 :                         errmsg("column \"%s\" is in index used as replica identity",
   12571                 :                                get_attname(RelationGetRelid(rel), lfirst_int(lc), false)));
   12572                 : 
   12573                 :             /* Reset attnotnull */
   12574             120 :             if (attForm->attnotnull)
   12575                 :             {
   12576             120 :                 attForm->attnotnull = false;
   12577             120 :                 CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
   12578                 :             }
   12579                 :         }
   12580             122 :         table_close(attrel, RowExclusiveLock);
   12581 ECB             :     }
   12582                 : 
   12583                 :     /*
   12584                 :      * For partitioned tables, non-CHECK inherited constraints are dropped via
   12585                 :      * the dependency mechanism, so we're done here.
   12586                 :      */
   12587 GNC         305 :     if (con->contype != CONSTRAINT_CHECK &&
   12588 GIC         179 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   12589                 :     {
   12590              24 :         table_close(conrel, RowExclusiveLock);
   12591 GNC          24 :         return conobj;
   12592                 :     }
   12593                 : 
   12594                 :     /*
   12595                 :      * Propagate to children as appropriate.  Unlike most other ALTER
   12596 ECB             :      * routines, we have to do this one level of recursion at a time; we can't
   12597                 :      * use find_all_inheritors to do it in one pass.
   12598                 :      */
   12599 CBC         281 :     if (!is_no_inherit_constraint)
   12600             208 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
   12601 ECB             :     else
   12602 GIC          73 :         children = NIL;
   12603 ECB             : 
   12604                 :     /*
   12605                 :      * For a partitioned table, if partitions exist and we are told not to
   12606                 :      * recurse, it's a user error.  It doesn't make sense to have a constraint
   12607                 :      * be defined only on the parent, especially if it's a partitioned table.
   12608                 :      */
   12609 GIC         281 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
   12610 CBC          31 :         children != NIL && !recurse)
   12611               3 :         ereport(ERROR,
   12612 ECB             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12613                 :                  errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
   12614                 :                  errhint("Do not specify the ONLY keyword.")));
   12615                 : 
   12616                 :     /* For NOT NULL constraints we recurse by column name */
   12617 GNC         278 :     if (con->contype == CONSTRAINT_NOTNULL)
   12618              79 :         colname = NameStr(TupleDescAttr(RelationGetDescr(rel),
   12619                 :                                         linitial_int(unconstrained_cols) - 1)->attname);
   12620                 :     else
   12621             199 :         colname = NULL;     /* keep compiler quiet */
   12622                 : 
   12623 GIC         365 :     foreach(child, children)
   12624                 :     {
   12625              87 :         Oid         childrelid = lfirst_oid(child);
   12626                 :         Relation    childrel;
   12627                 :         HeapTuple   tuple;
   12628                 :         Form_pg_constraint childcon;
   12629                 :         HeapTuple   copy_tuple;
   12630                 :         SysScanDesc scan;
   12631                 :         ScanKeyData skey[3];
   12632                 : 
   12633 GNC          87 :         if (list_member_oid(*readyRels, childrelid))
   12634               3 :             continue;       /* child already processed */
   12635                 : 
   12636                 :         /* find_inheritance_children already got lock */
   12637 GIC          84 :         childrel = table_open(childrelid, NoLock);
   12638              84 :         CheckTableNotInUse(childrel, "ALTER TABLE");
   12639 ECB             : 
   12640                 :         /*
   12641                 :          * We search for NOT NULL constraint by column number, and other
   12642                 :          * constraints by name.
   12643                 :          */
   12644 GNC          84 :         if (con->contype == CONSTRAINT_NOTNULL)
   12645                 :         {
   12646              26 :             bool    found = false;
   12647                 :             AttrNumber child_colnum;
   12648                 :             HeapTuple   child_tup;
   12649                 : 
   12650              26 :             child_colnum = get_attnum(RelationGetRelid(childrel), colname);
   12651              26 :             ScanKeyInit(&skey[0],
   12652                 :                         Anum_pg_constraint_conrelid,
   12653                 :                         BTEqualStrategyNumber, F_OIDEQ,
   12654                 :                         ObjectIdGetDatum(childrelid));
   12655              26 :             scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12656                 :                                       true, NULL, 1, skey);
   12657              37 :             while (HeapTupleIsValid(child_tup = systable_getnext(scan)))
   12658                 :             {
   12659              37 :                 Form_pg_constraint constr = (Form_pg_constraint) GETSTRUCT(child_tup);
   12660                 :                 AttrNumber  constr_colnum;
   12661 ECB             : 
   12662 GNC          37 :                 if (constr->contype != CONSTRAINT_NOTNULL)
   12663               2 :                     continue;
   12664              35 :                 constr_colnum = extractNotNullColumn(child_tup);
   12665              35 :                 if (constr_colnum != child_colnum)
   12666               9 :                     continue;
   12667 ECB             : 
   12668 GNC          26 :                 found = true;
   12669              26 :                 break;  /* found it */
   12670                 :             }
   12671              26 :             if (!found) /* shouldn't happen? */
   12672 UNC           0 :                 elog(ERROR, "failed to find NOT NULL constraint for column \"%s\" in table \"%s\"",
   12673                 :                      colname, RelationGetRelationName(childrel));
   12674                 : 
   12675 GNC          26 :             copy_tuple = heap_copytuple(child_tup);
   12676              26 :             systable_endscan(scan);
   12677                 :         }
   12678                 :         else
   12679                 :         {
   12680              58 :             ScanKeyInit(&skey[0],
   12681                 :                         Anum_pg_constraint_conrelid,
   12682                 :                         BTEqualStrategyNumber, F_OIDEQ,
   12683                 :                         ObjectIdGetDatum(childrelid));
   12684              58 :             ScanKeyInit(&skey[1],
   12685                 :                         Anum_pg_constraint_contypid,
   12686                 :                         BTEqualStrategyNumber, F_OIDEQ,
   12687                 :                         ObjectIdGetDatum(InvalidOid));
   12688              58 :             ScanKeyInit(&skey[2],
   12689                 :                         Anum_pg_constraint_conname,
   12690                 :                         BTEqualStrategyNumber, F_NAMEEQ,
   12691                 :                         CStringGetDatum(constrName));
   12692              58 :             scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12693                 :                                       true, NULL, 3, skey);
   12694                 :             /* There can only be one, so no need to loop */
   12695              58 :             tuple = systable_getnext(scan);
   12696              58 :             if (!HeapTupleIsValid(tuple))
   12697 UNC           0 :                 ereport(ERROR,
   12698                 :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
   12699                 :                          errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12700                 :                                 constrName,
   12701                 :                                 RelationGetRelationName(childrel))));
   12702 GNC          58 :             copy_tuple = heap_copytuple(tuple);
   12703              58 :             systable_endscan(scan);
   12704                 :         }
   12705                 : 
   12706              84 :         childcon = (Form_pg_constraint) GETSTRUCT(copy_tuple);
   12707                 : 
   12708                 :         /* Right now only CHECK and NOT NULL constraints can be inherited */
   12709              84 :         if (childcon->contype != CONSTRAINT_CHECK &&
   12710              26 :             childcon->contype != CONSTRAINT_NOTNULL)
   12711 UNC           0 :             elog(ERROR, "inherited constraint is not a CHECK or NOT NULL constraint");
   12712                 : 
   12713 GNC          84 :         if (childcon->coninhcount <= 0)   /* shouldn't happen */
   12714 LBC           0 :             elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   12715 ECB             :                  childrelid, constrName);
   12716                 : 
   12717 CBC          84 :         if (recurse)
   12718 ECB             :         {
   12719                 :             /*
   12720                 :              * If the child constraint has other definition sources, just
   12721                 :              * decrement its inheritance count; if not, recurse to delete it.
   12722                 :              */
   12723 GNC          81 :             if (childcon->coninhcount == 1 && !childcon->conislocal)
   12724                 :             {
   12725                 :                 /* Time to delete this child constraint, too */
   12726              78 :                 dropconstraint_internal(childrel, copy_tuple, behavior,
   12727                 :                                         recurse, true, missing_ok, readyRels,
   12728                 :                                         lockmode);
   12729                 :             }
   12730 ECB             :             else
   12731                 :             {
   12732                 :                 /* Child constraint must survive my deletion */
   12733 GNC           3 :                 childcon->coninhcount--;
   12734 GIC           3 :                 CatalogTupleUpdate(conrel, &copy_tuple->t_self, copy_tuple);
   12735                 : 
   12736                 :                 /* Make update visible */
   12737               3 :                 CommandCounterIncrement();
   12738                 :             }
   12739                 :         }
   12740                 :         else
   12741                 :         {
   12742 ECB             :             /*
   12743                 :              * If we were told to drop ONLY in this table (no recursion), we
   12744                 :              * need to mark the inheritors' constraints as locally defined
   12745                 :              * rather than inherited.
   12746                 :              */
   12747 GNC           3 :             childcon->coninhcount--;
   12748               3 :             childcon->conislocal = true;
   12749                 : 
   12750 GIC           3 :             CatalogTupleUpdate(conrel, &copy_tuple->t_self, copy_tuple);
   12751                 : 
   12752                 :             /* Make update visible */
   12753               3 :             CommandCounterIncrement();
   12754 ECB             :         }
   12755                 : 
   12756 CBC          84 :         heap_freetuple(copy_tuple);
   12757 ECB             : 
   12758 CBC          84 :         table_close(childrel, NoLock);
   12759 ECB             :     }
   12760                 : 
   12761 CBC         278 :     table_close(conrel, RowExclusiveLock);
   12762                 : 
   12763 GNC         278 :     return conobj;
   12764 ECB             : }
   12765                 : 
   12766                 : /*
   12767                 :  * ALTER COLUMN TYPE
   12768                 :  *
   12769                 :  * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
   12770                 :  * TYPE during phase 1 --- the AlterTableCmd passed in here is already
   12771                 :  * transformed (and must be, because we rely on some transformed fields).
   12772                 :  *
   12773                 :  * The point of this is that the execution of all ALTER COLUMN TYPEs for a
   12774                 :  * table will be done "in parallel" during phase 3, so all the USING
   12775                 :  * expressions should be parsed assuming the original column types.  Also,
   12776                 :  * this allows a USING expression to refer to a field that will be dropped.
   12777                 :  *
   12778                 :  * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
   12779                 :  * the first two execution steps in phase 2; they must not see the effects
   12780                 :  * of any other subcommand types, since the USING expressions are parsed
   12781                 :  * against the unmodified table's state.
   12782                 :  */
   12783                 : static void
   12784 CBC         550 : ATPrepAlterColumnType(List **wqueue,
   12785 ECB             :                       AlteredTableInfo *tab, Relation rel,
   12786                 :                       bool recurse, bool recursing,
   12787                 :                       AlterTableCmd *cmd, LOCKMODE lockmode,
   12788                 :                       AlterTableUtilityContext *context)
   12789                 : {
   12790 CBC         550 :     char       *colName = cmd->name;
   12791             550 :     ColumnDef  *def = (ColumnDef *) cmd->def;
   12792             550 :     TypeName   *typeName = def->typeName;
   12793             550 :     Node       *transform = def->cooked_default;
   12794 ECB             :     HeapTuple   tuple;
   12795                 :     Form_pg_attribute attTup;
   12796 EUB             :     AttrNumber  attnum;
   12797                 :     Oid         targettype;
   12798                 :     int32       targettypmod;
   12799                 :     Oid         targetcollid;
   12800                 :     NewColumnValue *newval;
   12801 GIC         550 :     ParseState *pstate = make_parsestate(NULL);
   12802 ECB             :     AclResult   aclresult;
   12803                 :     bool        is_expr;
   12804                 : 
   12805 GIC         550 :     if (rel->rd_rel->reloftype && !recursing)
   12806 CBC           3 :         ereport(ERROR,
   12807 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12808                 :                  errmsg("cannot alter column type of typed table")));
   12809                 : 
   12810                 :     /* lookup the attribute so we can check inheritance status */
   12811 GIC         547 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   12812             547 :     if (!HeapTupleIsValid(tuple))
   12813 UIC           0 :         ereport(ERROR,
   12814                 :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   12815                 :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   12816 ECB             :                         colName, RelationGetRelationName(rel))));
   12817 CBC         547 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
   12818             547 :     attnum = attTup->attnum;
   12819 ECB             : 
   12820                 :     /* Can't alter a system attribute */
   12821 CBC         547 :     if (attnum <= 0)
   12822 LBC           0 :         ereport(ERROR,
   12823 ECB             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   12824                 :                  errmsg("cannot alter system column \"%s\"",
   12825                 :                         colName)));
   12826                 : 
   12827                 :     /*
   12828                 :      * Don't alter inherited columns.  At outer level, there had better not be
   12829                 :      * any inherited definition; when recursing, we assume this was checked at
   12830                 :      * the parent level (see below).
   12831                 :      */
   12832 CBC         547 :     if (attTup->attinhcount > 0 && !recursing)
   12833               3 :         ereport(ERROR,
   12834 ECB             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12835                 :                  errmsg("cannot alter inherited column \"%s\"",
   12836                 :                         colName)));
   12837                 : 
   12838                 :     /* Don't alter columns used in the partition key */
   12839 CBC         544 :     if (has_partition_attrs(rel,
   12840 ECB             :                             bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
   12841                 :                             &is_expr))
   12842 CBC           9 :         ereport(ERROR,
   12843 ECB             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12844                 :                  errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
   12845                 :                         colName, RelationGetRelationName(rel))));
   12846                 : 
   12847                 :     /* Look up the target type */
   12848 CBC         535 :     typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
   12849 ECB             : 
   12850 GNC         535 :     aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
   12851 CBC         535 :     if (aclresult != ACLCHECK_OK)
   12852               6 :         aclcheck_error_type(aclresult, targettype);
   12853 ECB             : 
   12854                 :     /* And the collation */
   12855 CBC         529 :     targetcollid = GetColumnDefCollation(NULL, def, targettype);
   12856 EUB             : 
   12857                 :     /* make sure datatype is legal for a column */
   12858 GIC         529 :     CheckAttributeType(colName, targettype, targetcollid,
   12859             529 :                        list_make1_oid(rel->rd_rel->reltype),
   12860                 :                        0);
   12861                 : 
   12862 CBC         526 :     if (tab->relkind == RELKIND_RELATION ||
   12863 GIC          95 :         tab->relkind == RELKIND_PARTITIONED_TABLE)
   12864                 :     {
   12865                 :         /*
   12866 ECB             :          * Set up an expression to transform the old data value to the new
   12867                 :          * type. If a USING option was given, use the expression as
   12868                 :          * transformed by transformAlterTableStmt, else just take the old
   12869                 :          * value and try to coerce it.  We do this first so that type
   12870                 :          * incompatibility can be detected before we waste effort, and because
   12871                 :          * we need the expression to be parsed against the original table row
   12872                 :          * type.
   12873                 :          */
   12874 GIC         458 :         if (!transform)
   12875                 :         {
   12876             356 :             transform = (Node *) makeVar(1, attnum,
   12877                 :                                          attTup->atttypid, attTup->atttypmod,
   12878                 :                                          attTup->attcollation,
   12879 ECB             :                                          0);
   12880                 :         }
   12881                 : 
   12882 GIC         458 :         transform = coerce_to_target_type(pstate,
   12883                 :                                           transform, exprType(transform),
   12884                 :                                           targettype, targettypmod,
   12885 ECB             :                                           COERCION_ASSIGNMENT,
   12886                 :                                           COERCE_IMPLICIT_CAST,
   12887                 :                                           -1);
   12888 CBC         458 :         if (transform == NULL)
   12889                 :         {
   12890                 :             /* error text depends on whether USING was specified or not */
   12891              12 :             if (def->cooked_default != NULL)
   12892 GIC           3 :                 ereport(ERROR,
   12893                 :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   12894                 :                          errmsg("result of USING clause for column \"%s\""
   12895                 :                                 " cannot be cast automatically to type %s",
   12896                 :                                 colName, format_type_be(targettype)),
   12897                 :                          errhint("You might need to add an explicit cast.")));
   12898                 :             else
   12899 CBC           9 :                 ereport(ERROR,
   12900                 :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   12901                 :                          errmsg("column \"%s\" cannot be cast automatically to type %s",
   12902                 :                                 colName, format_type_be(targettype)),
   12903                 :                 /* translator: USING is SQL, don't translate it */
   12904                 :                          errhint("You might need to specify \"USING %s::%s\".",
   12905                 :                                  quote_identifier(colName),
   12906                 :                                  format_type_with_typemod(targettype,
   12907                 :                                                           targettypmod))));
   12908 ECB             :         }
   12909                 : 
   12910                 :         /* Fix collations after all else */
   12911 CBC         446 :         assign_expr_collations(pstate, transform);
   12912 EUB             : 
   12913                 :         /* Plan the expr now so we can accurately assess the need to rewrite. */
   12914 CBC         446 :         transform = (Node *) expression_planner((Expr *) transform);
   12915                 : 
   12916                 :         /*
   12917                 :          * Add a work queue item to make ATRewriteTable update the column
   12918                 :          * contents.
   12919 ECB             :          */
   12920 GIC         446 :         newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
   12921             446 :         newval->attnum = attnum;
   12922             446 :         newval->expr = (Expr *) transform;
   12923 CBC         446 :         newval->is_generated = false;
   12924                 : 
   12925 GIC         446 :         tab->newvals = lappend(tab->newvals, newval);
   12926             446 :         if (ATColumnChangeRequiresRewrite(transform, attnum))
   12927 CBC         364 :             tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
   12928                 :     }
   12929 GIC          68 :     else if (transform)
   12930               6 :         ereport(ERROR,
   12931 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12932                 :                  errmsg("\"%s\" is not a table",
   12933                 :                         RelationGetRelationName(rel))));
   12934                 : 
   12935 CBC         508 :     if (!RELKIND_HAS_STORAGE(tab->relkind))
   12936                 :     {
   12937 ECB             :         /*
   12938                 :          * For relations without storage, do this check now.  Regular tables
   12939                 :          * will check it later when the table is being rewritten.
   12940                 :          */
   12941 GIC          89 :         find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
   12942 ECB             :     }
   12943                 : 
   12944 CBC         493 :     ReleaseSysCache(tuple);
   12945                 : 
   12946 ECB             :     /*
   12947                 :      * Recurse manually by queueing a new command for each child, if
   12948                 :      * necessary. We cannot apply ATSimpleRecursion here because we need to
   12949                 :      * remap attribute numbers in the USING expression, if any.
   12950                 :      *
   12951                 :      * If we are told not to recurse, there had better not be any child
   12952                 :      * tables; else the alter would put them out of step.
   12953                 :      */
   12954 GIC         493 :     if (recurse)
   12955                 :     {
   12956             373 :         Oid         relid = RelationGetRelid(rel);
   12957 ECB             :         List       *child_oids,
   12958                 :                    *child_numparents;
   12959                 :         ListCell   *lo,
   12960                 :                    *li;
   12961                 : 
   12962 GIC         373 :         child_oids = find_all_inheritors(relid, lockmode,
   12963                 :                                          &child_numparents);
   12964                 : 
   12965                 :         /*
   12966                 :          * find_all_inheritors does the recursive search of the inheritance
   12967                 :          * hierarchy, so all we have to do is process all of the relids in the
   12968                 :          * list that it returns.
   12969 ECB             :          */
   12970 GIC         841 :         forboth(lo, child_oids, li, child_numparents)
   12971                 :         {
   12972             480 :             Oid         childrelid = lfirst_oid(lo);
   12973             480 :             int         numparents = lfirst_int(li);
   12974                 :             Relation    childrel;
   12975                 :             HeapTuple   childtuple;
   12976                 :             Form_pg_attribute childattTup;
   12977                 : 
   12978 CBC         480 :             if (childrelid == relid)
   12979             373 :                 continue;
   12980                 : 
   12981 ECB             :             /* find_all_inheritors already got lock */
   12982 GIC         107 :             childrel = relation_open(childrelid, NoLock);
   12983 CBC         107 :             CheckTableNotInUse(childrel, "ALTER TABLE");
   12984                 : 
   12985 ECB             :             /*
   12986                 :              * Verify that the child doesn't have any inherited definitions of
   12987                 :              * this column that came from outside this inheritance hierarchy.
   12988 EUB             :              * (renameatt makes a similar test, though in a different way
   12989 ECB             :              * because of its different recursion mechanism.)
   12990                 :              */
   12991 CBC         107 :             childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
   12992                 :                                                colName);
   12993             107 :             if (!HeapTupleIsValid(childtuple))
   12994 LBC           0 :                 ereport(ERROR,
   12995                 :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   12996                 :                          errmsg("column \"%s\" of relation \"%s\" does not exist",
   12997                 :                                 colName, RelationGetRelationName(childrel))));
   12998 GIC         107 :             childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
   12999                 : 
   13000             107 :             if (childattTup->attinhcount > numparents)
   13001 CBC           3 :                 ereport(ERROR,
   13002                 :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13003                 :                          errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
   13004                 :                                 colName, RelationGetRelationName(childrel))));
   13005                 : 
   13006             104 :             ReleaseSysCache(childtuple);
   13007 ECB             : 
   13008                 :             /*
   13009                 :              * Remap the attribute numbers.  If no USING expression was
   13010                 :              * specified, there is no need for this step.
   13011                 :              */
   13012 GIC         104 :             if (def->cooked_default)
   13013 ECB             :             {
   13014                 :                 AttrMap    *attmap;
   13015                 :                 bool        found_whole_row;
   13016                 : 
   13017                 :                 /* create a copy to scribble on */
   13018 CBC          36 :                 cmd = copyObject(cmd);
   13019 ECB             : 
   13020 GIC          36 :                 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
   13021                 :                                                RelationGetDescr(rel),
   13022                 :                                                false);
   13023              72 :                 ((ColumnDef *) cmd->def)->cooked_default =
   13024              36 :                     map_variable_attnos(def->cooked_default,
   13025                 :                                         1, 0,
   13026                 :                                         attmap,
   13027                 :                                         InvalidOid, &found_whole_row);
   13028              36 :                 if (found_whole_row)
   13029               3 :                     ereport(ERROR,
   13030 ECB             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13031                 :                              errmsg("cannot convert whole-row table reference"),
   13032                 :                              errdetail("USING expression contains a whole-row table reference.")));
   13033 GIC          33 :                 pfree(attmap);
   13034 ECB             :             }
   13035 CBC         101 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
   13036 GIC          95 :             relation_close(childrel, NoLock);
   13037 ECB             :         }
   13038                 :     }
   13039 GIC         145 :     else if (!recursing &&
   13040              25 :              find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
   13041 UIC           0 :         ereport(ERROR,
   13042                 :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13043                 :                  errmsg("type of inherited column \"%s\" must be changed in child tables too",
   13044                 :                         colName)));
   13045 ECB             : 
   13046 GIC         481 :     if (tab->relkind == RELKIND_COMPOSITE_TYPE)
   13047 CBC          25 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
   13048 GIC         478 : }
   13049 ECB             : 
   13050 EUB             : /*
   13051 ECB             :  * When the data type of a column is changed, a rewrite might not be required
   13052                 :  * if the new type is sufficiently identical to the old one, and the USING
   13053                 :  * clause isn't trying to insert some other value.  It's safe to skip the
   13054                 :  * rewrite in these cases:
   13055                 :  *
   13056                 :  * - the old type is binary coercible to the new type
   13057 EUB             :  * - the new type is an unconstrained domain over the old type
   13058 ECB             :  * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
   13059                 :  *
   13060                 :  * In the case of a constrained domain, we could get by with scanning the
   13061                 :  * table and checking the constraint rather than actually rewriting it, but we
   13062                 :  * don't currently try to do that.
   13063                 :  */
   13064                 : static bool
   13065 GIC         446 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
   13066                 : {
   13067             446 :     Assert(expr != NULL);
   13068                 : 
   13069                 :     for (;;)
   13070                 :     {
   13071                 :         /* only one varno, so no need to check that */
   13072             498 :         if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
   13073 CBC          82 :             return false;
   13074             416 :         else if (IsA(expr, RelabelType))
   13075 GIC          46 :             expr = (Node *) ((RelabelType *) expr)->arg;
   13076             370 :         else if (IsA(expr, CoerceToDomain))
   13077                 :         {
   13078 UIC           0 :             CoerceToDomain *d = (CoerceToDomain *) expr;
   13079 ECB             : 
   13080 LBC           0 :             if (DomainHasConstraints(d->resulttype))
   13081               0 :                 return true;
   13082 UIC           0 :             expr = (Node *) d->arg;
   13083                 :         }
   13084 GIC         370 :         else if (IsA(expr, FuncExpr))
   13085                 :         {
   13086             270 :             FuncExpr   *f = (FuncExpr *) expr;
   13087 ECB             : 
   13088 CBC         270 :             switch (f->funcid)
   13089                 :             {
   13090 GIC           9 :                 case F_TIMESTAMPTZ_TIMESTAMP:
   13091                 :                 case F_TIMESTAMP_TIMESTAMPTZ:
   13092               9 :                     if (TimestampTimestampTzRequiresRewrite())
   13093               3 :                         return true;
   13094                 :                     else
   13095 CBC           6 :                         expr = linitial(f->args);
   13096 GIC           6 :                     break;
   13097             261 :                 default:
   13098             261 :                     return true;
   13099                 :             }
   13100                 :         }
   13101                 :         else
   13102             100 :             return true;
   13103 ECB             :     }
   13104                 : }
   13105                 : 
   13106                 : /*
   13107                 :  * ALTER COLUMN .. SET DATA TYPE
   13108                 :  *
   13109                 :  * Return the address of the modified column.
   13110                 :  */
   13111                 : static ObjectAddress
   13112 CBC         463 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
   13113                 :                       AlterTableCmd *cmd, LOCKMODE lockmode)
   13114 ECB             : {
   13115 GIC         463 :     char       *colName = cmd->name;
   13116 CBC         463 :     ColumnDef  *def = (ColumnDef *) cmd->def;
   13117 GIC         463 :     TypeName   *typeName = def->typeName;
   13118 ECB             :     HeapTuple   heapTup;
   13119                 :     Form_pg_attribute attTup,
   13120                 :                 attOldTup;
   13121                 :     AttrNumber  attnum;
   13122                 :     HeapTuple   typeTuple;
   13123                 :     Form_pg_type tform;
   13124                 :     Oid         targettype;
   13125                 :     int32       targettypmod;
   13126                 :     Oid         targetcollid;
   13127                 :     Node       *defaultexpr;
   13128                 :     Relation    attrelation;
   13129 EUB             :     Relation    depRel;
   13130 ECB             :     ScanKeyData key[3];
   13131                 :     SysScanDesc scan;
   13132                 :     HeapTuple   depTup;
   13133                 :     ObjectAddress address;
   13134                 : 
   13135                 :     /*
   13136                 :      * Clear all the missing values if we're rewriting the table, since this
   13137                 :      * renders them pointless.
   13138                 :      */
   13139 GIC         463 :     if (tab->rewrite)
   13140                 :     {
   13141                 :         Relation    newrel;
   13142                 : 
   13143 CBC         337 :         newrel = table_open(RelationGetRelid(rel), NoLock);
   13144             337 :         RelationClearMissing(newrel);
   13145             337 :         relation_close(newrel, NoLock);
   13146                 :         /* make sure we don't conflict with later attribute modifications */
   13147             337 :         CommandCounterIncrement();
   13148                 :     }
   13149                 : 
   13150 GIC         463 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
   13151                 : 
   13152                 :     /* Look up the target column */
   13153 CBC         463 :     heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
   13154 GBC         463 :     if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
   13155 UIC           0 :         ereport(ERROR,
   13156                 :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   13157                 :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   13158                 :                         colName, RelationGetRelationName(rel))));
   13159 GIC         463 :     attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   13160             463 :     attnum = attTup->attnum;
   13161             463 :     attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
   13162                 : 
   13163                 :     /* Check for multiple ALTER TYPE on same column --- can't cope */
   13164             463 :     if (attTup->atttypid != attOldTup->atttypid ||
   13165 CBC         463 :         attTup->atttypmod != attOldTup->atttypmod)
   13166 LBC           0 :         ereport(ERROR,
   13167                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13168                 :                  errmsg("cannot alter type of column \"%s\" twice",
   13169                 :                         colName)));
   13170                 : 
   13171                 :     /* Look up the target type (should not fail, since prep found it) */
   13172 CBC         463 :     typeTuple = typenameType(NULL, typeName, &targettypmod);
   13173 GIC         463 :     tform = (Form_pg_type) GETSTRUCT(typeTuple);
   13174 CBC         463 :     targettype = tform->oid;
   13175 ECB             :     /* And the collation */
   13176 GIC         463 :     targetcollid = GetColumnDefCollation(NULL, def, targettype);
   13177                 : 
   13178 ECB             :     /*
   13179                 :      * If there is a default expression for the column, get it and ensure we
   13180                 :      * can coerce it to the new datatype.  (We must do this before changing
   13181                 :      * the column type, because build_column_default itself will try to
   13182                 :      * coerce, and will not issue the error message we want if it fails.)
   13183                 :      *
   13184                 :      * We remove any implicit coercion steps at the top level of the old
   13185                 :      * default expression; this has been agreed to satisfy the principle of
   13186                 :      * least surprise.  (The conversion to the new column type should act like
   13187                 :      * it started from what the user sees as the stored expression, and the
   13188                 :      * implicit coercions aren't going to be shown.)
   13189                 :      */
   13190 GIC         463 :     if (attTup->atthasdef)
   13191                 :     {
   13192              31 :         defaultexpr = build_column_default(rel, attnum);
   13193              31 :         Assert(defaultexpr);
   13194              31 :         defaultexpr = strip_implicit_coercions(defaultexpr);
   13195              31 :         defaultexpr = coerce_to_target_type(NULL,   /* no UNKNOWN params */
   13196                 :                                             defaultexpr, exprType(defaultexpr),
   13197 ECB             :                                             targettype, targettypmod,
   13198                 :                                             COERCION_ASSIGNMENT,
   13199                 :                                             COERCE_IMPLICIT_CAST,
   13200                 :                                             -1);
   13201 GIC          31 :         if (defaultexpr == NULL)
   13202                 :         {
   13203               6 :             if (attTup->attgenerated)
   13204               3 :                 ereport(ERROR,
   13205                 :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13206                 :                          errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
   13207 ECB             :                                 colName, format_type_be(targettype))));
   13208                 :             else
   13209 CBC           3 :                 ereport(ERROR,
   13210                 :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   13211                 :                          errmsg("default for column \"%s\" cannot be cast automatically to type %s",
   13212                 :                                 colName, format_type_be(targettype))));
   13213                 :         }
   13214                 :     }
   13215 ECB             :     else
   13216 CBC         432 :         defaultexpr = NULL;
   13217                 : 
   13218                 :     /*
   13219 ECB             :      * Find everything that depends on the column (constraints, indexes, etc),
   13220                 :      * and record enough information to let us recreate the objects.
   13221                 :      *
   13222                 :      * The actual recreation does not happen here, but only after we have
   13223                 :      * performed all the individual ALTER TYPE operations.  We have to save
   13224                 :      * the info before executing ALTER TYPE, though, else the deparser will
   13225                 :      * get confused.
   13226                 :      */
   13227 GIC         457 :     depRel = table_open(DependRelationId, RowExclusiveLock);
   13228                 : 
   13229             457 :     ScanKeyInit(&key[0],
   13230                 :                 Anum_pg_depend_refclassid,
   13231 ECB             :                 BTEqualStrategyNumber, F_OIDEQ,
   13232                 :                 ObjectIdGetDatum(RelationRelationId));
   13233 GIC         457 :     ScanKeyInit(&key[1],
   13234                 :                 Anum_pg_depend_refobjid,
   13235 ECB             :                 BTEqualStrategyNumber, F_OIDEQ,
   13236                 :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   13237 GIC         457 :     ScanKeyInit(&key[2],
   13238                 :                 Anum_pg_depend_refobjsubid,
   13239                 :                 BTEqualStrategyNumber, F_INT4EQ,
   13240                 :                 Int32GetDatum((int32) attnum));
   13241                 : 
   13242 CBC         457 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   13243                 :                               NULL, 3, key);
   13244 ECB             : 
   13245 GIC         847 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   13246                 :     {
   13247             402 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   13248 ECB             :         ObjectAddress foundObject;
   13249                 : 
   13250 GIC         402 :         foundObject.classId = foundDep->classid;
   13251             402 :         foundObject.objectId = foundDep->objid;
   13252             402 :         foundObject.objectSubId = foundDep->objsubid;
   13253 ECB             : 
   13254 GIC         402 :         switch (getObjectClass(&foundObject))
   13255 ECB             :         {
   13256 GIC         123 :             case OCLASS_CLASS:
   13257 ECB             :                 {
   13258 GIC         123 :                     char        relKind = get_rel_relkind(foundObject.objectId);
   13259                 : 
   13260 CBC         123 :                     if (relKind == RELKIND_INDEX ||
   13261 ECB             :                         relKind == RELKIND_PARTITIONED_INDEX)
   13262                 :                     {
   13263 CBC         107 :                         Assert(foundObject.objectSubId == 0);
   13264             107 :                         RememberIndexForRebuilding(foundObject.objectId, tab);
   13265                 :                     }
   13266              16 :                     else if (relKind == RELKIND_SEQUENCE)
   13267 ECB             :                     {
   13268                 :                         /*
   13269                 :                          * This must be a SERIAL column's sequence.  We need
   13270 EUB             :                          * not do anything to it.
   13271                 :                          */
   13272 GIC          16 :                         Assert(foundObject.objectSubId == 0);
   13273 ECB             :                     }
   13274                 :                     else
   13275                 :                     {
   13276                 :                         /* Not expecting any other direct dependencies... */
   13277 UIC           0 :                         elog(ERROR, "unexpected object depending on column: %s",
   13278 ECB             :                              getObjectDescription(&foundObject, false));
   13279                 :                     }
   13280 GIC         123 :                     break;
   13281                 :                 }
   13282 ECB             : 
   13283 GIC         235 :             case OCLASS_CONSTRAINT:
   13284             235 :                 Assert(foundObject.objectSubId == 0);
   13285             235 :                 RememberConstraintForRebuilding(foundObject.objectId, tab);
   13286 CBC         235 :                 break;
   13287                 : 
   13288 GIC           6 :             case OCLASS_REWRITE:
   13289                 :                 /* XXX someday see if we can cope with revising views */
   13290 CBC           6 :                 ereport(ERROR,
   13291                 :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13292                 :                          errmsg("cannot alter type of a column used by a view or rule"),
   13293 ECB             :                          errdetail("%s depends on column \"%s\"",
   13294                 :                                    getObjectDescription(&foundObject, false),
   13295 EUB             :                                    colName)));
   13296                 :                 break;
   13297                 : 
   13298 UIC           0 :             case OCLASS_TRIGGER:
   13299                 : 
   13300 ECB             :                 /*
   13301                 :                  * A trigger can depend on a column because the column is
   13302                 :                  * specified as an update target, or because the column is
   13303                 :                  * used in the trigger's WHEN condition.  The first case would
   13304                 :                  * not require any extra work, but the second case would
   13305                 :                  * require updating the WHEN expression, which will take a
   13306                 :                  * significant amount of new code.  Since we can't easily tell
   13307                 :                  * which case applies, we punt for both.  FIXME someday.
   13308                 :                  */
   13309 UBC           0 :                 ereport(ERROR,
   13310                 :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13311 ECB             :                          errmsg("cannot alter type of a column used in a trigger definition"),
   13312 EUB             :                          errdetail("%s depends on column \"%s\"",
   13313                 :                                    getObjectDescription(&foundObject, false),
   13314                 :                                    colName)));
   13315 ECB             :                 break;
   13316                 : 
   13317 UIC           0 :             case OCLASS_POLICY:
   13318                 : 
   13319                 :                 /*
   13320                 :                  * A policy can depend on a column because the column is
   13321 ECB             :                  * specified in the policy's USING or WITH CHECK qual
   13322                 :                  * expressions.  It might be possible to rewrite and recheck
   13323                 :                  * the policy expression, but punt for now.  It's certainly
   13324                 :                  * easy enough to remove and recreate the policy; still, FIXME
   13325                 :                  * someday.
   13326                 :                  */
   13327 UIC           0 :                 ereport(ERROR,
   13328                 :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13329                 :                          errmsg("cannot alter type of a column used in a policy definition"),
   13330                 :                          errdetail("%s depends on column \"%s\"",
   13331 ECB             :                                    getObjectDescription(&foundObject, false),
   13332                 :                                    colName)));
   13333                 :                 break;
   13334                 : 
   13335 CBC          31 :             case OCLASS_DEFAULT:
   13336                 :                 {
   13337 GIC          31 :                     ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
   13338                 : 
   13339              31 :                     if (col.objectId == RelationGetRelid(rel) &&
   13340              31 :                         col.objectSubId == attnum)
   13341                 :                     {
   13342                 :                         /*
   13343                 :                          * Ignore the column's own default expression, which
   13344                 :                          * we will deal with below.
   13345 ECB             :                          */
   13346 CBC          25 :                         Assert(defaultexpr);
   13347                 :                     }
   13348 ECB             :                     else
   13349                 :                     {
   13350                 :                         /*
   13351                 :                          * This must be a reference from the expression of a
   13352                 :                          * generated column elsewhere in the same table.
   13353                 :                          * Changing the type of a column that is used by a
   13354                 :                          * generated column is not allowed by SQL standard, so
   13355                 :                          * just punt for now.  It might be doable with some
   13356                 :                          * thinking and effort.
   13357                 :                          */
   13358 GIC           6 :                         ereport(ERROR,
   13359 ECB             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13360                 :                                  errmsg("cannot alter type of a column used by a generated column"),
   13361                 :                                  errdetail("Column \"%s\" is used by generated column \"%s\".",
   13362                 :                                            colName,
   13363                 :                                            get_attname(col.objectId,
   13364                 :                                                        col.objectSubId,
   13365                 :                                                        false))));
   13366                 :                     }
   13367 GIC          25 :                     break;
   13368                 :                 }
   13369                 : 
   13370               7 :             case OCLASS_STATISTIC_EXT:
   13371                 : 
   13372                 :                 /*
   13373                 :                  * Give the extended-stats machinery a chance to fix anything
   13374                 :                  * that this column type change would break.
   13375                 :                  */
   13376               7 :                 RememberStatisticsForRebuilding(foundObject.objectId, tab);
   13377               7 :                 break;
   13378                 : 
   13379 UIC           0 :             case OCLASS_PROC:
   13380                 :             case OCLASS_TYPE:
   13381                 :             case OCLASS_CAST:
   13382 ECB             :             case OCLASS_COLLATION:
   13383                 :             case OCLASS_CONVERSION:
   13384                 :             case OCLASS_LANGUAGE:
   13385                 :             case OCLASS_LARGEOBJECT:
   13386                 :             case OCLASS_OPERATOR:
   13387                 :             case OCLASS_OPCLASS:
   13388                 :             case OCLASS_OPFAMILY:
   13389                 :             case OCLASS_AM:
   13390                 :             case OCLASS_AMOP:
   13391                 :             case OCLASS_AMPROC:
   13392                 :             case OCLASS_SCHEMA:
   13393                 :             case OCLASS_TSPARSER:
   13394                 :             case OCLASS_TSDICT:
   13395                 :             case OCLASS_TSTEMPLATE:
   13396                 :             case OCLASS_TSCONFIG:
   13397                 :             case OCLASS_ROLE:
   13398                 :             case OCLASS_ROLE_MEMBERSHIP:
   13399                 :             case OCLASS_DATABASE:
   13400                 :             case OCLASS_TBLSPACE:
   13401                 :             case OCLASS_FDW:
   13402                 :             case OCLASS_FOREIGN_SERVER:
   13403                 :             case OCLASS_USER_MAPPING:
   13404                 :             case OCLASS_DEFACL:
   13405                 :             case OCLASS_EXTENSION:
   13406                 :             case OCLASS_EVENT_TRIGGER:
   13407                 :             case OCLASS_PARAMETER_ACL:
   13408                 :             case OCLASS_PUBLICATION:
   13409                 :             case OCLASS_PUBLICATION_NAMESPACE:
   13410                 :             case OCLASS_PUBLICATION_REL:
   13411                 :             case OCLASS_SUBSCRIPTION:
   13412 EUB             :             case OCLASS_TRANSFORM:
   13413                 : 
   13414                 :                 /*
   13415                 :                  * We don't expect any of these sorts of objects to depend on
   13416 ECB             :                  * a column.
   13417                 :                  */
   13418 UIC           0 :                 elog(ERROR, "unexpected object depending on column: %s",
   13419                 :                      getObjectDescription(&foundObject, false));
   13420 ECB             :                 break;
   13421 EUB             : 
   13422                 :                 /*
   13423                 :                  * There's intentionally no default: case here; we want the
   13424                 :                  * compiler to warn if a new OCLASS hasn't been handled above.
   13425                 :                  */
   13426                 :         }
   13427                 :     }
   13428                 : 
   13429 GIC         445 :     systable_endscan(scan);
   13430                 : 
   13431 ECB             :     /*
   13432                 :      * Now scan for dependencies of this column on other things.  The only
   13433                 :      * things we should find are the dependency on the column datatype and
   13434                 :      * possibly a collation dependency.  Those can be removed.
   13435                 :      */
   13436 GIC         445 :     ScanKeyInit(&key[0],
   13437                 :                 Anum_pg_depend_classid,
   13438 ECB             :                 BTEqualStrategyNumber, F_OIDEQ,
   13439                 :                 ObjectIdGetDatum(RelationRelationId));
   13440 GIC         445 :     ScanKeyInit(&key[1],
   13441 ECB             :                 Anum_pg_depend_objid,
   13442                 :                 BTEqualStrategyNumber, F_OIDEQ,
   13443                 :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   13444 GIC         445 :     ScanKeyInit(&key[2],
   13445                 :                 Anum_pg_depend_objsubid,
   13446                 :                 BTEqualStrategyNumber, F_INT4EQ,
   13447 ECB             :                 Int32GetDatum((int32) attnum));
   13448                 : 
   13449 CBC         445 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
   13450 ECB             :                               NULL, 3, key);
   13451                 : 
   13452 GIC         447 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   13453                 :     {
   13454 CBC           2 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   13455                 :         ObjectAddress foundObject;
   13456                 : 
   13457               2 :         foundObject.classId = foundDep->refclassid;
   13458               2 :         foundObject.objectId = foundDep->refobjid;
   13459 GIC           2 :         foundObject.objectSubId = foundDep->refobjsubid;
   13460                 : 
   13461 CBC           2 :         if (foundDep->deptype != DEPENDENCY_NORMAL)
   13462 LBC           0 :             elog(ERROR, "found unexpected dependency type '%c'",
   13463                 :                  foundDep->deptype);
   13464 GIC           2 :         if (!(foundDep->refclassid == TypeRelationId &&
   13465               2 :               foundDep->refobjid == attTup->atttypid) &&
   13466 UIC           0 :             !(foundDep->refclassid == CollationRelationId &&
   13467               0 :               foundDep->refobjid == attTup->attcollation))
   13468               0 :             elog(ERROR, "found unexpected dependency for column: %s",
   13469                 :                  getObjectDescription(&foundObject, false));
   13470                 : 
   13471 GIC           2 :         CatalogTupleDelete(depRel, &depTup->t_self);
   13472                 :     }
   13473 ECB             : 
   13474 GIC         445 :     systable_endscan(scan);
   13475 ECB             : 
   13476 GIC         445 :     table_close(depRel, RowExclusiveLock);
   13477                 : 
   13478                 :     /*
   13479                 :      * Here we go --- change the recorded column type and collation.  (Note
   13480                 :      * heapTup is a copy of the syscache entry, so okay to scribble on.) First
   13481 ECB             :      * fix up the missing value if any.
   13482                 :      */
   13483 GIC         445 :     if (attTup->atthasmissing)
   13484                 :     {
   13485                 :         Datum       missingval;
   13486                 :         bool        missingNull;
   13487 ECB             : 
   13488                 :         /* if rewrite is true the missing value should already be cleared */
   13489 GIC           3 :         Assert(tab->rewrite == 0);
   13490 ECB             : 
   13491                 :         /* Get the missing value datum */
   13492 GIC           3 :         missingval = heap_getattr(heapTup,
   13493                 :                                   Anum_pg_attribute_attmissingval,
   13494                 :                                   attrelation->rd_att,
   13495                 :                                   &missingNull);
   13496                 : 
   13497                 :         /* if it's a null array there is nothing to do */
   13498 ECB             : 
   13499 GIC           3 :         if (!missingNull)
   13500                 :         {
   13501                 :             /*
   13502                 :              * Get the datum out of the array and repack it in a new array
   13503                 :              * built with the new type data. We assume that since the table
   13504                 :              * doesn't need rewriting, the actual Datum doesn't need to be
   13505                 :              * changed, only the array metadata.
   13506                 :              */
   13507                 : 
   13508               3 :             int         one = 1;
   13509                 :             bool        isNull;
   13510 GNC           3 :             Datum       valuesAtt[Natts_pg_attribute] = {0};
   13511               3 :             bool        nullsAtt[Natts_pg_attribute] = {0};
   13512               3 :             bool        replacesAtt[Natts_pg_attribute] = {0};
   13513 ECB             :             HeapTuple   newTup;
   13514                 : 
   13515 CBC           6 :             missingval = array_get_element(missingval,
   13516 ECB             :                                            1,
   13517                 :                                            &one,
   13518                 :                                            0,
   13519 GIC           3 :                                            attTup->attlen,
   13520 CBC           3 :                                            attTup->attbyval,
   13521               3 :                                            attTup->attalign,
   13522 ECB             :                                            &isNull);
   13523 GIC           3 :             missingval = PointerGetDatum(construct_array(&missingval,
   13524 ECB             :                                                          1,
   13525                 :                                                          targettype,
   13526 GIC           3 :                                                          tform->typlen,
   13527               3 :                                                          tform->typbyval,
   13528               3 :                                                          tform->typalign));
   13529                 : 
   13530 CBC           3 :             valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
   13531 GIC           3 :             replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
   13532               3 :             nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
   13533                 : 
   13534               3 :             newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
   13535                 :                                        valuesAtt, nullsAtt, replacesAtt);
   13536 CBC           3 :             heap_freetuple(heapTup);
   13537 GIC           3 :             heapTup = newTup;
   13538               3 :             attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   13539 ECB             :         }
   13540                 :     }
   13541                 : 
   13542 GIC         445 :     attTup->atttypid = targettype;
   13543             445 :     attTup->atttypmod = targettypmod;
   13544             445 :     attTup->attcollation = targetcollid;
   13545 GNC         445 :     if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
   13546 UNC           0 :         ereport(ERROR,
   13547                 :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   13548                 :                 errmsg("too many array dimensions"));
   13549 GIC         445 :     attTup->attndims = list_length(typeName->arrayBounds);
   13550             445 :     attTup->attlen = tform->typlen;
   13551             445 :     attTup->attbyval = tform->typbyval;
   13552             445 :     attTup->attalign = tform->typalign;
   13553 CBC         445 :     attTup->attstorage = tform->typstorage;
   13554 GIC         445 :     attTup->attcompression = InvalidCompressionMethod;
   13555 ECB             : 
   13556 GIC         445 :     ReleaseSysCache(typeTuple);
   13557                 : 
   13558             445 :     CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
   13559                 : 
   13560             445 :     table_close(attrelation, RowExclusiveLock);
   13561 ECB             : 
   13562                 :     /* Install dependencies on new datatype and collation */
   13563 GIC         445 :     add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
   13564             445 :     add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
   13565                 : 
   13566                 :     /*
   13567                 :      * Drop any pg_statistic entry for the column, since it's now wrong type
   13568                 :      */
   13569 CBC         445 :     RemoveStatistics(RelationGetRelid(rel), attnum);
   13570                 : 
   13571             445 :     InvokeObjectPostAlterHook(RelationRelationId,
   13572 ECB             :                               RelationGetRelid(rel), attnum);
   13573                 : 
   13574                 :     /*
   13575                 :      * Update the default, if present, by brute force --- remove and re-add
   13576                 :      * the default.  Probably unsafe to take shortcuts, since the new version
   13577                 :      * may well have additional dependencies.  (It's okay to do this now,
   13578                 :      * rather than after other ALTER TYPE commands, since the default won't
   13579                 :      * depend on other column types.)
   13580                 :      */
   13581 CBC         445 :     if (defaultexpr)
   13582 ECB             :     {
   13583                 :         /*
   13584                 :          * If it's a GENERATED default, drop its dependency records, in
   13585                 :          * particular its INTERNAL dependency on the column, which would
   13586                 :          * otherwise cause dependency.c to refuse to perform the deletion.
   13587                 :          */
   13588 GIC          25 :         if (attTup->attgenerated)
   13589                 :         {
   13590 CBC           3 :             Oid         attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
   13591                 : 
   13592               3 :             if (!OidIsValid(attrdefoid))
   13593 UBC           0 :                 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
   13594                 :                      RelationGetRelid(rel), attnum);
   13595 GIC           3 :             (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
   13596                 :         }
   13597 ECB             : 
   13598                 :         /*
   13599                 :          * Make updates-so-far visible, particularly the new pg_attribute row
   13600                 :          * which will be updated again.
   13601                 :          */
   13602 GIC          25 :         CommandCounterIncrement();
   13603                 : 
   13604                 :         /*
   13605 ECB             :          * We use RESTRICT here for safety, but at present we do not expect
   13606                 :          * anything to depend on the default.
   13607                 :          */
   13608 GIC          25 :         RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
   13609                 :                           true);
   13610                 : 
   13611 CBC          25 :         StoreAttrDefault(rel, attnum, defaultexpr, true, false);
   13612                 :     }
   13613                 : 
   13614 GIC         445 :     ObjectAddressSubSet(address, RelationRelationId,
   13615                 :                         RelationGetRelid(rel), attnum);
   13616                 : 
   13617 ECB             :     /* Cleanup */
   13618 GIC         445 :     heap_freetuple(heapTup);
   13619 ECB             : 
   13620 GIC         445 :     return address;
   13621                 : }
   13622 ECB             : 
   13623                 : /*
   13624                 :  * Subroutine for ATExecAlterColumnType: remember that a replica identity
   13625                 :  * needs to be reset.
   13626                 :  */
   13627                 : static void
   13628 CBC         199 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
   13629                 : {
   13630 GIC         199 :     if (!get_index_isreplident(indoid))
   13631             190 :         return;
   13632 ECB             : 
   13633 GIC           9 :     if (tab->replicaIdentityIndex)
   13634 LBC           0 :         elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
   13635 ECB             : 
   13636 GIC           9 :     tab->replicaIdentityIndex = get_rel_name(indoid);
   13637                 : }
   13638 ECB             : 
   13639                 : /*
   13640 EUB             :  * Subroutine for ATExecAlterColumnType: remember any clustered index.
   13641                 :  */
   13642                 : static void
   13643 GIC         199 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
   13644                 : {
   13645 CBC         199 :     if (!get_index_isclustered(indoid))
   13646             190 :         return;
   13647 ECB             : 
   13648 GIC           9 :     if (tab->clusterOnIndex)
   13649 UIC           0 :         elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
   13650                 : 
   13651 GIC           9 :     tab->clusterOnIndex = get_rel_name(indoid);
   13652                 : }
   13653                 : 
   13654                 : /*
   13655                 :  * Subroutine for ATExecAlterColumnType: remember that a constraint needs
   13656                 :  * to be rebuilt (which we might already know).
   13657                 :  */
   13658                 : static void
   13659             241 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
   13660                 : {
   13661                 :     /*
   13662                 :      * This de-duplication check is critical for two independent reasons: we
   13663                 :      * mustn't try to recreate the same constraint twice, and if a constraint
   13664 ECB             :      * depends on more than one column whose type is to be altered, we must
   13665                 :      * capture its definition string before applying any of the column type
   13666                 :      * changes.  ruleutils.c will get confused if we ask again later.
   13667                 :      */
   13668 GIC         241 :     if (!list_member_oid(tab->changedConstraintOids, conoid))
   13669                 :     {
   13670                 :         /* OK, capture the constraint's existing definition string */
   13671 CBC         202 :         char       *defstring = pg_get_constraintdef_command(conoid);
   13672 ECB             :         Oid         indoid;
   13673                 : 
   13674 CBC         202 :         tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
   13675 ECB             :                                                  conoid);
   13676 GIC         202 :         tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
   13677 EUB             :                                              defstring);
   13678                 : 
   13679                 :         /*
   13680                 :          * For the index of a constraint, if any, remember if it is used for
   13681                 :          * the table's replica identity or if it is a clustered index, so that
   13682                 :          * ATPostAlterTypeCleanup() can queue up commands necessary to restore
   13683 ECB             :          * those properties.
   13684                 :          */
   13685 CBC         202 :         indoid = get_constraint_index(conoid);
   13686 GIC         202 :         if (OidIsValid(indoid))
   13687 ECB             :         {
   13688 GIC         102 :             RememberReplicaIdentityForRebuilding(indoid, tab);
   13689 CBC         102 :             RememberClusterOnForRebuilding(indoid, tab);
   13690                 :         }
   13691 ECB             :     }
   13692 CBC         241 : }
   13693                 : 
   13694 ECB             : /*
   13695                 :  * Subroutine for ATExecAlterColumnType: remember that an index needs
   13696                 :  * to be rebuilt (which we might already know).
   13697                 :  */
   13698                 : static void
   13699 GIC         107 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
   13700                 : {
   13701 ECB             :     /*
   13702                 :      * This de-duplication check is critical for two independent reasons: we
   13703                 :      * mustn't try to recreate the same index twice, and if an index depends
   13704                 :      * on more than one column whose type is to be altered, we must capture
   13705                 :      * its definition string before applying any of the column type changes.
   13706                 :      * ruleutils.c will get confused if we ask again later.
   13707                 :      */
   13708 GIC         107 :     if (!list_member_oid(tab->changedIndexOids, indoid))
   13709                 :     {
   13710                 :         /*
   13711 ECB             :          * Before adding it as an index-to-rebuild, we'd better see if it
   13712                 :          * belongs to a constraint, and if so rebuild the constraint instead.
   13713                 :          * Typically this check fails, because constraint indexes normally
   13714                 :          * have only dependencies on their constraint.  But it's possible for
   13715                 :          * such an index to also have direct dependencies on table columns,
   13716                 :          * for example with a partial exclusion constraint.
   13717                 :          */
   13718 GIC         103 :         Oid         conoid = get_index_constraint(indoid);
   13719                 : 
   13720             103 :         if (OidIsValid(conoid))
   13721                 :         {
   13722               6 :             RememberConstraintForRebuilding(conoid, tab);
   13723                 :         }
   13724                 :         else
   13725                 :         {
   13726                 :             /* OK, capture the index's existing definition string */
   13727              97 :             char       *defstring = pg_get_indexdef_string(indoid);
   13728                 : 
   13729              97 :             tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
   13730                 :                                                 indoid);
   13731              97 :             tab->changedIndexDefs = lappend(tab->changedIndexDefs,
   13732                 :                                             defstring);
   13733                 : 
   13734                 :             /*
   13735                 :              * Remember if this index is used for the table's replica identity
   13736                 :              * or if it is a clustered index, so that ATPostAlterTypeCleanup()
   13737                 :              * can queue up commands necessary to restore those properties.
   13738 ECB             :              */
   13739 GIC          97 :             RememberReplicaIdentityForRebuilding(indoid, tab);
   13740              97 :             RememberClusterOnForRebuilding(indoid, tab);
   13741                 :         }
   13742 ECB             :     }
   13743 CBC         107 : }
   13744 ECB             : 
   13745                 : /*
   13746                 :  * Subroutine for ATExecAlterColumnType: remember that a statistics object
   13747                 :  * needs to be rebuilt (which we might already know).
   13748                 :  */
   13749                 : static void
   13750 GIC           7 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
   13751                 : {
   13752 ECB             :     /*
   13753                 :      * This de-duplication check is critical for two independent reasons: we
   13754 EUB             :      * mustn't try to recreate the same statistics object twice, and if the
   13755                 :      * statistics object depends on more than one column whose type is to be
   13756                 :      * altered, we must capture its definition string before applying any of
   13757                 :      * the type changes. ruleutils.c will get confused if we ask again later.
   13758 ECB             :      */
   13759 CBC           7 :     if (!list_member_oid(tab->changedStatisticsOids, stxoid))
   13760 ECB             :     {
   13761                 :         /* OK, capture the statistics object's existing definition string */
   13762 GIC           7 :         char       *defstring = pg_get_statisticsobjdef_string(stxoid);
   13763 ECB             : 
   13764 CBC           7 :         tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
   13765 EUB             :                                                  stxoid);
   13766 GIC           7 :         tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
   13767                 :                                              defstring);
   13768                 :     }
   13769               7 : }
   13770                 : 
   13771 ECB             : /*
   13772                 :  * Cleanup after we've finished all the ALTER TYPE operations for a
   13773                 :  * particular relation.  We have to drop and recreate all the indexes
   13774                 :  * and constraints that depend on the altered columns.  We do the
   13775                 :  * actual dropping here, but re-creation is managed by adding work
   13776                 :  * queue entries to do those steps later.
   13777                 :  */
   13778                 : static void
   13779 GIC         430 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
   13780                 : {
   13781                 :     ObjectAddress obj;
   13782                 :     ObjectAddresses *objects;
   13783                 :     ListCell   *def_item;
   13784                 :     ListCell   *oid_item;
   13785                 : 
   13786                 :     /*
   13787                 :      * Collect all the constraints and indexes to drop so we can process them
   13788                 :      * in a single call.  That way we don't have to worry about dependencies
   13789 ECB             :      * among them.
   13790                 :      */
   13791 CBC         430 :     objects = new_object_addresses();
   13792 ECB             : 
   13793                 :     /*
   13794                 :      * Re-parse the index and constraint definitions, and attach them to the
   13795                 :      * appropriate work queue entries.  We do this before dropping because in
   13796                 :      * the case of a FOREIGN KEY constraint, we might not yet have exclusive
   13797                 :      * lock on the table the constraint is attached to, and we need to get
   13798                 :      * that before reparsing/dropping.
   13799                 :      *
   13800                 :      * We can't rely on the output of deparsing to tell us which relation to
   13801                 :      * operate on, because concurrent activity might have made the name
   13802                 :      * resolve differently.  Instead, we've got to use the OID of the
   13803                 :      * constraint or index we're processing to figure out which relation to
   13804                 :      * operate on.
   13805                 :      */
   13806 GIC         632 :     forboth(oid_item, tab->changedConstraintOids,
   13807                 :             def_item, tab->changedConstraintDefs)
   13808 ECB             :     {
   13809 GIC         202 :         Oid         oldId = lfirst_oid(oid_item);
   13810                 :         HeapTuple   tup;
   13811                 :         Form_pg_constraint con;
   13812                 :         Oid         relid;
   13813                 :         Oid         confrelid;
   13814                 :         char        contype;
   13815 ECB             :         bool        conislocal;
   13816                 : 
   13817 GIC         202 :         tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   13818             202 :         if (!HeapTupleIsValid(tup)) /* should not happen */
   13819 UIC           0 :             elog(ERROR, "cache lookup failed for constraint %u", oldId);
   13820 GIC         202 :         con = (Form_pg_constraint) GETSTRUCT(tup);
   13821             202 :         if (OidIsValid(con->conrelid))
   13822             195 :             relid = con->conrelid;
   13823                 :         else
   13824                 :         {
   13825                 :             /* must be a domain constraint */
   13826 CBC           7 :             relid = get_typ_typrelid(getBaseType(con->contypid));
   13827 GIC           7 :             if (!OidIsValid(relid))
   13828 LBC           0 :                 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
   13829                 :         }
   13830 GIC         202 :         confrelid = con->confrelid;
   13831             202 :         contype = con->contype;
   13832 CBC         202 :         conislocal = con->conislocal;
   13833 GIC         202 :         ReleaseSysCache(tup);
   13834                 : 
   13835             202 :         ObjectAddressSet(obj, ConstraintRelationId, oldId);
   13836 CBC         202 :         add_exact_object_address(&obj, objects);
   13837                 : 
   13838                 :         /*
   13839                 :          * If the constraint is inherited (only), we don't want to inject a
   13840                 :          * new definition here; it'll get recreated when ATAddCheckConstraint
   13841 ECB             :          * recurses from adding the parent table's constraint.  But we had to
   13842                 :          * carry the info this far so that we can drop the constraint below.
   13843                 :          */
   13844 CBC         202 :         if (!conislocal)
   13845 GIC           8 :             continue;
   13846 ECB             : 
   13847                 :         /*
   13848                 :          * When rebuilding an FK constraint that references the table we're
   13849                 :          * modifying, we might not yet have any lock on the FK's table, so get
   13850                 :          * one now.  We'll need AccessExclusiveLock for the DROP CONSTRAINT
   13851                 :          * step, so there's no value in asking for anything weaker.
   13852                 :          */
   13853 CBC         194 :         if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
   13854 GIC          18 :             LockRelationOid(relid, AccessExclusiveLock);
   13855 ECB             : 
   13856 GIC         194 :         ATPostAlterTypeParse(oldId, relid, confrelid,
   13857 CBC         194 :                              (char *) lfirst(def_item),
   13858 GIC         194 :                              wqueue, lockmode, tab->rewrite);
   13859 ECB             :     }
   13860 GIC         527 :     forboth(oid_item, tab->changedIndexOids,
   13861                 :             def_item, tab->changedIndexDefs)
   13862 ECB             :     {
   13863 CBC          97 :         Oid         oldId = lfirst_oid(oid_item);
   13864                 :         Oid         relid;
   13865 ECB             : 
   13866 GIC          97 :         relid = IndexGetRelation(oldId, false);
   13867              97 :         ATPostAlterTypeParse(oldId, relid, InvalidOid,
   13868              97 :                              (char *) lfirst(def_item),
   13869              97 :                              wqueue, lockmode, tab->rewrite);
   13870                 : 
   13871 CBC          97 :         ObjectAddressSet(obj, RelationRelationId, oldId);
   13872 GIC          97 :         add_exact_object_address(&obj, objects);
   13873                 :     }
   13874                 : 
   13875                 :     /* add dependencies for new statistics */
   13876 GBC         437 :     forboth(oid_item, tab->changedStatisticsOids,
   13877                 :             def_item, tab->changedStatisticsDefs)
   13878                 :     {
   13879 CBC           7 :         Oid         oldId = lfirst_oid(oid_item);
   13880                 :         Oid         relid;
   13881                 : 
   13882               7 :         relid = StatisticsGetRelation(oldId, false);
   13883               7 :         ATPostAlterTypeParse(oldId, relid, InvalidOid,
   13884               7 :                              (char *) lfirst(def_item),
   13885               7 :                              wqueue, lockmode, tab->rewrite);
   13886                 : 
   13887               7 :         ObjectAddressSet(obj, StatisticExtRelationId, oldId);
   13888 GIC           7 :         add_exact_object_address(&obj, objects);
   13889 ECB             :     }
   13890                 : 
   13891                 :     /*
   13892                 :      * Queue up command to restore replica identity index marking
   13893                 :      */
   13894 GIC         430 :     if (tab->replicaIdentityIndex)
   13895                 :     {
   13896               9 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   13897 GBC           9 :         ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
   13898                 : 
   13899 GIC           9 :         subcmd->identity_type = REPLICA_IDENTITY_INDEX;
   13900               9 :         subcmd->name = tab->replicaIdentityIndex;
   13901               9 :         cmd->subtype = AT_ReplicaIdentity;
   13902               9 :         cmd->def = (Node *) subcmd;
   13903                 : 
   13904                 :         /* do it after indexes and constraints */
   13905               9 :         tab->subcmds[AT_PASS_OLD_CONSTR] =
   13906               9 :             lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   13907                 :     }
   13908 EUB             : 
   13909                 :     /*
   13910                 :      * Queue up command to restore marking of index used for cluster.
   13911                 :      */
   13912 GIC         430 :     if (tab->clusterOnIndex)
   13913                 :     {
   13914               9 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   13915                 : 
   13916 GBC           9 :         cmd->subtype = AT_ClusterOn;
   13917 GIC           9 :         cmd->name = tab->clusterOnIndex;
   13918                 : 
   13919                 :         /* do it after indexes and constraints */
   13920               9 :         tab->subcmds[AT_PASS_OLD_CONSTR] =
   13921               9 :             lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   13922                 :     }
   13923                 : 
   13924                 :     /*
   13925                 :      * It should be okay to use DROP_RESTRICT here, since nothing else should
   13926 EUB             :      * be depending on these objects.
   13927                 :      */
   13928 GIC         430 :     performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   13929                 : 
   13930             430 :     free_object_addresses(objects);
   13931                 : 
   13932                 :     /*
   13933                 :      * The objects will get recreated during subsequent passes over the work
   13934 ECB             :      * queue.
   13935                 :      */
   13936 CBC         430 : }
   13937                 : 
   13938 ECB             : /*
   13939                 :  * Parse the previously-saved definition string for a constraint, index or
   13940                 :  * statistics object against the newly-established column data type(s), and
   13941                 :  * queue up the resulting command parsetrees for execution.
   13942                 :  *
   13943                 :  * This might fail if, for example, you have a WHERE clause that uses an
   13944                 :  * operator that's not available for the new column type.
   13945                 :  */
   13946                 : static void
   13947 GIC         298 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
   13948                 :                      List **wqueue, LOCKMODE lockmode, bool rewrite)
   13949                 : {
   13950                 :     List       *raw_parsetree_list;
   13951                 :     List       *querytree_list;
   13952                 :     ListCell   *list_item;
   13953                 :     Relation    rel;
   13954                 : 
   13955                 :     /*
   13956                 :      * We expect that we will get only ALTER TABLE and CREATE INDEX
   13957 ECB             :      * statements. Hence, there is no need to pass them through
   13958                 :      * parse_analyze_*() or the rewriter, but instead we need to pass them
   13959                 :      * through parse_utilcmd.c to make them ready for execution.
   13960                 :      */
   13961 GIC         298 :     raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
   13962             298 :     querytree_list = NIL;
   13963             596 :     foreach(list_item, raw_parsetree_list)
   13964                 :     {
   13965             298 :         RawStmt    *rs = lfirst_node(RawStmt, list_item);
   13966 CBC         298 :         Node       *stmt = rs->stmt;
   13967                 : 
   13968 GIC         298 :         if (IsA(stmt, IndexStmt))
   13969 CBC          97 :             querytree_list = lappend(querytree_list,
   13970 GIC          97 :                                      transformIndexStmt(oldRelId,
   13971                 :                                                         (IndexStmt *) stmt,
   13972                 :                                                         cmd));
   13973             201 :         else if (IsA(stmt, AlterTableStmt))
   13974                 :         {
   13975 ECB             :             List       *beforeStmts;
   13976                 :             List       *afterStmts;
   13977                 : 
   13978 GBC         187 :             stmt = (Node *) transformAlterTableStmt(oldRelId,
   13979                 :                                                     (AlterTableStmt *) stmt,
   13980                 :                                                     cmd,
   13981                 :                                                     &beforeStmts,
   13982                 :                                                     &afterStmts);
   13983 GIC         187 :             querytree_list = list_concat(querytree_list, beforeStmts);
   13984             187 :             querytree_list = lappend(querytree_list, stmt);
   13985             187 :             querytree_list = list_concat(querytree_list, afterStmts);
   13986                 :         }
   13987              14 :         else if (IsA(stmt, CreateStatsStmt))
   13988               7 :             querytree_list = lappend(querytree_list,
   13989               7 :                                      transformStatsStmt(oldRelId,
   13990                 :                                                         (CreateStatsStmt *) stmt,
   13991                 :                                                         cmd));
   13992                 :         else
   13993               7 :             querytree_list = lappend(querytree_list, stmt);
   13994                 :     }
   13995                 : 
   13996                 :     /* Caller should already have acquired whatever lock we need. */
   13997             298 :     rel = relation_open(oldRelId, NoLock);
   13998                 : 
   13999                 :     /*
   14000                 :      * Attach each generated command to the proper place in the work queue.
   14001                 :      * Note this could result in creation of entirely new work-queue entries.
   14002                 :      *
   14003                 :      * Also note that we have to tweak the command subtypes, because it turns
   14004                 :      * out that re-creation of indexes and constraints has to act a bit
   14005                 :      * differently from initial creation.
   14006                 :      */
   14007             596 :     foreach(list_item, querytree_list)
   14008                 :     {
   14009             298 :         Node       *stm = (Node *) lfirst(list_item);
   14010                 :         AlteredTableInfo *tab;
   14011                 : 
   14012             298 :         tab = ATGetQueueEntry(wqueue, rel);
   14013                 : 
   14014             298 :         if (IsA(stm, IndexStmt))
   14015                 :         {
   14016              97 :             IndexStmt  *stmt = (IndexStmt *) stm;
   14017 EUB             :             AlterTableCmd *newcmd;
   14018                 : 
   14019 GIC          97 :             if (!rewrite)
   14020              27 :                 TryReuseIndex(oldId, stmt);
   14021              97 :             stmt->reset_default_tblspc = true;
   14022                 :             /* keep the index's comment */
   14023              97 :             stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
   14024                 : 
   14025              97 :             newcmd = makeNode(AlterTableCmd);
   14026              97 :             newcmd->subtype = AT_ReAddIndex;
   14027              97 :             newcmd->def = (Node *) stmt;
   14028 CBC          97 :             tab->subcmds[AT_PASS_OLD_INDEX] =
   14029 GIC          97 :                 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
   14030                 :         }
   14031             201 :         else if (IsA(stm, AlterTableStmt))
   14032                 :         {
   14033             187 :             AlterTableStmt *stmt = (AlterTableStmt *) stm;
   14034                 :             ListCell   *lcmd;
   14035 ECB             : 
   14036 GIC         434 :             foreach(lcmd, stmt->cmds)
   14037                 :             {
   14038             247 :                 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
   14039 ECB             : 
   14040 GIC         247 :                 if (cmd->subtype == AT_AddIndex)
   14041                 :                 {
   14042                 :                     IndexStmt  *indstmt;
   14043 ECB             :                     Oid         indoid;
   14044                 : 
   14045 GIC         102 :                     indstmt = castNode(IndexStmt, cmd->def);
   14046             102 :                     indoid = get_constraint_index(oldId);
   14047                 : 
   14048 CBC         102 :                     if (!rewrite)
   14049 GIC          24 :                         TryReuseIndex(indoid, indstmt);
   14050                 :                     /* keep any comment on the index */
   14051 CBC         102 :                     indstmt->idxcomment = GetComment(indoid,
   14052                 :                                                      RelationRelationId, 0);
   14053             102 :                     indstmt->reset_default_tblspc = true;
   14054                 : 
   14055 GIC         102 :                     cmd->subtype = AT_ReAddIndex;
   14056 CBC         102 :                     tab->subcmds[AT_PASS_OLD_INDEX] =
   14057             102 :                         lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
   14058 ECB             : 
   14059                 :                     /* recreate any comment on the constraint */
   14060 CBC         102 :                     RebuildConstraintComment(tab,
   14061 EUB             :                                              AT_PASS_OLD_INDEX,
   14062                 :                                              oldId,
   14063 ECB             :                                              rel,
   14064                 :                                              NIL,
   14065 GBC         102 :                                              indstmt->idxname);
   14066 EUB             :                 }
   14067 GBC         145 :                 else if (cmd->subtype == AT_AddConstraint)
   14068                 :                 {
   14069 GIC          85 :                     Constraint *con = castNode(Constraint, cmd->def);
   14070 ECB             : 
   14071 GIC          85 :                     con->old_pktable_oid = refRelId;
   14072                 :                     /* rewriting neither side of a FK */
   14073 CBC          85 :                     if (con->contype == CONSTR_FOREIGN &&
   14074 GIC          30 :                         !rewrite && tab->rewrite == 0)
   14075 CBC           3 :                         TryReuseForeignKey(oldId, con);
   14076 GIC          85 :                     con->reset_default_tblspc = true;
   14077              85 :                     cmd->subtype = AT_ReAddConstraint;
   14078              85 :                     tab->subcmds[AT_PASS_OLD_CONSTR] =
   14079              85 :                         lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   14080                 : 
   14081                 :                     /* recreate any comment on the constraint */
   14082 CBC          85 :                     RebuildConstraintComment(tab,
   14083                 :                                              AT_PASS_OLD_CONSTR,
   14084                 :                                              oldId,
   14085                 :                                              rel,
   14086                 :                                              NIL,
   14087 GIC          85 :                                              con->conname);
   14088 ECB             :                 }
   14089 GNC          60 :                 else if (cmd->subtype == AT_SetAttNotNull)
   14090                 :                 {
   14091 ECB             :                     /*
   14092                 :                      * The parser will create AT_AttSetNotNull subcommands for
   14093                 :                      * columns of PRIMARY KEY indexes/constraints, but we need
   14094                 :                      * not do anything with them here, because the columns'
   14095                 :                      * NOT NULL marks will already have been propagated into
   14096                 :                      * the new table definition.
   14097                 :                      */
   14098                 :                 }
   14099                 :                 else
   14100 UIC           0 :                     elog(ERROR, "unexpected statement subtype: %d",
   14101                 :                          (int) cmd->subtype);
   14102                 :             }
   14103                 :         }
   14104 GIC          14 :         else if (IsA(stm, AlterDomainStmt))
   14105                 :         {
   14106               7 :             AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
   14107 ECB             : 
   14108 GIC           7 :             if (stmt->subtype == 'C')    /* ADD CONSTRAINT */
   14109 ECB             :             {
   14110 CBC           7 :                 Constraint *con = castNode(Constraint, stmt->def);
   14111               7 :                 AlterTableCmd *cmd = makeNode(AlterTableCmd);
   14112                 : 
   14113 GIC           7 :                 cmd->subtype = AT_ReAddDomainConstraint;
   14114 CBC           7 :                 cmd->def = (Node *) stmt;
   14115 GIC           7 :                 tab->subcmds[AT_PASS_OLD_CONSTR] =
   14116               7 :                     lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   14117                 : 
   14118 ECB             :                 /* recreate any comment on the constraint */
   14119 CBC           7 :                 RebuildConstraintComment(tab,
   14120 ECB             :                                          AT_PASS_OLD_CONSTR,
   14121                 :                                          oldId,
   14122                 :                                          NULL,
   14123                 :                                          stmt->typeName,
   14124 GIC           7 :                                          con->conname);
   14125 ECB             :             }
   14126                 :             else
   14127 LBC           0 :                 elog(ERROR, "unexpected statement subtype: %d",
   14128                 :                      (int) stmt->subtype);
   14129 ECB             :         }
   14130 CBC           7 :         else if (IsA(stm, CreateStatsStmt))
   14131 ECB             :         {
   14132 GIC           7 :             CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
   14133 ECB             :             AlterTableCmd *newcmd;
   14134                 : 
   14135                 :             /* keep the statistics object's comment */
   14136 CBC           7 :             stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
   14137 ECB             : 
   14138 GIC           7 :             newcmd = makeNode(AlterTableCmd);
   14139               7 :             newcmd->subtype = AT_ReAddStatistics;
   14140               7 :             newcmd->def = (Node *) stmt;
   14141 CBC           7 :             tab->subcmds[AT_PASS_MISC] =
   14142               7 :                 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
   14143 ECB             :         }
   14144                 :         else
   14145 UBC           0 :             elog(ERROR, "unexpected statement type: %d",
   14146                 :                  (int) nodeTag(stm));
   14147                 :     }
   14148 ECB             : 
   14149 CBC         298 :     relation_close(rel, NoLock);
   14150             298 : }
   14151 ECB             : 
   14152                 : /*
   14153                 :  * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
   14154                 :  * for a table or domain constraint that is being rebuilt.
   14155                 :  *
   14156                 :  * objid is the OID of the constraint.
   14157                 :  * Pass "rel" for a table constraint, or "domname" (domain's qualified name
   14158                 :  * as a string list) for a domain constraint.
   14159                 :  * (We could dig that info, as well as the conname, out of the pg_constraint
   14160                 :  * entry; but callers already have them so might as well pass them.)
   14161                 :  */
   14162                 : static void
   14163 CBC         194 : RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
   14164                 :                          Relation rel, List *domname,
   14165                 :                          const char *conname)
   14166                 : {
   14167                 :     CommentStmt *cmd;
   14168 ECB             :     char       *comment_str;
   14169                 :     AlterTableCmd *newcmd;
   14170                 : 
   14171                 :     /* Look for comment for object wanted, and leave if none */
   14172 GIC         194 :     comment_str = GetComment(objid, ConstraintRelationId, 0);
   14173             194 :     if (comment_str == NULL)
   14174             161 :         return;
   14175                 : 
   14176                 :     /* Build CommentStmt node, copying all input data for safety */
   14177              33 :     cmd = makeNode(CommentStmt);
   14178              33 :     if (rel)
   14179                 :     {
   14180 CBC          27 :         cmd->objtype = OBJECT_TABCONSTRAINT;
   14181 GIC          27 :         cmd->object = (Node *)
   14182              27 :             list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
   14183                 :                        makeString(pstrdup(RelationGetRelationName(rel))),
   14184                 :                        makeString(pstrdup(conname)));
   14185                 :     }
   14186                 :     else
   14187 ECB             :     {
   14188 GIC           6 :         cmd->objtype = OBJECT_DOMCONSTRAINT;
   14189 CBC           6 :         cmd->object = (Node *)
   14190 GIC           6 :             list_make2(makeTypeNameFromNameList(copyObject(domname)),
   14191 ECB             :                        makeString(pstrdup(conname)));
   14192 EUB             :     }
   14193 GIC          33 :     cmd->comment = comment_str;
   14194 ECB             : 
   14195                 :     /* Append it to list of commands */
   14196 GIC          33 :     newcmd = makeNode(AlterTableCmd);
   14197              33 :     newcmd->subtype = AT_ReAddComment;
   14198              33 :     newcmd->def = (Node *) cmd;
   14199              33 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
   14200                 : }
   14201 ECB             : 
   14202                 : /*
   14203                 :  * Subroutine for ATPostAlterTypeParse().  Calls out to CheckIndexCompatible()
   14204                 :  * for the real analysis, then mutates the IndexStmt based on that verdict.
   14205                 :  */
   14206                 : static void
   14207 CBC          51 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
   14208                 : {
   14209 GIC          51 :     if (CheckIndexCompatible(oldId,
   14210 CBC          51 :                              stmt->accessMethod,
   14211                 :                              stmt->indexParams,
   14212                 :                              stmt->excludeOpNames))
   14213 ECB             :     {
   14214 GIC          51 :         Relation    irel = index_open(oldId, NoLock);
   14215                 : 
   14216                 :         /* If it's a partitioned index, there is no storage to share. */
   14217 CBC          51 :         if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   14218                 :         {
   14219 GNC          36 :             stmt->oldNumber = irel->rd_locator.relNumber;
   14220 GIC          36 :             stmt->oldCreateSubid = irel->rd_createSubid;
   14221 GNC          36 :             stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
   14222                 :         }
   14223 GIC          51 :         index_close(irel, NoLock);
   14224                 :     }
   14225              51 : }
   14226                 : 
   14227 ECB             : /*
   14228                 :  * Subroutine for ATPostAlterTypeParse().
   14229                 :  *
   14230                 :  * Stash the old P-F equality operator into the Constraint node, for possible
   14231                 :  * use by ATAddForeignKeyConstraint() in determining whether revalidation of
   14232                 :  * this constraint can be skipped.
   14233 EUB             :  */
   14234                 : static void
   14235 CBC           3 : TryReuseForeignKey(Oid oldId, Constraint *con)
   14236                 : {
   14237                 :     HeapTuple   tup;
   14238                 :     Datum       adatum;
   14239                 :     ArrayType  *arr;
   14240                 :     Oid        *rawarr;
   14241 ECB             :     int         numkeys;
   14242                 :     int         i;
   14243                 : 
   14244 CBC           3 :     Assert(con->contype == CONSTR_FOREIGN);
   14245 GIC           3 :     Assert(con->old_conpfeqop == NIL);   /* already prepared this node */
   14246 ECB             : 
   14247 GBC           3 :     tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   14248 GIC           3 :     if (!HeapTupleIsValid(tup)) /* should not happen */
   14249 LBC           0 :         elog(ERROR, "cache lookup failed for constraint %u", oldId);
   14250                 : 
   14251 GNC           3 :     adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
   14252                 :                                     Anum_pg_constraint_conpfeqop);
   14253 GIC           3 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
   14254               3 :     numkeys = ARR_DIMS(arr)[0];
   14255 ECB             :     /* test follows the one in ri_FetchConstraintInfo() */
   14256 GIC           3 :     if (ARR_NDIM(arr) != 1 ||
   14257               3 :         ARR_HASNULL(arr) ||
   14258               3 :         ARR_ELEMTYPE(arr) != OIDOID)
   14259 UIC           0 :         elog(ERROR, "conpfeqop is not a 1-D Oid array");
   14260 GIC           3 :     rawarr = (Oid *) ARR_DATA_PTR(arr);
   14261                 : 
   14262                 :     /* stash a List of the operator Oids in our Constraint node */
   14263               6 :     for (i = 0; i < numkeys; i++)
   14264 CBC           3 :         con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
   14265                 : 
   14266 GIC           3 :     ReleaseSysCache(tup);
   14267 CBC           3 : }
   14268                 : 
   14269                 : /*
   14270 ECB             :  * ALTER COLUMN .. OPTIONS ( ... )
   14271                 :  *
   14272                 :  * Returns the address of the modified column
   14273                 :  */
   14274                 : static ObjectAddress
   14275 GIC          82 : ATExecAlterColumnGenericOptions(Relation rel,
   14276                 :                                 const char *colName,
   14277                 :                                 List *options,
   14278                 :                                 LOCKMODE lockmode)
   14279                 : {
   14280                 :     Relation    ftrel;
   14281 ECB             :     Relation    attrel;
   14282                 :     ForeignServer *server;
   14283                 :     ForeignDataWrapper *fdw;
   14284                 :     HeapTuple   tuple;
   14285                 :     HeapTuple   newtuple;
   14286                 :     bool        isnull;
   14287                 :     Datum       repl_val[Natts_pg_attribute];
   14288                 :     bool        repl_null[Natts_pg_attribute];
   14289                 :     bool        repl_repl[Natts_pg_attribute];
   14290                 :     Datum       datum;
   14291                 :     Form_pg_foreign_table fttableform;
   14292                 :     Form_pg_attribute atttableform;
   14293                 :     AttrNumber  attnum;
   14294                 :     ObjectAddress address;
   14295                 : 
   14296 GIC          82 :     if (options == NIL)
   14297 UIC           0 :         return InvalidObjectAddress;
   14298                 : 
   14299                 :     /* First, determine FDW validator associated to the foreign table. */
   14300 GIC          82 :     ftrel = table_open(ForeignTableRelationId, AccessShareLock);
   14301              82 :     tuple = SearchSysCache1(FOREIGNTABLEREL, rel->rd_id);
   14302              82 :     if (!HeapTupleIsValid(tuple))
   14303 UIC           0 :         ereport(ERROR,
   14304 ECB             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   14305                 :                  errmsg("foreign table \"%s\" does not exist",
   14306                 :                         RelationGetRelationName(rel))));
   14307 GIC          82 :     fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   14308              82 :     server = GetForeignServer(fttableform->ftserver);
   14309              82 :     fdw = GetForeignDataWrapper(server->fdwid);
   14310                 : 
   14311              82 :     table_close(ftrel, AccessShareLock);
   14312              82 :     ReleaseSysCache(tuple);
   14313                 : 
   14314 CBC          82 :     attrel = table_open(AttributeRelationId, RowExclusiveLock);
   14315 GIC          82 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   14316 CBC          82 :     if (!HeapTupleIsValid(tuple))
   14317 UIC           0 :         ereport(ERROR,
   14318 ECB             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   14319                 :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   14320                 :                         colName, RelationGetRelationName(rel))));
   14321                 : 
   14322                 :     /* Prevent them from altering a system attribute */
   14323 CBC          82 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   14324 GIC          82 :     attnum = atttableform->attnum;
   14325 CBC          82 :     if (attnum <= 0)
   14326 GIC           3 :         ereport(ERROR,
   14327 ECB             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14328                 :                  errmsg("cannot alter system column \"%s\"", colName)));
   14329                 : 
   14330                 : 
   14331                 :     /* Initialize buffers for new tuple values */
   14332 GIC          79 :     memset(repl_val, 0, sizeof(repl_val));
   14333              79 :     memset(repl_null, false, sizeof(repl_null));
   14334              79 :     memset(repl_repl, false, sizeof(repl_repl));
   14335 ECB             : 
   14336                 :     /* Extract the current options */
   14337 GIC          79 :     datum = SysCacheGetAttr(ATTNAME,
   14338                 :                             tuple,
   14339 ECB             :                             Anum_pg_attribute_attfdwoptions,
   14340                 :                             &isnull);
   14341 GIC          79 :     if (isnull)
   14342              74 :         datum = PointerGetDatum(NULL);
   14343                 : 
   14344                 :     /* Transform the options */
   14345              79 :     datum = transformGenericOptions(AttributeRelationId,
   14346 ECB             :                                     datum,
   14347                 :                                     options,
   14348                 :                                     fdw->fdwvalidator);
   14349                 : 
   14350 GIC          79 :     if (PointerIsValid(DatumGetPointer(datum)))
   14351              79 :         repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
   14352                 :     else
   14353 UIC           0 :         repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
   14354                 : 
   14355 CBC          79 :     repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
   14356                 : 
   14357                 :     /* Everything looks good - update the tuple */
   14358 ECB             : 
   14359 GIC          79 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
   14360 ECB             :                                  repl_val, repl_null, repl_repl);
   14361                 : 
   14362 CBC          79 :     CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
   14363                 : 
   14364 GIC          79 :     InvokeObjectPostAlterHook(RelationRelationId,
   14365 ECB             :                               RelationGetRelid(rel),
   14366                 :                               atttableform->attnum);
   14367 GIC          79 :     ObjectAddressSubSet(address, RelationRelationId,
   14368                 :                         RelationGetRelid(rel), attnum);
   14369                 : 
   14370              79 :     ReleaseSysCache(tuple);
   14371                 : 
   14372              79 :     table_close(attrel, RowExclusiveLock);
   14373                 : 
   14374              79 :     heap_freetuple(newtuple);
   14375 ECB             : 
   14376 GIC          79 :     return address;
   14377                 : }
   14378                 : 
   14379                 : /*
   14380                 :  * ALTER TABLE OWNER
   14381                 :  *
   14382                 :  * recursing is true if we are recursing from a table to its indexes,
   14383                 :  * sequences, or toast table.  We don't allow the ownership of those things to
   14384                 :  * be changed separately from the parent table.  Also, we can skip permission
   14385                 :  * checks (this is necessary not just an optimization, else we'd fail to
   14386                 :  * handle toast tables properly).
   14387 ECB             :  *
   14388                 :  * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
   14389                 :  * free-standing composite type.
   14390                 :  */
   14391                 : void
   14392 GIC         940 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
   14393                 : {
   14394                 :     Relation    target_rel;
   14395                 :     Relation    class_rel;
   14396                 :     HeapTuple   tuple;
   14397                 :     Form_pg_class tuple_class;
   14398                 : 
   14399                 :     /*
   14400                 :      * Get exclusive lock till end of transaction on the target table. Use
   14401                 :      * relation_open so that we can work on indexes and sequences.
   14402 ECB             :      */
   14403 GIC         940 :     target_rel = relation_open(relationOid, lockmode);
   14404                 : 
   14405 ECB             :     /* Get its pg_class tuple, too */
   14406 GIC         940 :     class_rel = table_open(RelationRelationId, RowExclusiveLock);
   14407                 : 
   14408             940 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
   14409             940 :     if (!HeapTupleIsValid(tuple))
   14410 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", relationOid);
   14411 GIC         940 :     tuple_class = (Form_pg_class) GETSTRUCT(tuple);
   14412                 : 
   14413 ECB             :     /* Can we change the ownership of this tuple? */
   14414 CBC         940 :     switch (tuple_class->relkind)
   14415 EUB             :     {
   14416 CBC         826 :         case RELKIND_RELATION:
   14417 ECB             :         case RELKIND_VIEW:
   14418                 :         case RELKIND_MATVIEW:
   14419                 :         case RELKIND_FOREIGN_TABLE:
   14420                 :         case RELKIND_PARTITIONED_TABLE:
   14421                 :             /* ok to change owner */
   14422 CBC         826 :             break;
   14423              42 :         case RELKIND_INDEX:
   14424 GBC          42 :             if (!recursing)
   14425                 :             {
   14426 ECB             :                 /*
   14427                 :                  * Because ALTER INDEX OWNER used to be allowed, and in fact
   14428                 :                  * is generated by old versions of pg_dump, we give a warning
   14429                 :                  * and do nothing rather than erroring out.  Also, to avoid
   14430                 :                  * unnecessary chatter while restoring those old dumps, say
   14431                 :                  * nothing at all if the command would be a no-op anyway.
   14432                 :                  */
   14433 UIC           0 :                 if (tuple_class->relowner != newOwnerId)
   14434               0 :                     ereport(WARNING,
   14435                 :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14436                 :                              errmsg("cannot change owner of index \"%s\"",
   14437                 :                                     NameStr(tuple_class->relname)),
   14438                 :                              errhint("Change the ownership of the index's table, instead.")));
   14439                 :                 /* quick hack to exit via the no-op path */
   14440 LBC           0 :                 newOwnerId = tuple_class->relowner;
   14441 ECB             :             }
   14442 GIC          42 :             break;
   14443              10 :         case RELKIND_PARTITIONED_INDEX:
   14444              10 :             if (recursing)
   14445              10 :                 break;
   14446 UIC           0 :             ereport(ERROR,
   14447                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14448                 :                      errmsg("cannot change owner of index \"%s\"",
   14449 ECB             :                             NameStr(tuple_class->relname)),
   14450                 :                      errhint("Change the ownership of the index's table, instead.")));
   14451                 :             break;
   14452 CBC          41 :         case RELKIND_SEQUENCE:
   14453              41 :             if (!recursing &&
   14454              26 :                 tuple_class->relowner != newOwnerId)
   14455                 :             {
   14456 ECB             :                 /* if it's an owned sequence, disallow changing it by itself */
   14457                 :                 Oid         tableId;
   14458                 :                 int32       colId;
   14459                 : 
   14460 UIC           0 :                 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
   14461               0 :                     sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
   14462 LBC           0 :                     ereport(ERROR,
   14463 ECB             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14464                 :                              errmsg("cannot change owner of sequence \"%s\"",
   14465                 :                                     NameStr(tuple_class->relname)),
   14466                 :                              errdetail("Sequence \"%s\" is linked to table \"%s\".",
   14467                 :                                        NameStr(tuple_class->relname),
   14468                 :                                        get_rel_name(tableId))));
   14469                 :             }
   14470 GIC          41 :             break;
   14471               3 :         case RELKIND_COMPOSITE_TYPE:
   14472 CBC           3 :             if (recursing)
   14473 GIC           3 :                 break;
   14474 UIC           0 :             ereport(ERROR,
   14475 ECB             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14476                 :                      errmsg("\"%s\" is a composite type",
   14477                 :                             NameStr(tuple_class->relname)),
   14478                 :                      errhint("Use ALTER TYPE instead.")));
   14479                 :             break;
   14480 CBC          18 :         case RELKIND_TOASTVALUE:
   14481              18 :             if (recursing)
   14482 GIC          18 :                 break;
   14483 ECB             :             /* FALL THRU */
   14484                 :         default:
   14485 UIC           0 :             ereport(ERROR,
   14486                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14487                 :                      errmsg("cannot change owner of relation \"%s\"",
   14488                 :                             NameStr(tuple_class->relname)),
   14489                 :                      errdetail_relkind_not_supported(tuple_class->relkind)));
   14490 ECB             :     }
   14491                 : 
   14492                 :     /*
   14493                 :      * If the new owner is the same as the existing owner, consider the
   14494                 :      * command to have succeeded.  This is for dump restoration purposes.
   14495                 :      */
   14496 CBC         940 :     if (tuple_class->relowner != newOwnerId)
   14497 ECB             :     {
   14498                 :         Datum       repl_val[Natts_pg_class];
   14499                 :         bool        repl_null[Natts_pg_class];
   14500                 :         bool        repl_repl[Natts_pg_class];
   14501                 :         Acl        *newAcl;
   14502                 :         Datum       aclDatum;
   14503                 :         bool        isNull;
   14504                 :         HeapTuple   newtuple;
   14505                 : 
   14506                 :         /* skip permission checks when recursing to index or toast table */
   14507 GIC         224 :         if (!recursing)
   14508 ECB             :         {
   14509                 :             /* Superusers can always do it */
   14510 CBC         136 :             if (!superuser())
   14511                 :             {
   14512              21 :                 Oid         namespaceOid = tuple_class->relnamespace;
   14513 ECB             :                 AclResult   aclresult;
   14514                 : 
   14515                 :                 /* Otherwise, must be owner of the existing object */
   14516 GNC          21 :                 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
   14517 LBC           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
   14518 UIC           0 :                                    RelationGetRelationName(target_rel));
   14519                 : 
   14520                 :                 /* Must be able to become new owner */
   14521 GNC          21 :                 check_can_set_role(GetUserId(), newOwnerId);
   14522                 : 
   14523                 :                 /* New owner must have CREATE privilege on namespace */
   14524              15 :                 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
   14525                 :                                                   ACL_CREATE);
   14526 CBC          15 :                 if (aclresult != ACLCHECK_OK)
   14527 UIC           0 :                     aclcheck_error(aclresult, OBJECT_SCHEMA,
   14528               0 :                                    get_namespace_name(namespaceOid));
   14529                 :             }
   14530                 :         }
   14531                 : 
   14532 CBC         218 :         memset(repl_null, false, sizeof(repl_null));
   14533 GIC         218 :         memset(repl_repl, false, sizeof(repl_repl));
   14534                 : 
   14535             218 :         repl_repl[Anum_pg_class_relowner - 1] = true;
   14536             218 :         repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
   14537                 : 
   14538                 :         /*
   14539                 :          * Determine the modified ACL for the new owner.  This is only
   14540                 :          * necessary when the ACL is non-null.
   14541                 :          */
   14542             218 :         aclDatum = SysCacheGetAttr(RELOID, tuple,
   14543 ECB             :                                    Anum_pg_class_relacl,
   14544                 :                                    &isNull);
   14545 GIC         218 :         if (!isNull)
   14546                 :         {
   14547              16 :             newAcl = aclnewowner(DatumGetAclP(aclDatum),
   14548                 :                                  tuple_class->relowner, newOwnerId);
   14549              16 :             repl_repl[Anum_pg_class_relacl - 1] = true;
   14550              16 :             repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
   14551                 :         }
   14552                 : 
   14553             218 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
   14554                 : 
   14555             218 :         CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
   14556                 : 
   14557 CBC         218 :         heap_freetuple(newtuple);
   14558 ECB             : 
   14559                 :         /*
   14560                 :          * We must similarly update any per-column ACLs to reflect the new
   14561                 :          * owner; for neatness reasons that's split out as a subroutine.
   14562                 :          */
   14563 GIC         218 :         change_owner_fix_column_acls(relationOid,
   14564 ECB             :                                      tuple_class->relowner,
   14565                 :                                      newOwnerId);
   14566                 : 
   14567                 :         /*
   14568                 :          * Update owner dependency reference, if any.  A composite type has
   14569                 :          * none, because it's tracked for the pg_type entry instead of here;
   14570                 :          * indexes and TOAST tables don't have their own entries either.
   14571                 :          */
   14572 GIC         218 :         if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
   14573             215 :             tuple_class->relkind != RELKIND_INDEX &&
   14574 CBC         173 :             tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
   14575 GIC         163 :             tuple_class->relkind != RELKIND_TOASTVALUE)
   14576             145 :             changeDependencyOnOwner(RelationRelationId, relationOid,
   14577                 :                                     newOwnerId);
   14578                 : 
   14579 ECB             :         /*
   14580                 :          * Also change the ownership of the table's row type, if it has one
   14581                 :          */
   14582 GIC         218 :         if (OidIsValid(tuple_class->reltype))
   14583 CBC         139 :             AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
   14584 ECB             : 
   14585                 :         /*
   14586                 :          * If we are operating on a table or materialized view, also change
   14587                 :          * the ownership of any indexes and sequences that belong to the
   14588                 :          * relation, as well as its toast table (if it has one).
   14589                 :          */
   14590 GIC         218 :         if (tuple_class->relkind == RELKIND_RELATION ||
   14591             112 :             tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
   14592              93 :             tuple_class->relkind == RELKIND_MATVIEW ||
   14593 CBC          93 :             tuple_class->relkind == RELKIND_TOASTVALUE)
   14594                 :         {
   14595                 :             List       *index_oid_list;
   14596                 :             ListCell   *i;
   14597                 : 
   14598                 :             /* Find all the indexes belonging to this relation */
   14599 GIC         143 :             index_oid_list = RelationGetIndexList(target_rel);
   14600                 : 
   14601                 :             /* For each index, recursively change its ownership */
   14602             195 :             foreach(i, index_oid_list)
   14603 CBC          52 :                 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
   14604                 : 
   14605             143 :             list_free(index_oid_list);
   14606                 :         }
   14607                 : 
   14608 ECB             :         /* If it has a toast table, recurse to change its ownership */
   14609 GIC         218 :         if (tuple_class->reltoastrelid != InvalidOid)
   14610 CBC          18 :             ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
   14611                 :                               true, lockmode);
   14612 ECB             : 
   14613                 :         /* If it has dependent sequences, recurse to change them too */
   14614 GIC         218 :         change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
   14615 ECB             :     }
   14616                 : 
   14617 CBC         934 :     InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
   14618                 : 
   14619             934 :     ReleaseSysCache(tuple);
   14620 GIC         934 :     table_close(class_rel, RowExclusiveLock);
   14621 CBC         934 :     relation_close(target_rel, NoLock);
   14622             934 : }
   14623 ECB             : 
   14624                 : /*
   14625                 :  * change_owner_fix_column_acls
   14626                 :  *
   14627                 :  * Helper function for ATExecChangeOwner.  Scan the columns of the table
   14628                 :  * and fix any non-null column ACLs to reflect the new owner.
   14629                 :  */
   14630                 : static void
   14631 GIC         218 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
   14632 ECB             : {
   14633                 :     Relation    attRelation;
   14634                 :     SysScanDesc scan;
   14635                 :     ScanKeyData key[1];
   14636                 :     HeapTuple   attributeTuple;
   14637                 : 
   14638 GIC         218 :     attRelation = table_open(AttributeRelationId, RowExclusiveLock);
   14639             218 :     ScanKeyInit(&key[0],
   14640                 :                 Anum_pg_attribute_attrelid,
   14641 ECB             :                 BTEqualStrategyNumber, F_OIDEQ,
   14642                 :                 ObjectIdGetDatum(relationOid));
   14643 GIC         218 :     scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
   14644 ECB             :                               true, NULL, 1, key);
   14645 CBC        1502 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   14646                 :     {
   14647            1284 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   14648                 :         Datum       repl_val[Natts_pg_attribute];
   14649 ECB             :         bool        repl_null[Natts_pg_attribute];
   14650                 :         bool        repl_repl[Natts_pg_attribute];
   14651                 :         Acl        *newAcl;
   14652                 :         Datum       aclDatum;
   14653                 :         bool        isNull;
   14654                 :         HeapTuple   newtuple;
   14655                 : 
   14656                 :         /* Ignore dropped columns */
   14657 GIC        1284 :         if (att->attisdropped)
   14658            1284 :             continue;
   14659                 : 
   14660            1284 :         aclDatum = heap_getattr(attributeTuple,
   14661 ECB             :                                 Anum_pg_attribute_attacl,
   14662                 :                                 RelationGetDescr(attRelation),
   14663                 :                                 &isNull);
   14664                 :         /* Null ACLs do not require changes */
   14665 CBC        1284 :         if (isNull)
   14666 GIC        1284 :             continue;
   14667 ECB             : 
   14668 UIC           0 :         memset(repl_null, false, sizeof(repl_null));
   14669 LBC           0 :         memset(repl_repl, false, sizeof(repl_repl));
   14670 ECB             : 
   14671 LBC           0 :         newAcl = aclnewowner(DatumGetAclP(aclDatum),
   14672 ECB             :                              oldOwnerId, newOwnerId);
   14673 LBC           0 :         repl_repl[Anum_pg_attribute_attacl - 1] = true;
   14674               0 :         repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
   14675 ECB             : 
   14676 UIC           0 :         newtuple = heap_modify_tuple(attributeTuple,
   14677                 :                                      RelationGetDescr(attRelation),
   14678 ECB             :                                      repl_val, repl_null, repl_repl);
   14679                 : 
   14680 UIC           0 :         CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
   14681                 : 
   14682               0 :         heap_freetuple(newtuple);
   14683 ECB             :     }
   14684 GIC         218 :     systable_endscan(scan);
   14685 CBC         218 :     table_close(attRelation, RowExclusiveLock);
   14686 GIC         218 : }
   14687                 : 
   14688                 : /*
   14689                 :  * change_owner_recurse_to_sequences
   14690                 :  *
   14691                 :  * Helper function for ATExecChangeOwner.  Examines pg_depend searching
   14692                 :  * for sequences that are dependent on serial columns, and changes their
   14693                 :  * ownership.
   14694                 :  */
   14695                 : static void
   14696 GBC         218 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
   14697                 : {
   14698                 :     Relation    depRel;
   14699                 :     SysScanDesc scan;
   14700 ECB             :     ScanKeyData key[2];
   14701                 :     HeapTuple   tup;
   14702                 : 
   14703                 :     /*
   14704                 :      * SERIAL sequences are those having an auto dependency on one of the
   14705                 :      * table's columns (we don't care *which* column, exactly).
   14706                 :      */
   14707 CBC         218 :     depRel = table_open(DependRelationId, AccessShareLock);
   14708                 : 
   14709             218 :     ScanKeyInit(&key[0],
   14710 ECB             :                 Anum_pg_depend_refclassid,
   14711                 :                 BTEqualStrategyNumber, F_OIDEQ,
   14712                 :                 ObjectIdGetDatum(RelationRelationId));
   14713 GIC         218 :     ScanKeyInit(&key[1],
   14714                 :                 Anum_pg_depend_refobjid,
   14715 ECB             :                 BTEqualStrategyNumber, F_OIDEQ,
   14716                 :                 ObjectIdGetDatum(relationOid));
   14717                 :     /* we leave refobjsubid unspecified */
   14718                 : 
   14719 GIC         218 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   14720 ECB             :                               NULL, 2, key);
   14721                 : 
   14722 GIC         605 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   14723 EUB             :     {
   14724 GIC         387 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   14725                 :         Relation    seqRel;
   14726 ECB             : 
   14727                 :         /* skip dependencies other than auto dependencies on columns */
   14728 CBC         387 :         if (depForm->refobjsubid == 0 ||
   14729 GIC         134 :             depForm->classid != RelationRelationId ||
   14730              61 :             depForm->objsubid != 0 ||
   14731              61 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   14732 CBC         326 :             continue;
   14733                 : 
   14734 ECB             :         /* Use relation_open just in case it's an index */
   14735 CBC          61 :         seqRel = relation_open(depForm->objid, lockmode);
   14736 ECB             : 
   14737                 :         /* skip non-sequence relations */
   14738 CBC          61 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   14739                 :         {
   14740                 :             /* No need to keep the lock */
   14741 GBC          52 :             relation_close(seqRel, lockmode);
   14742 GIC          52 :             continue;
   14743                 :         }
   14744                 : 
   14745 ECB             :         /* We don't need to close the sequence while we alter it. */
   14746 CBC           9 :         ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
   14747                 : 
   14748                 :         /* Now we can close it.  Keep the lock till end of transaction. */
   14749 GIC           9 :         relation_close(seqRel, NoLock);
   14750                 :     }
   14751                 : 
   14752             218 :     systable_endscan(scan);
   14753                 : 
   14754             218 :     relation_close(depRel, AccessShareLock);
   14755             218 : }
   14756                 : 
   14757                 : /*
   14758                 :  * ALTER TABLE CLUSTER ON
   14759 ECB             :  *
   14760                 :  * The only thing we have to do is to change the indisclustered bits.
   14761                 :  *
   14762                 :  * Return the address of the new clustering index.
   14763                 :  */
   14764                 : static ObjectAddress
   14765 GIC          32 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
   14766                 : {
   14767                 :     Oid         indexOid;
   14768 ECB             :     ObjectAddress address;
   14769                 : 
   14770 CBC          32 :     indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
   14771                 : 
   14772 GIC          32 :     if (!OidIsValid(indexOid))
   14773 LBC           0 :         ereport(ERROR,
   14774 ECB             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   14775                 :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   14776                 :                         indexName, RelationGetRelationName(rel))));
   14777                 : 
   14778                 :     /* Check index is valid to cluster on */
   14779 GIC          32 :     check_index_is_clusterable(rel, indexOid, lockmode);
   14780                 : 
   14781                 :     /* And do the work */
   14782              32 :     mark_index_clustered(rel, indexOid, false);
   14783                 : 
   14784 CBC          29 :     ObjectAddressSet(address,
   14785 ECB             :                      RelationRelationId, indexOid);
   14786                 : 
   14787 GIC          29 :     return address;
   14788                 : }
   14789 ECB             : 
   14790                 : /*
   14791                 :  * ALTER TABLE SET WITHOUT CLUSTER
   14792                 :  *
   14793                 :  * We have to find any indexes on the table that have indisclustered bit
   14794                 :  * set and turn it off.
   14795                 :  */
   14796                 : static void
   14797 GIC           9 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
   14798                 : {
   14799               9 :     mark_index_clustered(rel, InvalidOid, false);
   14800               6 : }
   14801                 : 
   14802                 : /*
   14803 ECB             :  * Preparation phase for SET ACCESS METHOD
   14804                 :  *
   14805                 :  * Check that access method exists.  If it is the same as the table's current
   14806                 :  * access method, it is a no-op.  Otherwise, a table rewrite is necessary.
   14807                 :  */
   14808                 : static void
   14809 GIC          15 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
   14810 ECB             : {
   14811                 :     Oid         amoid;
   14812                 : 
   14813                 :     /* Check that the table access method exists */
   14814 GIC          15 :     amoid = get_table_am_oid(amname, false);
   14815 ECB             : 
   14816 CBC          15 :     if (rel->rd_rel->relam == amoid)
   14817 LBC           0 :         return;
   14818                 : 
   14819 ECB             :     /* Save info for Phase 3 to do the real work */
   14820 GIC          15 :     tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
   14821 CBC          15 :     tab->newAccessMethod = amoid;
   14822                 : }
   14823                 : 
   14824                 : /*
   14825                 :  * ALTER TABLE SET TABLESPACE
   14826                 :  */
   14827                 : static void
   14828 GIC          79 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
   14829                 : {
   14830                 :     Oid         tablespaceId;
   14831 ECB             : 
   14832                 :     /* Check that the tablespace exists */
   14833 GIC          79 :     tablespaceId = get_tablespace_oid(tablespacename, false);
   14834                 : 
   14835                 :     /* Check permissions except when moving to database's default */
   14836              79 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
   14837                 :     {
   14838                 :         AclResult   aclresult;
   14839                 : 
   14840 GNC          33 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
   14841 CBC          33 :         if (aclresult != ACLCHECK_OK)
   14842 UIC           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
   14843 ECB             :     }
   14844                 : 
   14845 EUB             :     /* Save info for Phase 3 to do the real work */
   14846 GIC          79 :     if (OidIsValid(tab->newTableSpace))
   14847 LBC           0 :         ereport(ERROR,
   14848                 :                 (errcode(ERRCODE_SYNTAX_ERROR),
   14849 ECB             :                  errmsg("cannot have multiple SET TABLESPACE subcommands")));
   14850                 : 
   14851 GIC          79 :     tab->newTableSpace = tablespaceId;
   14852 CBC          79 : }
   14853 ECB             : 
   14854                 : /*
   14855 EUB             :  * Set, reset, or replace reloptions.
   14856 ECB             :  */
   14857                 : static void
   14858 GIC         464 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
   14859 ECB             :                     LOCKMODE lockmode)
   14860                 : {
   14861                 :     Oid         relid;
   14862                 :     Relation    pgclass;
   14863                 :     HeapTuple   tuple;
   14864                 :     HeapTuple   newtuple;
   14865                 :     Datum       datum;
   14866                 :     bool        isnull;
   14867                 :     Datum       newOptions;
   14868                 :     Datum       repl_val[Natts_pg_class];
   14869                 :     bool        repl_null[Natts_pg_class];
   14870                 :     bool        repl_repl[Natts_pg_class];
   14871                 :     static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
   14872                 : 
   14873 GIC         464 :     if (defList == NIL && operation != AT_ReplaceRelOptions)
   14874 UIC           0 :         return;                 /* nothing to do */
   14875                 : 
   14876 GIC         464 :     pgclass = table_open(RelationRelationId, RowExclusiveLock);
   14877                 : 
   14878                 :     /* Fetch heap tuple */
   14879             464 :     relid = RelationGetRelid(rel);
   14880             464 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   14881             464 :     if (!HeapTupleIsValid(tuple))
   14882 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   14883                 : 
   14884 GIC         464 :     if (operation == AT_ReplaceRelOptions)
   14885                 :     {
   14886                 :         /*
   14887                 :          * If we're supposed to replace the reloptions list, we just pretend
   14888                 :          * there were none before.
   14889                 :          */
   14890              97 :         datum = (Datum) 0;
   14891              97 :         isnull = true;
   14892 ECB             :     }
   14893 EUB             :     else
   14894                 :     {
   14895                 :         /* Get the old reloptions */
   14896 CBC         367 :         datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   14897 ECB             :                                 &isnull);
   14898                 :     }
   14899 EUB             : 
   14900                 :     /* Generate new proposed reloptions (text array) */
   14901 GIC         464 :     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
   14902                 :                                      defList, NULL, validnsps, false,
   14903 ECB             :                                      operation == AT_ResetRelOptions);
   14904                 : 
   14905                 :     /* Validate */
   14906 GIC         461 :     switch (rel->rd_rel->relkind)
   14907 ECB             :     {
   14908 CBC         253 :         case RELKIND_RELATION:
   14909                 :         case RELKIND_TOASTVALUE:
   14910 ECB             :         case RELKIND_MATVIEW:
   14911 CBC         253 :             (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
   14912             253 :             break;
   14913 GBC           3 :         case RELKIND_PARTITIONED_TABLE:
   14914 GIC           3 :             (void) partitioned_table_reloptions(newOptions, true);
   14915 UIC           0 :             break;
   14916 GIC         148 :         case RELKIND_VIEW:
   14917             148 :             (void) view_reloptions(newOptions, true);
   14918             139 :             break;
   14919 CBC          57 :         case RELKIND_INDEX:
   14920 ECB             :         case RELKIND_PARTITIONED_INDEX:
   14921 CBC          57 :             (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
   14922              46 :             break;
   14923 UIC           0 :         default:
   14924               0 :             ereport(ERROR,
   14925                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14926                 :                      errmsg("cannot set options for relation \"%s\"",
   14927                 :                             RelationGetRelationName(rel)),
   14928 ECB             :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
   14929                 :             break;
   14930                 :     }
   14931                 : 
   14932                 :     /* Special-case validation of view options */
   14933 CBC         438 :     if (rel->rd_rel->relkind == RELKIND_VIEW)
   14934                 :     {
   14935 GIC         139 :         Query      *view_query = get_view_query(rel);
   14936             139 :         List       *view_options = untransformRelOptions(newOptions);
   14937 ECB             :         ListCell   *cell;
   14938 CBC         139 :         bool        check_option = false;
   14939                 : 
   14940 GIC         190 :         foreach(cell, view_options)
   14941 ECB             :         {
   14942 GIC          51 :             DefElem    *defel = (DefElem *) lfirst(cell);
   14943                 : 
   14944              51 :             if (strcmp(defel->defname, "check_option") == 0)
   14945              12 :                 check_option = true;
   14946 ECB             :         }
   14947                 : 
   14948                 :         /*
   14949 EUB             :          * If the check option is specified, look to see if the view is
   14950                 :          * actually auto-updatable or not.
   14951 ECB             :          */
   14952 GIC         139 :         if (check_option)
   14953                 :         {
   14954                 :             const char *view_updatable_error =
   14955 CBC          12 :             view_query_is_auto_updatable(view_query, true);
   14956                 : 
   14957 GIC          12 :             if (view_updatable_error)
   14958 LBC           0 :                 ereport(ERROR,
   14959                 :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14960 ECB             :                          errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
   14961                 :                          errhint("%s", _(view_updatable_error))));
   14962                 :         }
   14963                 :     }
   14964                 : 
   14965                 :     /*
   14966                 :      * All we need do here is update the pg_class row; the new options will be
   14967                 :      * propagated into relcaches during post-commit cache inval.
   14968                 :      */
   14969 GIC         438 :     memset(repl_val, 0, sizeof(repl_val));
   14970 CBC         438 :     memset(repl_null, false, sizeof(repl_null));
   14971 GIC         438 :     memset(repl_repl, false, sizeof(repl_repl));
   14972 ECB             : 
   14973 GIC         438 :     if (newOptions != (Datum) 0)
   14974             294 :         repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   14975                 :     else
   14976             144 :         repl_null[Anum_pg_class_reloptions - 1] = true;
   14977                 : 
   14978             438 :     repl_repl[Anum_pg_class_reloptions - 1] = true;
   14979                 : 
   14980             438 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   14981                 :                                  repl_val, repl_null, repl_repl);
   14982                 : 
   14983             438 :     CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   14984                 : 
   14985             438 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   14986                 : 
   14987             438 :     heap_freetuple(newtuple);
   14988 ECB             : 
   14989 GIC         438 :     ReleaseSysCache(tuple);
   14990                 : 
   14991                 :     /* repeat the whole exercise for the toast table, if there's one */
   14992             438 :     if (OidIsValid(rel->rd_rel->reltoastrelid))
   14993                 :     {
   14994                 :         Relation    toastrel;
   14995             128 :         Oid         toastid = rel->rd_rel->reltoastrelid;
   14996                 : 
   14997             128 :         toastrel = table_open(toastid, lockmode);
   14998                 : 
   14999 ECB             :         /* Fetch heap tuple */
   15000 GIC         128 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
   15001             128 :         if (!HeapTupleIsValid(tuple))
   15002 LBC           0 :             elog(ERROR, "cache lookup failed for relation %u", toastid);
   15003                 : 
   15004 CBC         128 :         if (operation == AT_ReplaceRelOptions)
   15005 ECB             :         {
   15006 EUB             :             /*
   15007 ECB             :              * If we're supposed to replace the reloptions list, we just
   15008                 :              * pretend there were none before.
   15009                 :              */
   15010 LBC           0 :             datum = (Datum) 0;
   15011 UIC           0 :             isnull = true;
   15012 ECB             :         }
   15013                 :         else
   15014                 :         {
   15015                 :             /* Get the old reloptions */
   15016 GIC         128 :             datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   15017                 :                                     &isnull);
   15018 ECB             :         }
   15019                 : 
   15020 CBC         128 :         newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
   15021                 :                                          defList, "toast", validnsps, false,
   15022                 :                                          operation == AT_ResetRelOptions);
   15023                 : 
   15024 GIC         128 :         (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
   15025                 : 
   15026             128 :         memset(repl_val, 0, sizeof(repl_val));
   15027             128 :         memset(repl_null, false, sizeof(repl_null));
   15028             128 :         memset(repl_repl, false, sizeof(repl_repl));
   15029 EUB             : 
   15030 GBC         128 :         if (newOptions != (Datum) 0)
   15031 GIC          21 :             repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   15032                 :         else
   15033             107 :             repl_null[Anum_pg_class_reloptions - 1] = true;
   15034                 : 
   15035             128 :         repl_repl[Anum_pg_class_reloptions - 1] = true;
   15036 EUB             : 
   15037 GIC         128 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   15038 ECB             :                                      repl_val, repl_null, repl_repl);
   15039                 : 
   15040 CBC         128 :         CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   15041 ECB             : 
   15042 GBC         128 :         InvokeObjectPostAlterHookArg(RelationRelationId,
   15043                 :                                      RelationGetRelid(toastrel), 0,
   15044                 :                                      InvalidOid, true);
   15045                 : 
   15046 GIC         128 :         heap_freetuple(newtuple);
   15047                 : 
   15048 CBC         128 :         ReleaseSysCache(tuple);
   15049 ECB             : 
   15050 CBC         128 :         table_close(toastrel, NoLock);
   15051                 :     }
   15052                 : 
   15053 GIC         438 :     table_close(pgclass, RowExclusiveLock);
   15054                 : }
   15055                 : 
   15056 EUB             : /*
   15057                 :  * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
   15058                 :  * rewriting to be done, so we just want to copy the data as fast as possible.
   15059                 :  */
   15060                 : static void
   15061 GIC          81 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
   15062                 : {
   15063                 :     Relation    rel;
   15064                 :     Oid         reltoastrelid;
   15065                 :     RelFileNumber newrelfilenumber;
   15066                 :     RelFileLocator newrlocator;
   15067 CBC          81 :     List       *reltoastidxids = NIL;
   15068 ECB             :     ListCell   *lc;
   15069                 : 
   15070 EUB             :     /*
   15071                 :      * Need lock here in case we are recursing to toast table or index
   15072                 :      */
   15073 GIC          81 :     rel = relation_open(tableOid, lockmode);
   15074                 : 
   15075                 :     /* Check first if relation can be moved to new tablespace */
   15076 CBC          81 :     if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   15077 ECB             :     {
   15078 CBC           1 :         InvokeObjectPostAlterHook(RelationRelationId,
   15079                 :                                   RelationGetRelid(rel), 0);
   15080 GIC           1 :         relation_close(rel, NoLock);
   15081 GBC           1 :         return;
   15082                 :     }
   15083                 : 
   15084 GIC          80 :     reltoastrelid = rel->rd_rel->reltoastrelid;
   15085                 :     /* Fetch the list of indexes on toast relation if necessary */
   15086              80 :     if (OidIsValid(reltoastrelid))
   15087                 :     {
   15088              10 :         Relation    toastRel = relation_open(reltoastrelid, lockmode);
   15089                 : 
   15090              10 :         reltoastidxids = RelationGetIndexList(toastRel);
   15091              10 :         relation_close(toastRel, lockmode);
   15092 ECB             :     }
   15093                 : 
   15094                 :     /*
   15095                 :      * Relfilenumbers are not unique in databases across tablespaces, so we
   15096                 :      * need to allocate a new one in the new tablespace.
   15097                 :      */
   15098 GNC          80 :     newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
   15099              80 :                                            rel->rd_rel->relpersistence);
   15100                 : 
   15101                 :     /* Open old and new relation */
   15102              80 :     newrlocator = rel->rd_locator;
   15103              80 :     newrlocator.relNumber = newrelfilenumber;
   15104              80 :     newrlocator.spcOid = newTableSpace;
   15105                 : 
   15106                 :     /* hand off to AM to actually create new rel storage and copy the data */
   15107 GIC          80 :     if (rel->rd_rel->relkind == RELKIND_INDEX)
   15108 ECB             :     {
   15109 GNC          31 :         index_copy_data(rel, newrlocator);
   15110                 :     }
   15111                 :     else
   15112 ECB             :     {
   15113 GBC          49 :         Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
   15114 GNC          49 :         table_relation_copy_data(rel, &newrlocator);
   15115                 :     }
   15116                 : 
   15117 ECB             :     /*
   15118                 :      * Update the pg_class row.
   15119                 :      *
   15120                 :      * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
   15121                 :      * executed on pg_class or its indexes (the above copy wouldn't contain
   15122                 :      * the updated pg_class entry), but that's forbidden with
   15123 EUB             :      * CheckRelationTableSpaceMove().
   15124                 :      */
   15125 GNC          80 :     SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
   15126                 : 
   15127 GIC          80 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15128 ECB             : 
   15129 GNC          80 :     RelationAssumeNewRelfilelocator(rel);
   15130                 : 
   15131 CBC          80 :     relation_close(rel, NoLock);
   15132 ECB             : 
   15133                 :     /* Make sure the reltablespace change is visible */
   15134 GIC          80 :     CommandCounterIncrement();
   15135                 : 
   15136                 :     /* Move associated toast relation and/or indexes, too */
   15137              80 :     if (OidIsValid(reltoastrelid))
   15138 CBC          10 :         ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
   15139 GIC          90 :     foreach(lc, reltoastidxids)
   15140              10 :         ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
   15141 ECB             : 
   15142                 :     /* Clean up */
   15143 CBC          80 :     list_free(reltoastidxids);
   15144                 : }
   15145 ECB             : 
   15146                 : /*
   15147                 :  * Special handling of ALTER TABLE SET TABLESPACE for relations with no
   15148                 :  * storage that have an interest in preserving tablespace.
   15149                 :  *
   15150                 :  * Since these have no storage the tablespace can be updated with a simple
   15151                 :  * metadata only operation to update the tablespace.
   15152                 :  */
   15153                 : static void
   15154 GIC          18 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
   15155                 : {
   15156                 :     /*
   15157                 :      * Shouldn't be called on relations having storage; these are processed in
   15158                 :      * phase 3.
   15159 ECB             :      */
   15160 GIC          18 :     Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   15161                 : 
   15162                 :     /* check if relation can be moved to its new tablespace */
   15163              18 :     if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   15164                 :     {
   15165 UIC           0 :         InvokeObjectPostAlterHook(RelationRelationId,
   15166                 :                                   RelationGetRelid(rel),
   15167                 :                                   0);
   15168 LBC           0 :         return;
   15169 ECB             :     }
   15170                 : 
   15171                 :     /* Update can be done, so change reltablespace */
   15172 CBC          15 :     SetRelationTableSpace(rel, newTableSpace, InvalidOid);
   15173                 : 
   15174 GIC          15 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   15175                 : 
   15176                 :     /* Make sure the reltablespace change is visible */
   15177              15 :     CommandCounterIncrement();
   15178 ECB             : }
   15179                 : 
   15180                 : /*
   15181                 :  * Alter Table ALL ... SET TABLESPACE
   15182                 :  *
   15183                 :  * Allows a user to move all objects of some type in a given tablespace in the
   15184                 :  * current database to another tablespace.  Objects can be chosen based on the
   15185                 :  * owner of the object also, to allow users to move only their objects.
   15186                 :  * The user must have CREATE rights on the new tablespace, as usual.   The main
   15187                 :  * permissions handling is done by the lower-level table move function.
   15188                 :  *
   15189                 :  * All to-be-moved objects are locked first. If NOWAIT is specified and the
   15190                 :  * lock can't be acquired then we ereport(ERROR).
   15191                 :  */
   15192                 : Oid
   15193 GIC          15 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
   15194                 : {
   15195 CBC          15 :     List       *relations = NIL;
   15196                 :     ListCell   *l;
   15197                 :     ScanKeyData key[1];
   15198 ECB             :     Relation    rel;
   15199                 :     TableScanDesc scan;
   15200                 :     HeapTuple   tuple;
   15201                 :     Oid         orig_tablespaceoid;
   15202                 :     Oid         new_tablespaceoid;
   15203 GIC          15 :     List       *role_oids = roleSpecsToIds(stmt->roles);
   15204                 : 
   15205 ECB             :     /* Ensure we were not asked to move something we can't */
   15206 CBC          15 :     if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
   15207 GIC           6 :         stmt->objtype != OBJECT_MATVIEW)
   15208 UIC           0 :         ereport(ERROR,
   15209                 :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   15210 ECB             :                  errmsg("only tables, indexes, and materialized views exist in tablespaces")));
   15211                 : 
   15212                 :     /* Get the orig and new tablespace OIDs */
   15213 CBC          15 :     orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
   15214 GIC          15 :     new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
   15215 ECB             : 
   15216                 :     /* Can't move shared relations in to or out of pg_global */
   15217                 :     /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
   15218 CBC          15 :     if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
   15219                 :         new_tablespaceoid == GLOBALTABLESPACE_OID)
   15220 UIC           0 :         ereport(ERROR,
   15221                 :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   15222                 :                  errmsg("cannot move relations in to or out of pg_global tablespace")));
   15223                 : 
   15224                 :     /*
   15225                 :      * Must have CREATE rights on the new tablespace, unless it is the
   15226                 :      * database default tablespace (which all users implicitly have CREATE
   15227 ECB             :      * rights on).
   15228                 :      */
   15229 GIC          15 :     if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
   15230                 :     {
   15231                 :         AclResult   aclresult;
   15232                 : 
   15233 UNC           0 :         aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
   15234 ECB             :                                            ACL_CREATE);
   15235 LBC           0 :         if (aclresult != ACLCHECK_OK)
   15236 UIC           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
   15237               0 :                            get_tablespace_name(new_tablespaceoid));
   15238                 :     }
   15239 ECB             : 
   15240                 :     /*
   15241                 :      * Now that the checks are done, check if we should set either to
   15242                 :      * InvalidOid because it is our database's default tablespace.
   15243                 :      */
   15244 GIC          15 :     if (orig_tablespaceoid == MyDatabaseTableSpace)
   15245 UIC           0 :         orig_tablespaceoid = InvalidOid;
   15246                 : 
   15247 GIC          15 :     if (new_tablespaceoid == MyDatabaseTableSpace)
   15248              15 :         new_tablespaceoid = InvalidOid;
   15249                 : 
   15250                 :     /* no-op */
   15251              15 :     if (orig_tablespaceoid == new_tablespaceoid)
   15252 UIC           0 :         return new_tablespaceoid;
   15253 ECB             : 
   15254                 :     /*
   15255                 :      * Walk the list of objects in the tablespace and move them. This will
   15256                 :      * only find objects in our database, of course.
   15257                 :      */
   15258 GIC          15 :     ScanKeyInit(&key[0],
   15259                 :                 Anum_pg_class_reltablespace,
   15260                 :                 BTEqualStrategyNumber, F_OIDEQ,
   15261 ECB             :                 ObjectIdGetDatum(orig_tablespaceoid));
   15262                 : 
   15263 GIC          15 :     rel = table_open(RelationRelationId, AccessShareLock);
   15264 GBC          15 :     scan = table_beginscan_catalog(rel, 1, key);
   15265              66 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
   15266                 :     {
   15267              51 :         Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
   15268 GIC          51 :         Oid         relOid = relForm->oid;
   15269 EUB             : 
   15270                 :         /*
   15271                 :          * Do not move objects in pg_catalog as part of this, if an admin
   15272                 :          * really wishes to do so, they can issue the individual ALTER
   15273                 :          * commands directly.
   15274                 :          *
   15275                 :          * Also, explicitly avoid any shared tables, temp tables, or TOAST
   15276                 :          * (TOAST will be moved with the main table).
   15277                 :          */
   15278 GBC          51 :         if (IsCatalogNamespace(relForm->relnamespace) ||
   15279 GIC         102 :             relForm->relisshared ||
   15280 CBC         102 :             isAnyTempNamespace(relForm->relnamespace) ||
   15281              51 :             IsToastNamespace(relForm->relnamespace))
   15282 LBC           0 :             continue;
   15283                 : 
   15284                 :         /* Only move the object type requested */
   15285 GIC          51 :         if ((stmt->objtype == OBJECT_TABLE &&
   15286              30 :              relForm->relkind != RELKIND_RELATION &&
   15287              18 :              relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
   15288              33 :             (stmt->objtype == OBJECT_INDEX &&
   15289              18 :              relForm->relkind != RELKIND_INDEX &&
   15290               3 :              relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
   15291              30 :             (stmt->objtype == OBJECT_MATVIEW &&
   15292 CBC           3 :              relForm->relkind != RELKIND_MATVIEW))
   15293 GIC          21 :             continue;
   15294                 : 
   15295                 :         /* Check if we are only moving objects owned by certain roles */
   15296              30 :         if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
   15297 UIC           0 :             continue;
   15298                 : 
   15299                 :         /*
   15300                 :          * Handle permissions-checking here since we are locking the tables
   15301                 :          * and also to avoid doing a bunch of work only to fail part-way. Note
   15302                 :          * that permissions will also be checked by AlterTableInternal().
   15303 ECB             :          *
   15304                 :          * Caller must be considered an owner on the table to move it.
   15305                 :          */
   15306 GNC          30 :         if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
   15307 UIC           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
   15308               0 :                            NameStr(relForm->relname));
   15309 ECB             : 
   15310 GIC          30 :         if (stmt->nowait &&
   15311 UIC           0 :             !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
   15312               0 :             ereport(ERROR,
   15313                 :                     (errcode(ERRCODE_OBJECT_IN_USE),
   15314                 :                      errmsg("aborting because lock on relation \"%s.%s\" is not available",
   15315 ECB             :                             get_namespace_name(relForm->relnamespace),
   15316                 :                             NameStr(relForm->relname))));
   15317                 :         else
   15318 CBC          30 :             LockRelationOid(relOid, AccessExclusiveLock);
   15319                 : 
   15320 ECB             :         /* Add to our list of objects to move */
   15321 GIC          30 :         relations = lappend_oid(relations, relOid);
   15322                 :     }
   15323                 : 
   15324 CBC          15 :     table_endscan(scan);
   15325              15 :     table_close(rel, AccessShareLock);
   15326 ECB             : 
   15327 CBC          15 :     if (relations == NIL)
   15328               6 :         ereport(NOTICE,
   15329                 :                 (errcode(ERRCODE_NO_DATA_FOUND),
   15330                 :                  errmsg("no matching relations in tablespace \"%s\" found",
   15331 ECB             :                         orig_tablespaceoid == InvalidOid ? "(database default)" :
   15332                 :                         get_tablespace_name(orig_tablespaceoid))));
   15333                 : 
   15334                 :     /* Everything is locked, loop through and move all of the relations. */
   15335 GIC          45 :     foreach(l, relations)
   15336                 :     {
   15337 CBC          30 :         List       *cmds = NIL;
   15338              30 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   15339                 : 
   15340 GIC          30 :         cmd->subtype = AT_SetTableSpace;
   15341              30 :         cmd->name = stmt->new_tablespacename;
   15342 ECB             : 
   15343 GIC          30 :         cmds = lappend(cmds, cmd);
   15344                 : 
   15345 CBC          30 :         EventTriggerAlterTableStart((Node *) stmt);
   15346                 :         /* OID is set by AlterTableInternal */
   15347 GIC          30 :         AlterTableInternal(lfirst_oid(l), cmds, false);
   15348 CBC          30 :         EventTriggerAlterTableEnd();
   15349                 :     }
   15350 ECB             : 
   15351 CBC          15 :     return new_tablespaceoid;
   15352                 : }
   15353                 : 
   15354                 : static void
   15355 GNC          31 : index_copy_data(Relation rel, RelFileLocator newrlocator)
   15356                 : {
   15357                 :     SMgrRelation dstrel;
   15358                 : 
   15359              31 :     dstrel = smgropen(newrlocator, rel->rd_backend);
   15360                 : 
   15361 ECB             :     /*
   15362                 :      * Since we copy the file directly without looking at the shared buffers,
   15363                 :      * we'd better first flush out any pages of the source relation that are
   15364                 :      * in shared buffers.  We assume no new changes will be made while we are
   15365                 :      * holding exclusive lock on the rel.
   15366                 :      */
   15367 GIC          31 :     FlushRelationBuffers(rel);
   15368 ECB             : 
   15369 EUB             :     /*
   15370                 :      * Create and copy all forks of the relation, and schedule unlinking of
   15371                 :      * old physical files.
   15372                 :      *
   15373                 :      * NOTE: any conflict in relfilenumber value will be caught in
   15374                 :      * RelationCreateStorage().
   15375 ECB             :      */
   15376 GNC          31 :     RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
   15377                 : 
   15378 ECB             :     /* copy main fork */
   15379 GIC          31 :     RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
   15380 CBC          31 :                         rel->rd_rel->relpersistence);
   15381                 : 
   15382                 :     /* copy those extra forks that exist */
   15383              31 :     for (ForkNumber forkNum = MAIN_FORKNUM + 1;
   15384 GIC         124 :          forkNum <= MAX_FORKNUM; forkNum++)
   15385                 :     {
   15386              93 :         if (smgrexists(RelationGetSmgr(rel), forkNum))
   15387                 :         {
   15388 UIC           0 :             smgrcreate(dstrel, forkNum, false);
   15389                 : 
   15390                 :             /*
   15391                 :              * WAL log creation if the relation is persistent, or this is the
   15392                 :              * init fork of an unlogged relation.
   15393 ECB             :              */
   15394 UIC           0 :             if (RelationIsPermanent(rel) ||
   15395 LBC           0 :                 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
   15396 ECB             :                  forkNum == INIT_FORKNUM))
   15397 UNC           0 :                 log_smgrcreate(&newrlocator, forkNum);
   15398 UIC           0 :             RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
   15399               0 :                                 rel->rd_rel->relpersistence);
   15400                 :         }
   15401                 :     }
   15402                 : 
   15403                 :     /* drop old relation, and close new one */
   15404 GIC          31 :     RelationDropStorage(rel);
   15405 CBC          31 :     smgrclose(dstrel);
   15406 GIC          31 : }
   15407                 : 
   15408                 : /*
   15409                 :  * ALTER TABLE ENABLE/DISABLE TRIGGER
   15410 ECB             :  *
   15411                 :  * We just pass this off to trigger.c.
   15412                 :  */
   15413 EUB             : static void
   15414 GIC         168 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
   15415                 :                            char fires_when, bool skip_system, bool recurse,
   15416 ECB             :                            LOCKMODE lockmode)
   15417                 : {
   15418 GNC         168 :     EnableDisableTrigger(rel, trigname, InvalidOid,
   15419                 :                          fires_when, skip_system, recurse,
   15420                 :                          lockmode);
   15421 GIC         168 : }
   15422                 : 
   15423                 : /*
   15424 ECB             :  * ALTER TABLE ENABLE/DISABLE RULE
   15425                 :  *
   15426                 :  * We just pass this off to rewriteDefine.c.
   15427                 :  */
   15428                 : static void
   15429 CBC           9 : ATExecEnableDisableRule(Relation rel, const char *rulename,
   15430                 :                         char fires_when, LOCKMODE lockmode)
   15431                 : {
   15432               9 :     EnableDisableRule(rel, rulename, fires_when);
   15433 GIC           9 : }
   15434                 : 
   15435                 : /*
   15436 ECB             :  * ALTER TABLE INHERIT
   15437                 :  *
   15438 EUB             :  * Add a parent to the child's parents. This verifies that all the columns and
   15439                 :  * check constraints of the parent appear in the child and that they have the
   15440                 :  * same data types and expressions.
   15441                 :  */
   15442 ECB             : static void
   15443 GBC         143 : ATPrepAddInherit(Relation child_rel)
   15444                 : {
   15445 GIC         143 :     if (child_rel->rd_rel->reloftype)
   15446               3 :         ereport(ERROR,
   15447 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15448                 :                  errmsg("cannot change inheritance of typed table")));
   15449                 : 
   15450 GIC         140 :     if (child_rel->rd_rel->relispartition)
   15451               3 :         ereport(ERROR,
   15452                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15453                 :                  errmsg("cannot change inheritance of a partition")));
   15454 ECB             : 
   15455 GIC         137 :     if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   15456               3 :         ereport(ERROR,
   15457                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15458                 :                  errmsg("cannot change inheritance of partitioned table")));
   15459             134 : }
   15460                 : 
   15461                 : /*
   15462                 :  * Return the address of the new parent relation.
   15463                 :  */
   15464                 : static ObjectAddress
   15465             134 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
   15466                 : {
   15467                 :     Relation    parent_rel;
   15468                 :     List       *children;
   15469 ECB             :     ObjectAddress address;
   15470 EUB             :     const char *trigger_name;
   15471                 : 
   15472 ECB             :     /*
   15473                 :      * A self-exclusive lock is needed here.  See the similar case in
   15474                 :      * MergeAttributes() for a full explanation.
   15475                 :      */
   15476 CBC         134 :     parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
   15477 ECB             : 
   15478 EUB             :     /*
   15479                 :      * Must be owner of both parent and child -- child was checked by
   15480 ECB             :      * ATSimplePermissions call in ATPrepCmd
   15481                 :      */
   15482 GIC         134 :     ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
   15483                 : 
   15484                 :     /* Permanent rels cannot inherit from temporary ones */
   15485             134 :     if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   15486 CBC           3 :         child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   15487 LBC           0 :         ereport(ERROR,
   15488                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15489                 :                  errmsg("cannot inherit from temporary relation \"%s\"",
   15490                 :                         RelationGetRelationName(parent_rel))));
   15491                 : 
   15492 ECB             :     /* If parent rel is temp, it must belong to this session */
   15493 GIC         134 :     if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   15494               3 :         !parent_rel->rd_islocaltemp)
   15495 UIC           0 :         ereport(ERROR,
   15496                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15497 ECB             :                  errmsg("cannot inherit from temporary relation of another session")));
   15498                 : 
   15499                 :     /* Ditto for the child */
   15500 GIC         134 :     if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   15501               3 :         !child_rel->rd_islocaltemp)
   15502 LBC           0 :         ereport(ERROR,
   15503                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15504 ECB             :                  errmsg("cannot inherit to temporary relation of another session")));
   15505                 : 
   15506                 :     /* Prevent partitioned tables from becoming inheritance parents */
   15507 CBC         134 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   15508               3 :         ereport(ERROR,
   15509 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15510                 :                  errmsg("cannot inherit from partitioned table \"%s\"",
   15511 EUB             :                         parent->relname)));
   15512 ECB             : 
   15513                 :     /* Likewise for partitions */
   15514 CBC         131 :     if (parent_rel->rd_rel->relispartition)
   15515               3 :         ereport(ERROR,
   15516                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15517 ECB             :                  errmsg("cannot inherit from a partition")));
   15518                 : 
   15519 EUB             :     /*
   15520                 :      * Prevent circularity by seeing if proposed parent inherits from child.
   15521                 :      * (In particular, this disallows making a rel inherit from itself.)
   15522                 :      *
   15523                 :      * This is not completely bulletproof because of race conditions: in
   15524                 :      * multi-level inheritance trees, someone else could concurrently be
   15525                 :      * making another inheritance link that closes the loop but does not join
   15526                 :      * either of the rels we have locked.  Preventing that seems to require
   15527                 :      * exclusive locks on the entire inheritance tree, which is a cure worse
   15528                 :      * than the disease.  find_all_inheritors() will cope with circularity
   15529 ECB             :      * anyway, so don't sweat it too much.
   15530                 :      *
   15531                 :      * We use weakest lock we can on child's children, namely AccessShareLock.
   15532                 :      */
   15533 GIC         128 :     children = find_all_inheritors(RelationGetRelid(child_rel),
   15534 ECB             :                                    AccessShareLock, NULL);
   15535                 : 
   15536 CBC         128 :     if (list_member_oid(children, RelationGetRelid(parent_rel)))
   15537 GIC           6 :         ereport(ERROR,
   15538 ECB             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   15539                 :                  errmsg("circular inheritance not allowed"),
   15540                 :                  errdetail("\"%s\" is already a child of \"%s\".",
   15541                 :                            parent->relname,
   15542                 :                            RelationGetRelationName(child_rel))));
   15543                 : 
   15544                 :     /*
   15545                 :      * If child_rel has row-level triggers with transition tables, we
   15546                 :      * currently don't allow it to become an inheritance child.  See also
   15547                 :      * prohibitions in ATExecAttachPartition() and CreateTrigger().
   15548                 :      */
   15549 GIC         122 :     trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
   15550             122 :     if (trigger_name != NULL)
   15551 CBC           3 :         ereport(ERROR,
   15552                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15553 ECB             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
   15554 EUB             :                         trigger_name, RelationGetRelationName(child_rel)),
   15555                 :                  errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
   15556                 : 
   15557                 :     /* OK to create inheritance */
   15558 GIC         119 :     CreateInheritance(child_rel, parent_rel);
   15559                 : 
   15560              95 :     ObjectAddressSet(address, RelationRelationId,
   15561                 :                      RelationGetRelid(parent_rel));
   15562                 : 
   15563                 :     /* keep our lock on the parent relation until commit */
   15564              95 :     table_close(parent_rel, NoLock);
   15565 ECB             : 
   15566 CBC          95 :     return address;
   15567 ECB             : }
   15568                 : 
   15569                 : /*
   15570                 :  * CreateInheritance
   15571                 :  *      Catalog manipulation portion of creating inheritance between a child
   15572                 :  *      table and a parent table.
   15573                 :  *
   15574                 :  * Common to ATExecAddInherit() and ATExecAttachPartition().
   15575                 :  */
   15576                 : static void
   15577 GIC        1087 : CreateInheritance(Relation child_rel, Relation parent_rel)
   15578                 : {
   15579 ECB             :     Relation    catalogRelation;
   15580                 :     SysScanDesc scan;
   15581                 :     ScanKeyData key;
   15582                 :     HeapTuple   inheritsTuple;
   15583                 :     int32       inhseqno;
   15584                 : 
   15585                 :     /* Note: get RowExclusiveLock because we will write pg_inherits below. */
   15586 GIC        1087 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   15587                 : 
   15588 ECB             :     /*
   15589                 :      * Check for duplicates in the list of parents, and determine the highest
   15590                 :      * inhseqno already present; we'll use the next one for the new parent.
   15591                 :      * Also, if proposed child is a partition, it cannot already be
   15592                 :      * inheriting.
   15593                 :      *
   15594                 :      * Note: we do not reject the case where the child already inherits from
   15595                 :      * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
   15596                 :      */
   15597 CBC        1087 :     ScanKeyInit(&key,
   15598 EUB             :                 Anum_pg_inherits_inhrelid,
   15599                 :                 BTEqualStrategyNumber, F_OIDEQ,
   15600 ECB             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   15601 GIC        1087 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
   15602                 :                               true, NULL, 1, &key);
   15603                 : 
   15604                 :     /* inhseqno sequences start at 1 */
   15605            1087 :     inhseqno = 0;
   15606 GBC        1108 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   15607 EUB             :     {
   15608 GIC          24 :         Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   15609                 : 
   15610              24 :         if (inh->inhparent == RelationGetRelid(parent_rel))
   15611               3 :             ereport(ERROR,
   15612 ECB             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   15613                 :                      errmsg("relation \"%s\" would be inherited from more than once",
   15614                 :                             RelationGetRelationName(parent_rel))));
   15615                 : 
   15616 CBC          21 :         if (inh->inhseqno > inhseqno)
   15617 GIC          21 :             inhseqno = inh->inhseqno;
   15618                 :     }
   15619            1084 :     systable_endscan(scan);
   15620 ECB             : 
   15621                 :     /* Match up the columns and bump attinhcount as needed */
   15622 CBC        1084 :     MergeAttributesIntoExisting(child_rel, parent_rel);
   15623 ECB             : 
   15624                 :     /* Match up the constraints and bump coninhcount as needed */
   15625 GIC        1045 :     MergeConstraintsIntoExisting(child_rel, parent_rel);
   15626 ECB             : 
   15627                 :     /*
   15628                 :      * OK, it looks valid.  Make the catalog entries that show inheritance.
   15629                 :      */
   15630 GIC        1030 :     StoreCatalogInheritance1(RelationGetRelid(child_rel),
   15631 ECB             :                              RelationGetRelid(parent_rel),
   15632                 :                              inhseqno + 1,
   15633                 :                              catalogRelation,
   15634 GIC        1030 :                              parent_rel->rd_rel->relkind ==
   15635                 :                              RELKIND_PARTITIONED_TABLE);
   15636 ECB             : 
   15637                 :     /* Now we're done with pg_inherits */
   15638 CBC        1030 :     table_close(catalogRelation, RowExclusiveLock);
   15639 GIC        1030 : }
   15640                 : 
   15641                 : /*
   15642 ECB             :  * Obtain the source-text form of the constraint expression for a check
   15643                 :  * constraint, given its pg_constraint tuple
   15644                 :  */
   15645                 : static char *
   15646 CBC          66 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
   15647                 : {
   15648                 :     Form_pg_constraint con;
   15649 ECB             :     bool        isnull;
   15650                 :     Datum       attr;
   15651                 :     Datum       expr;
   15652                 : 
   15653 GIC          66 :     con = (Form_pg_constraint) GETSTRUCT(contup);
   15654              66 :     attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
   15655              66 :     if (isnull)
   15656 UIC           0 :         elog(ERROR, "null conbin for constraint %u", con->oid);
   15657 ECB             : 
   15658 GIC          66 :     expr = DirectFunctionCall2(pg_get_expr, attr,
   15659                 :                                ObjectIdGetDatum(con->conrelid));
   15660              66 :     return TextDatumGetCString(expr);
   15661                 : }
   15662                 : 
   15663 ECB             : /*
   15664                 :  * Determine whether two check constraints are functionally equivalent
   15665                 :  *
   15666                 :  * The test we apply is to see whether they reverse-compile to the same
   15667                 :  * source string.  This insulates us from issues like whether attributes
   15668                 :  * have the same physical column numbers in parent and child relations.
   15669                 :  */
   15670                 : static bool
   15671 GIC          33 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
   15672 ECB             : {
   15673 GIC          33 :     Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
   15674 CBC          33 :     Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
   15675                 : 
   15676              33 :     if (acon->condeferrable != bcon->condeferrable ||
   15677              33 :         acon->condeferred != bcon->condeferred ||
   15678 GIC          33 :         strcmp(decompile_conbin(a, tupleDesc),
   15679              33 :                decompile_conbin(b, tupleDesc)) != 0)
   15680 CBC           3 :         return false;
   15681                 :     else
   15682              30 :         return true;
   15683                 : }
   15684 ECB             : 
   15685                 : /*
   15686                 :  * Check columns in child table match up with columns in parent, and increment
   15687                 :  * their attinhcount.
   15688                 :  *
   15689                 :  * Called by CreateInheritance
   15690                 :  *
   15691                 :  * Currently all parent columns must be found in child. Missing columns are an
   15692                 :  * error.  One day we might consider creating new columns like CREATE TABLE
   15693                 :  * does.  However, that is widely unpopular --- in the common use case of
   15694                 :  * partitioned tables it's a foot-gun.
   15695                 :  *
   15696                 :  * The data type must match exactly. If the parent column is NOT NULL then
   15697                 :  * the child must be as well. Defaults are not compared, however.
   15698                 :  */
   15699                 : static void
   15700 CBC        1084 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
   15701                 : {
   15702                 :     Relation    attrrel;
   15703 ECB             :     AttrNumber  parent_attno;
   15704                 :     int         parent_natts;
   15705                 :     TupleDesc   tupleDesc;
   15706                 :     HeapTuple   tuple;
   15707 GIC        1084 :     bool        child_is_partition = false;
   15708                 : 
   15709 CBC        1084 :     attrrel = table_open(AttributeRelationId, RowExclusiveLock);
   15710 ECB             : 
   15711 GIC        1084 :     tupleDesc = RelationGetDescr(parent_rel);
   15712            1084 :     parent_natts = tupleDesc->natts;
   15713                 : 
   15714                 :     /* If parent_rel is a partitioned table, child_rel must be a partition */
   15715            1084 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   15716             968 :         child_is_partition = true;
   15717                 : 
   15718            3533 :     for (parent_attno = 1; parent_attno <= parent_natts; parent_attno++)
   15719                 :     {
   15720            2488 :         Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
   15721 ECB             :                                                     parent_attno - 1);
   15722 GIC        2488 :         char       *attributeName = NameStr(attribute->attname);
   15723 ECB             : 
   15724                 :         /* Ignore dropped columns in the parent. */
   15725 CBC        2488 :         if (attribute->attisdropped)
   15726 GIC         148 :             continue;
   15727 ECB             : 
   15728                 :         /* Find same column in child (matching on column name). */
   15729 GIC        2340 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel),
   15730 ECB             :                                           attributeName);
   15731 GIC        2340 :         if (HeapTupleIsValid(tuple))
   15732                 :         {
   15733 ECB             :             /* Check they are same type, typmod, and collation */
   15734 CBC        2334 :             Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
   15735 ECB             : 
   15736 CBC        2334 :             if (attribute->atttypid != childatt->atttypid ||
   15737 GIC        2331 :                 attribute->atttypmod != childatt->atttypmod)
   15738               6 :                 ereport(ERROR,
   15739 ECB             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15740                 :                          errmsg("child table \"%s\" has different type for column \"%s\"",
   15741                 :                                 RelationGetRelationName(child_rel),
   15742                 :                                 attributeName)));
   15743                 : 
   15744 GIC        2328 :             if (attribute->attcollation != childatt->attcollation)
   15745               3 :                 ereport(ERROR,
   15746                 :                         (errcode(ERRCODE_COLLATION_MISMATCH),
   15747                 :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
   15748                 :                                 RelationGetRelationName(child_rel),
   15749                 :                                 attributeName)));
   15750 ECB             : 
   15751                 :             /*
   15752                 :              * Check child doesn't discard NOT NULL property.  (Other
   15753                 :              * constraints are checked elsewhere.)
   15754                 :              */
   15755 GIC        2325 :             if (attribute->attnotnull && !childatt->attnotnull)
   15756 CBC           6 :                 ereport(ERROR,
   15757                 :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15758                 :                          errmsg("column \"%s\" in child table must be marked NOT NULL",
   15759 ECB             :                                 attributeName)));
   15760                 : 
   15761 EUB             :             /*
   15762                 :              * Child column must be generated if and only if parent column is.
   15763                 :              */
   15764 GBC        2319 :             if (attribute->attgenerated && !childatt->attgenerated)
   15765 GIC          12 :                 ereport(ERROR,
   15766                 :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15767                 :                          errmsg("column \"%s\" in child table must be a generated column",
   15768 ECB             :                                 attributeName)));
   15769 GNC        2307 :             if (childatt->attgenerated && !attribute->attgenerated)
   15770               6 :                 ereport(ERROR,
   15771                 :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15772                 :                          errmsg("column \"%s\" in child table must not be a generated column",
   15773                 :                                 attributeName)));
   15774                 : 
   15775                 :             /*
   15776                 :              * OK, bump the child column's inheritance count.  (If we fail
   15777                 :              * later on, this change will just roll back.)
   15778                 :              */
   15779 CBC        2301 :             childatt->attinhcount++;
   15780 GNC        2301 :             if (childatt->attinhcount < 0)
   15781 UNC           0 :                 ereport(ERROR,
   15782                 :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   15783                 :                         errmsg("too many inheritance parents"));
   15784                 : 
   15785                 :             /*
   15786                 :              * In case of partitions, we must enforce that value of attislocal
   15787 EUB             :              * is same in all partitions. (Note: there are only inherited
   15788                 :              * attributes in partitions)
   15789                 :              */
   15790 GBC        2301 :             if (child_is_partition)
   15791 EUB             :             {
   15792 GIC        2067 :                 Assert(childatt->attinhcount == 1);
   15793            2067 :                 childatt->attislocal = false;
   15794                 :             }
   15795                 : 
   15796            2301 :             CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
   15797            2301 :             heap_freetuple(tuple);
   15798 ECB             :         }
   15799 EUB             :         else
   15800                 :         {
   15801 CBC           6 :             ereport(ERROR,
   15802 ECB             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   15803                 :                      errmsg("child table is missing column \"%s\"",
   15804                 :                             attributeName)));
   15805                 :         }
   15806 EUB             :     }
   15807                 : 
   15808 GIC        1045 :     table_close(attrrel, RowExclusiveLock);
   15809            1045 : }
   15810                 : 
   15811                 : /*
   15812 ECB             :  * Check constraints in child table match up with constraints in parent,
   15813                 :  * and increment their coninhcount.
   15814                 :  *
   15815                 :  * Constraints that are marked ONLY in the parent are ignored.
   15816                 :  *
   15817                 :  * Called by CreateInheritance
   15818                 :  *
   15819                 :  * Currently all constraints in parent must be present in the child. One day we
   15820                 :  * may consider adding new constraints like CREATE TABLE does.
   15821                 :  *
   15822                 :  * XXX This is O(N^2) which may be an issue with tables with hundreds of
   15823                 :  * constraints. As long as tables have more like 10 constraints it shouldn't be
   15824                 :  * a problem though. Even 100 constraints ought not be the end of the world.
   15825                 :  *
   15826                 :  * XXX See MergeWithExistingConstraint too if you change this code.
   15827                 :  */
   15828                 : static void
   15829 GIC        1045 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
   15830                 : {
   15831                 :     Relation    catalog_relation;
   15832 ECB             :     TupleDesc   tuple_desc;
   15833                 :     SysScanDesc parent_scan;
   15834                 :     ScanKeyData parent_key;
   15835                 :     HeapTuple   parent_tuple;
   15836 GNC        1045 :     Oid         parent_relid = RelationGetRelid(parent_rel);
   15837 GBC        1045 :     bool        child_is_partition = false;
   15838                 : 
   15839 GIC        1045 :     catalog_relation = table_open(ConstraintRelationId, RowExclusiveLock);
   15840 CBC        1045 :     tuple_desc = RelationGetDescr(catalog_relation);
   15841 ECB             : 
   15842                 :     /* If parent_rel is a partitioned table, child_rel must be a partition */
   15843 CBC        1045 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   15844             944 :         child_is_partition = true;
   15845 ECB             : 
   15846                 :     /* Outer loop scans through the parent's constraint definitions */
   15847 CBC        1045 :     ScanKeyInit(&parent_key,
   15848 ECB             :                 Anum_pg_constraint_conrelid,
   15849                 :                 BTEqualStrategyNumber, F_OIDEQ,
   15850                 :                 ObjectIdGetDatum(parent_relid));
   15851 CBC        1045 :     parent_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId,
   15852 EUB             :                                      true, NULL, 1, &parent_key);
   15853                 : 
   15854 GIC        1549 :     while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
   15855                 :     {
   15856             519 :         Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
   15857                 :         SysScanDesc child_scan;
   15858                 :         ScanKeyData child_key;
   15859                 :         HeapTuple   child_tuple;
   15860             519 :         bool        found = false;
   15861 ECB             : 
   15862 GNC         519 :         if (parent_con->contype != CONSTRAINT_CHECK &&
   15863             464 :             parent_con->contype != CONSTRAINT_NOTNULL)
   15864 GBC         284 :             continue;
   15865                 : 
   15866 ECB             :         /* if the parent's constraint is marked NO INHERIT, it's not inherited */
   15867 GBC         245 :         if (parent_con->connoinherit)
   15868              10 :             continue;
   15869                 : 
   15870                 :         /* Search for a child constraint matching this one */
   15871 GIC         235 :         ScanKeyInit(&child_key,
   15872                 :                     Anum_pg_constraint_conrelid,
   15873                 :                     BTEqualStrategyNumber, F_OIDEQ,
   15874 ECB             :                     ObjectIdGetDatum(RelationGetRelid(child_rel)));
   15875 GIC         235 :         child_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId,
   15876                 :                                         true, NULL, 1, &child_key);
   15877 ECB             : 
   15878 GIC         432 :         while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
   15879                 :         {
   15880 CBC         420 :             Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
   15881 ECB             :             HeapTuple   child_copy;
   15882                 : 
   15883 GNC         420 :             if (child_con->contype != parent_con->contype)
   15884 CBC         105 :                 continue;
   15885                 : 
   15886                 :             /*
   15887                 :              * CHECK constraint are matched by name, NOT NULL ones by
   15888                 :              * attribute number
   15889                 :              */
   15890 GNC         315 :             if (child_con->contype == CONSTRAINT_CHECK &&
   15891              48 :                 strcmp(NameStr(parent_con->conname),
   15892 GIC          48 :                        NameStr(child_con->conname)) != 0)
   15893              15 :                 continue;
   15894 GNC         300 :             else if (child_con->contype == CONSTRAINT_NOTNULL)
   15895                 :             {
   15896             267 :                 AttrNumber  parent_attno = extractNotNullColumn(parent_tuple);
   15897             267 :                 AttrNumber  child_attno = extractNotNullColumn(child_tuple);
   15898                 : 
   15899             267 :                 if (strcmp(get_attname(parent_relid, parent_attno, false),
   15900             267 :                            get_attname(RelationGetRelid(child_rel), child_attno,
   15901                 :                                        false)) != 0)
   15902              77 :                     continue;
   15903                 :             }
   15904                 : 
   15905             223 :             if (child_con->contype == CONSTRAINT_CHECK &&
   15906              33 :                 !constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
   15907 CBC           3 :                 ereport(ERROR,
   15908                 :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15909 ECB             :                          errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
   15910                 :                                 RelationGetRelationName(child_rel),
   15911                 :                                 NameStr(parent_con->conname))));
   15912                 : 
   15913                 :             /* If the child constraint is "no inherit" then cannot merge */
   15914 GIC         220 :             if (child_con->connoinherit)
   15915 LBC           0 :                 ereport(ERROR,
   15916                 :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   15917 ECB             :                          errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
   15918                 :                                 NameStr(child_con->conname),
   15919                 :                                 RelationGetRelationName(child_rel))));
   15920                 : 
   15921                 :             /*
   15922                 :              * If the child constraint is "not valid" then cannot merge with a
   15923                 :              * valid parent constraint
   15924                 :              */
   15925 GIC         220 :             if (parent_con->convalidated && !child_con->convalidated)
   15926 UIC           0 :                 ereport(ERROR,
   15927 ECB             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   15928                 :                          errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
   15929                 :                                 NameStr(child_con->conname),
   15930                 :                                 RelationGetRelationName(child_rel))));
   15931                 : 
   15932                 :             /*
   15933                 :              * OK, bump the child constraint's inheritance count.  (If we fail
   15934                 :              * later on, this change will just roll back.)
   15935                 :              */
   15936 GIC         220 :             child_copy = heap_copytuple(child_tuple);
   15937             220 :             child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
   15938             220 :             child_con->coninhcount++;
   15939 GNC         220 :             if (child_con->coninhcount < 0)
   15940 UNC           0 :                 ereport(ERROR,
   15941                 :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   15942                 :                         errmsg("too many inheritance parents"));
   15943 ECB             : 
   15944                 :             /*
   15945                 :              * In case of partitions, an inherited constraint must be
   15946                 :              * inherited only once since it cannot have multiple parents and
   15947                 :              * it is never considered local.
   15948                 :              */
   15949 GIC         220 :             if (child_is_partition)
   15950                 :             {
   15951             187 :                 Assert(child_con->coninhcount == 1);
   15952 CBC         187 :                 child_con->conislocal = false;
   15953                 :             }
   15954                 : 
   15955             220 :             CatalogTupleUpdate(catalog_relation, &child_copy->t_self, child_copy);
   15956             220 :             heap_freetuple(child_copy);
   15957                 : 
   15958 GIC         220 :             found = true;
   15959 CBC         220 :             break;
   15960 ECB             :         }
   15961                 : 
   15962 CBC         232 :         systable_endscan(child_scan);
   15963                 : 
   15964 GBC         232 :         if (!found)
   15965 GIC          12 :             ereport(ERROR,
   15966                 :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   15967                 :                      errmsg("child table is missing constraint \"%s\"",
   15968                 :                             NameStr(parent_con->conname))));
   15969                 :     }
   15970 EUB             : 
   15971 GBC        1030 :     systable_endscan(parent_scan);
   15972 GIC        1030 :     table_close(catalog_relation, RowExclusiveLock);
   15973 GBC        1030 : }
   15974 EUB             : 
   15975                 : /*
   15976                 :  * ALTER TABLE NO INHERIT
   15977                 :  *
   15978                 :  * Return value is the address of the relation that is no longer parent.
   15979                 :  */
   15980 ECB             : static ObjectAddress
   15981 CBC          22 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
   15982 ECB             : {
   15983                 :     ObjectAddress address;
   15984                 :     Relation    parent_rel;
   15985                 : 
   15986 GIC          22 :     if (rel->rd_rel->relispartition)
   15987 UIC           0 :         ereport(ERROR,
   15988                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15989                 :                  errmsg("cannot change inheritance of a partition")));
   15990 ECB             : 
   15991                 :     /*
   15992                 :      * AccessShareLock on the parent is probably enough, seeing that DROP
   15993                 :      * TABLE doesn't lock parent tables at all.  We need some lock since we'll
   15994                 :      * be inspecting the parent's schema.
   15995                 :      */
   15996 GIC          22 :     parent_rel = table_openrv(parent, AccessShareLock);
   15997 ECB             : 
   15998                 :     /*
   15999                 :      * We don't bother to check ownership of the parent table --- ownership of
   16000                 :      * the child is presumed enough rights.
   16001                 :      */
   16002                 : 
   16003                 :     /* Off to RemoveInheritance() where most of the work happens */
   16004 GIC          22 :     RemoveInheritance(rel, parent_rel, false);
   16005 ECB             : 
   16006 GIC          19 :     ObjectAddressSet(address, RelationRelationId,
   16007                 :                      RelationGetRelid(parent_rel));
   16008 ECB             : 
   16009                 :     /* keep our lock on the parent relation until commit */
   16010 GIC          19 :     table_close(parent_rel, NoLock);
   16011                 : 
   16012              19 :     return address;
   16013                 : }
   16014                 : 
   16015                 : /*
   16016                 :  * MarkInheritDetached
   16017                 :  *
   16018                 :  * Set inhdetachpending for a partition, for ATExecDetachPartition
   16019 ECB             :  * in concurrent mode.  While at it, verify that no other partition is
   16020                 :  * already pending detach.
   16021                 :  */
   16022                 : static void
   16023 GIC          73 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
   16024                 : {
   16025                 :     Relation    catalogRelation;
   16026 ECB             :     SysScanDesc scan;
   16027                 :     ScanKeyData key;
   16028                 :     HeapTuple   inheritsTuple;
   16029 GIC          73 :     bool        found = false;
   16030                 : 
   16031 CBC          73 :     Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   16032 ECB             : 
   16033                 :     /*
   16034                 :      * Find pg_inherits entries by inhparent.  (We need to scan them all in
   16035                 :      * order to verify that no other partition is pending detach.)
   16036                 :      */
   16037 GIC          73 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   16038              73 :     ScanKeyInit(&key,
   16039                 :                 Anum_pg_inherits_inhparent,
   16040                 :                 BTEqualStrategyNumber, F_OIDEQ,
   16041 ECB             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   16042 GIC          73 :     scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
   16043                 :                               true, NULL, 1, &key);
   16044                 : 
   16045             288 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   16046                 :     {
   16047                 :         Form_pg_inherits inhForm;
   16048                 : 
   16049             143 :         inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   16050             143 :         if (inhForm->inhdetachpending)
   16051               1 :             ereport(ERROR,
   16052 ECB             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   16053                 :                     errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
   16054                 :                            get_rel_name(inhForm->inhrelid),
   16055                 :                            get_namespace_name(parent_rel->rd_rel->relnamespace),
   16056                 :                            RelationGetRelationName(parent_rel)),
   16057                 :                     errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
   16058                 : 
   16059 GIC         142 :         if (inhForm->inhrelid == RelationGetRelid(child_rel))
   16060                 :         {
   16061 ECB             :             HeapTuple   newtup;
   16062                 : 
   16063 GBC          72 :             newtup = heap_copytuple(inheritsTuple);
   16064 GIC          72 :             ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
   16065                 : 
   16066              72 :             CatalogTupleUpdate(catalogRelation,
   16067                 :                                &inheritsTuple->t_self,
   16068                 :                                newtup);
   16069 CBC          72 :             found = true;
   16070              72 :             heap_freetuple(newtup);
   16071 EUB             :             /* keep looking, to ensure we catch others pending detach */
   16072                 :         }
   16073                 :     }
   16074                 : 
   16075                 :     /* Done */
   16076 CBC          72 :     systable_endscan(scan);
   16077              72 :     table_close(catalogRelation, RowExclusiveLock);
   16078 EUB             : 
   16079 GIC          72 :     if (!found)
   16080 UIC           0 :         ereport(ERROR,
   16081                 :                 (errcode(ERRCODE_UNDEFINED_TABLE),
   16082                 :                  errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   16083 ECB             :                         RelationGetRelationName(child_rel),
   16084                 :                         RelationGetRelationName(parent_rel))));
   16085 GIC          72 : }
   16086                 : 
   16087                 : /*
   16088                 :  * RemoveInheritance
   16089                 :  *
   16090 ECB             :  * Drop a parent from the child's parents. This just adjusts the attinhcount
   16091                 :  * and attislocal of the columns and removes the pg_inherit and pg_depend
   16092                 :  * entries.  expect_detached is passed down to DeleteInheritsTuple, q.v..
   16093                 :  *
   16094                 :  * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
   16095                 :  * up attislocal stays true, which means if a child is ever removed from a
   16096                 :  * parent then its columns will never be automatically dropped which may
   16097                 :  * surprise. But at least we'll never surprise by dropping columns someone
   16098                 :  * isn't expecting to be dropped which would actually mean data loss.
   16099                 :  *
   16100                 :  * coninhcount and conislocal for inherited constraints are adjusted in
   16101                 :  * exactly the same way.
   16102                 :  *
   16103                 :  * Common to ATExecDropInherit() and ATExecDetachPartition().
   16104                 :  */
   16105                 : static void
   16106 GIC         241 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
   16107                 : {
   16108                 :     Relation    catalogRelation;
   16109 ECB             :     SysScanDesc scan;
   16110                 :     ScanKeyData key[3];
   16111                 :     HeapTuple   attributeTuple,
   16112                 :                 constraintTuple;
   16113                 :     List       *connames;
   16114                 :     List       *nncolumns;
   16115                 :     bool        found;
   16116 GIC         241 :     bool        child_is_partition = false;
   16117                 : 
   16118                 :     /* If parent_rel is a partitioned table, child_rel must be a partition */
   16119             241 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   16120             219 :         child_is_partition = true;
   16121                 : 
   16122             241 :     found = DeleteInheritsTuple(RelationGetRelid(child_rel),
   16123                 :                                 RelationGetRelid(parent_rel),
   16124                 :                                 expect_detached,
   16125             241 :                                 RelationGetRelationName(child_rel));
   16126 CBC         241 :     if (!found)
   16127 ECB             :     {
   16128 CBC          12 :         if (child_is_partition)
   16129 GIC           9 :             ereport(ERROR,
   16130                 :                     (errcode(ERRCODE_UNDEFINED_TABLE),
   16131                 :                      errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   16132                 :                             RelationGetRelationName(child_rel),
   16133                 :                             RelationGetRelationName(parent_rel))));
   16134                 :         else
   16135 CBC           3 :             ereport(ERROR,
   16136                 :                     (errcode(ERRCODE_UNDEFINED_TABLE),
   16137 ECB             :                      errmsg("relation \"%s\" is not a parent of relation \"%s\"",
   16138                 :                             RelationGetRelationName(parent_rel),
   16139                 :                             RelationGetRelationName(child_rel))));
   16140                 :     }
   16141                 : 
   16142                 :     /*
   16143                 :      * Search through child columns looking for ones matching parent rel
   16144                 :      */
   16145 GIC         229 :     catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
   16146             229 :     ScanKeyInit(&key[0],
   16147                 :                 Anum_pg_attribute_attrelid,
   16148                 :                 BTEqualStrategyNumber, F_OIDEQ,
   16149                 :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   16150             229 :     scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
   16151                 :                               true, NULL, 1, key);
   16152            2011 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   16153                 :     {
   16154 CBC        1782 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   16155                 : 
   16156                 :         /* Ignore if dropped or not inherited */
   16157 GIC        1782 :         if (att->attisdropped)
   16158 UIC           0 :             continue;
   16159 GIC        1782 :         if (att->attinhcount <= 0)
   16160            1383 :             continue;
   16161                 : 
   16162             399 :         if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
   16163 CBC         399 :                                         NameStr(att->attname)))
   16164                 :         {
   16165                 :             /* Decrement inhcount and possibly set islocal to true */
   16166 GIC         393 :             HeapTuple   copyTuple = heap_copytuple(attributeTuple);
   16167             393 :             Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
   16168                 : 
   16169             393 :             copy_att->attinhcount--;
   16170             393 :             if (copy_att->attinhcount == 0)
   16171             393 :                 copy_att->attislocal = true;
   16172                 : 
   16173             393 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   16174 CBC         393 :             heap_freetuple(copyTuple);
   16175                 :         }
   16176                 :     }
   16177 GIC         229 :     systable_endscan(scan);
   16178 CBC         229 :     table_close(catalogRelation, RowExclusiveLock);
   16179                 : 
   16180                 :     /*
   16181                 :      * Likewise, find inherited check constraints and disinherit them. To do
   16182 ECB             :      * this, we first need a list of the names of the parent's check
   16183                 :      * constraints.  (We cheat a bit by only checking for name matches,
   16184                 :      * assuming that the expressions will match.)
   16185                 :      *
   16186                 :      * For NOT NULL columns, we store column numbers to match.
   16187                 :      */
   16188 GIC         229 :     catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
   16189 CBC         229 :     ScanKeyInit(&key[0],
   16190 ECB             :                 Anum_pg_constraint_conrelid,
   16191                 :                 BTEqualStrategyNumber, F_OIDEQ,
   16192                 :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   16193 GIC         229 :     scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   16194                 :                               true, NULL, 1, key);
   16195 ECB             : 
   16196 CBC         229 :     connames = NIL;
   16197 GNC         229 :     nncolumns = NIL;
   16198                 : 
   16199 CBC         330 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   16200                 :     {
   16201 GIC         101 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   16202 ECB             : 
   16203 GIC         101 :         if (con->contype == CONSTRAINT_CHECK)
   16204               6 :             connames = lappend(connames, pstrdup(NameStr(con->conname)));
   16205 GNC         101 :         if (con->contype == CONSTRAINT_NOTNULL)
   16206              19 :             nncolumns = lappend_int(nncolumns, extractNotNullColumn(constraintTuple));
   16207 ECB             :     }
   16208                 : 
   16209 GIC         229 :     systable_endscan(scan);
   16210                 : 
   16211                 :     /* Now scan the child's constraints */
   16212 CBC         229 :     ScanKeyInit(&key[0],
   16213                 :                 Anum_pg_constraint_conrelid,
   16214                 :                 BTEqualStrategyNumber, F_OIDEQ,
   16215                 :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   16216             229 :     scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   16217                 :                               true, NULL, 1, key);
   16218                 : 
   16219 GIC         428 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   16220 ECB             :     {
   16221 CBC         199 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   16222 GNC         199 :         bool        match = false;
   16223                 :         ListCell   *lc;
   16224                 : 
   16225                 :         /*
   16226                 :          * Match CHECK constraints by name, NOT NULL constraints by column
   16227                 :          * number, and ignore all others.
   16228                 :          */
   16229             199 :         if (con->contype == CONSTRAINT_CHECK)
   16230                 :         {
   16231              92 :             foreach(lc, connames)
   16232                 :             {
   16233              12 :                 if (con->contype == CONSTRAINT_CHECK &&
   16234              12 :                     strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
   16235                 :                 {
   16236               6 :                     match = true;
   16237               6 :                     break;
   16238                 :                 }
   16239 ECB             :             }
   16240                 :         }
   16241 GNC         113 :         else if (con->contype == CONSTRAINT_NOTNULL)
   16242                 :         {
   16243              28 :             AttrNumber  child_attno = extractNotNullColumn(constraintTuple);
   16244                 : 
   16245              40 :             foreach(lc, nncolumns)
   16246                 :             {
   16247              31 :                 if (lfirst_int(lc) == child_attno)
   16248                 :                 {
   16249              19 :                     match = true;
   16250              19 :                     break;
   16251                 :                 }
   16252                 :             }
   16253                 :         }
   16254                 :         else
   16255              85 :             continue;
   16256 ECB             : 
   16257 GBC         114 :         if (match)
   16258                 :         {
   16259 ECB             :             /* Decrement inhcount and possibly set islocal to true */
   16260 GIC          25 :             HeapTuple   copyTuple = heap_copytuple(constraintTuple);
   16261 CBC          25 :             Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   16262                 : 
   16263 GIC          25 :             if (copy_con->coninhcount <= 0) /* shouldn't happen */
   16264 UIC           0 :                 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   16265                 :                      RelationGetRelid(child_rel), NameStr(copy_con->conname));
   16266                 : 
   16267 GIC          25 :             copy_con->coninhcount--;
   16268              25 :             if (copy_con->coninhcount == 0)
   16269              25 :                 copy_con->conislocal = true;
   16270                 : 
   16271              25 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   16272 CBC          25 :             heap_freetuple(copyTuple);
   16273                 :         }
   16274 ECB             :     }
   16275                 : 
   16276 GIC         229 :     systable_endscan(scan);
   16277 CBC         229 :     table_close(catalogRelation, RowExclusiveLock);
   16278 ECB             : 
   16279 CBC         229 :     drop_parent_dependency(RelationGetRelid(child_rel),
   16280 ECB             :                            RelationRelationId,
   16281                 :                            RelationGetRelid(parent_rel),
   16282                 :                            child_dependency_type(child_is_partition));
   16283                 : 
   16284                 :     /*
   16285                 :      * Post alter hook of this inherits. Since object_access_hook doesn't take
   16286                 :      * multiple object identifiers, we relay oid of parent relation using
   16287                 :      * auxiliary_id argument.
   16288                 :      */
   16289 GIC         229 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
   16290                 :                                  RelationGetRelid(child_rel), 0,
   16291                 :                                  RelationGetRelid(parent_rel), false);
   16292             229 : }
   16293                 : 
   16294                 : /*
   16295                 :  * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
   16296                 :  * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
   16297                 :  * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
   16298                 :  * be TypeRelationId).  There's no convenient way to do this, so go trawling
   16299                 :  * through pg_depend.
   16300                 :  */
   16301 ECB             : static void
   16302 GIC         235 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
   16303                 :                        DependencyType deptype)
   16304                 : {
   16305                 :     Relation    catalogRelation;
   16306                 :     SysScanDesc scan;
   16307                 :     ScanKeyData key[3];
   16308 ECB             :     HeapTuple   depTuple;
   16309                 : 
   16310 CBC         235 :     catalogRelation = table_open(DependRelationId, RowExclusiveLock);
   16311                 : 
   16312             235 :     ScanKeyInit(&key[0],
   16313 ECB             :                 Anum_pg_depend_classid,
   16314                 :                 BTEqualStrategyNumber, F_OIDEQ,
   16315                 :                 ObjectIdGetDatum(RelationRelationId));
   16316 CBC         235 :     ScanKeyInit(&key[1],
   16317 ECB             :                 Anum_pg_depend_objid,
   16318                 :                 BTEqualStrategyNumber, F_OIDEQ,
   16319                 :                 ObjectIdGetDatum(relid));
   16320 GIC         235 :     ScanKeyInit(&key[2],
   16321 ECB             :                 Anum_pg_depend_objsubid,
   16322                 :                 BTEqualStrategyNumber, F_INT4EQ,
   16323                 :                 Int32GetDatum(0));
   16324                 : 
   16325 GIC         235 :     scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
   16326 ECB             :                               NULL, 3, key);
   16327                 : 
   16328 GIC         720 :     while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
   16329                 :     {
   16330 CBC         485 :         Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
   16331                 : 
   16332             485 :         if (dep->refclassid == refclassid &&
   16333 GIC         241 :             dep->refobjid == refobjid &&
   16334             235 :             dep->refobjsubid == 0 &&
   16335 CBC         235 :             dep->deptype == deptype)
   16336 GIC         235 :             CatalogTupleDelete(catalogRelation, &depTuple->t_self);
   16337 ECB             :     }
   16338                 : 
   16339 CBC         235 :     systable_endscan(scan);
   16340 GIC         235 :     table_close(catalogRelation, RowExclusiveLock);
   16341             235 : }
   16342                 : 
   16343                 : /*
   16344                 :  * ALTER TABLE OF
   16345 ECB             :  *
   16346                 :  * Attach a table to a composite type, as though it had been created with CREATE
   16347                 :  * TABLE OF.  All attname, atttypid, atttypmod and attcollation must match.  The
   16348                 :  * subject table must not have inheritance parents.  These restrictions ensure
   16349                 :  * that you cannot create a configuration impossible with CREATE TABLE OF alone.
   16350                 :  *
   16351                 :  * The address of the type is returned.
   16352                 :  */
   16353                 : static ObjectAddress
   16354 GIC          33 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
   16355                 : {
   16356 CBC          33 :     Oid         relid = RelationGetRelid(rel);
   16357 ECB             :     Type        typetuple;
   16358                 :     Form_pg_type typeform;
   16359                 :     Oid         typeid;
   16360                 :     Relation    inheritsRelation,
   16361                 :                 relationRelation;
   16362                 :     SysScanDesc scan;
   16363                 :     ScanKeyData key;
   16364                 :     AttrNumber  table_attno,
   16365                 :                 type_attno;
   16366                 :     TupleDesc   typeTupleDesc,
   16367                 :                 tableTupleDesc;
   16368                 :     ObjectAddress tableobj,
   16369                 :                 typeobj;
   16370                 :     HeapTuple   classtuple;
   16371                 : 
   16372                 :     /* Validate the type. */
   16373 GIC          33 :     typetuple = typenameType(NULL, ofTypename, NULL);
   16374              33 :     check_of_type(typetuple);
   16375              33 :     typeform = (Form_pg_type) GETSTRUCT(typetuple);
   16376              33 :     typeid = typeform->oid;
   16377                 : 
   16378                 :     /* Fail if the table has any inheritance parents. */
   16379              33 :     inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
   16380 CBC          33 :     ScanKeyInit(&key,
   16381 ECB             :                 Anum_pg_inherits_inhrelid,
   16382 EUB             :                 BTEqualStrategyNumber, F_OIDEQ,
   16383                 :                 ObjectIdGetDatum(relid));
   16384 GIC          33 :     scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
   16385                 :                               true, NULL, 1, &key);
   16386              33 :     if (HeapTupleIsValid(systable_getnext(scan)))
   16387               3 :         ereport(ERROR,
   16388                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16389                 :                  errmsg("typed tables cannot inherit")));
   16390              30 :     systable_endscan(scan);
   16391 CBC          30 :     table_close(inheritsRelation, AccessShareLock);
   16392                 : 
   16393 ECB             :     /*
   16394                 :      * Check the tuple descriptors for compatibility.  Unlike inheritance, we
   16395                 :      * require that the order also match.  However, attnotnull need not match.
   16396                 :      */
   16397 CBC          30 :     typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
   16398              30 :     tableTupleDesc = RelationGetDescr(rel);
   16399 GIC          30 :     table_attno = 1;
   16400              95 :     for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
   16401                 :     {
   16402 ECB             :         Form_pg_attribute type_attr,
   16403                 :                     table_attr;
   16404                 :         const char *type_attname,
   16405                 :                    *table_attname;
   16406                 : 
   16407                 :         /* Get the next non-dropped type attribute. */
   16408 GIC          77 :         type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
   16409 CBC          77 :         if (type_attr->attisdropped)
   16410              22 :             continue;
   16411 GIC          55 :         type_attname = NameStr(type_attr->attname);
   16412                 : 
   16413                 :         /* Get the next non-dropped table attribute. */
   16414                 :         do
   16415                 :         {
   16416              61 :             if (table_attno > tableTupleDesc->natts)
   16417               3 :                 ereport(ERROR,
   16418                 :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   16419                 :                          errmsg("table is missing column \"%s\"",
   16420                 :                                 type_attname)));
   16421              58 :             table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
   16422              58 :             table_attno++;
   16423              58 :         } while (table_attr->attisdropped);
   16424              52 :         table_attname = NameStr(table_attr->attname);
   16425                 : 
   16426                 :         /* Compare name. */
   16427              52 :         if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
   16428               3 :             ereport(ERROR,
   16429                 :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   16430 ECB             :                      errmsg("table has column \"%s\" where type requires \"%s\"",
   16431                 :                             table_attname, type_attname)));
   16432                 : 
   16433                 :         /* Compare type. */
   16434 GIC          49 :         if (table_attr->atttypid != type_attr->atttypid ||
   16435              46 :             table_attr->atttypmod != type_attr->atttypmod ||
   16436              43 :             table_attr->attcollation != type_attr->attcollation)
   16437 CBC           6 :             ereport(ERROR,
   16438 ECB             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   16439                 :                      errmsg("table \"%s\" has different type for column \"%s\"",
   16440                 :                             RelationGetRelationName(rel), type_attname)));
   16441                 :     }
   16442 GIC          18 :     ReleaseTupleDesc(typeTupleDesc);
   16443                 : 
   16444 ECB             :     /* Any remaining columns at the end of the table had better be dropped. */
   16445 CBC          18 :     for (; table_attno <= tableTupleDesc->natts; table_attno++)
   16446                 :     {
   16447 GIC           3 :         Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
   16448 ECB             :                                                      table_attno - 1);
   16449                 : 
   16450 GIC           3 :         if (!table_attr->attisdropped)
   16451               3 :             ereport(ERROR,
   16452 ECB             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   16453                 :                      errmsg("table has extra column \"%s\"",
   16454                 :                             NameStr(table_attr->attname))));
   16455                 :     }
   16456                 : 
   16457                 :     /* If the table was already typed, drop the existing dependency. */
   16458 GIC          15 :     if (rel->rd_rel->reloftype)
   16459               3 :         drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   16460                 :                                DEPENDENCY_NORMAL);
   16461 ECB             : 
   16462                 :     /* Record a dependency on the new type. */
   16463 CBC          15 :     tableobj.classId = RelationRelationId;
   16464              15 :     tableobj.objectId = relid;
   16465              15 :     tableobj.objectSubId = 0;
   16466 GIC          15 :     typeobj.classId = TypeRelationId;
   16467              15 :     typeobj.objectId = typeid;
   16468 CBC          15 :     typeobj.objectSubId = 0;
   16469              15 :     recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
   16470                 : 
   16471                 :     /* Update pg_class.reloftype */
   16472              15 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   16473 GIC          15 :     classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   16474              15 :     if (!HeapTupleIsValid(classtuple))
   16475 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   16476 CBC          15 :     ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
   16477 GIC          15 :     CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
   16478                 : 
   16479 CBC          15 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   16480                 : 
   16481              15 :     heap_freetuple(classtuple);
   16482 GIC          15 :     table_close(relationRelation, RowExclusiveLock);
   16483                 : 
   16484 CBC          15 :     ReleaseSysCache(typetuple);
   16485 ECB             : 
   16486 GIC          15 :     return typeobj;
   16487                 : }
   16488                 : 
   16489                 : /*
   16490                 :  * ALTER TABLE NOT OF
   16491 ECB             :  *
   16492                 :  * Detach a typed table from its originating type.  Just clear reloftype and
   16493                 :  * remove the dependency.
   16494                 :  */
   16495                 : static void
   16496 GIC           3 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
   16497 ECB             : {
   16498 CBC           3 :     Oid         relid = RelationGetRelid(rel);
   16499                 :     Relation    relationRelation;
   16500 ECB             :     HeapTuple   tuple;
   16501                 : 
   16502 GIC           3 :     if (!OidIsValid(rel->rd_rel->reloftype))
   16503 LBC           0 :         ereport(ERROR,
   16504                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16505                 :                  errmsg("\"%s\" is not a typed table",
   16506 ECB             :                         RelationGetRelationName(rel))));
   16507                 : 
   16508                 :     /*
   16509                 :      * We don't bother to check ownership of the type --- ownership of the
   16510                 :      * table is presumed enough rights.  No lock required on the type, either.
   16511                 :      */
   16512                 : 
   16513 GIC           3 :     drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   16514                 :                            DEPENDENCY_NORMAL);
   16515 ECB             : 
   16516 EUB             :     /* Clear pg_class.reloftype */
   16517 GIC           3 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   16518               3 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   16519               3 :     if (!HeapTupleIsValid(tuple))
   16520 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   16521 GIC           3 :     ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
   16522               3 :     CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
   16523                 : 
   16524               3 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   16525                 : 
   16526 CBC           3 :     heap_freetuple(tuple);
   16527 GBC           3 :     table_close(relationRelation, RowExclusiveLock);
   16528 GIC           3 : }
   16529                 : 
   16530                 : /*
   16531                 :  * relation_mark_replica_identity: Update a table's replica identity
   16532                 :  *
   16533                 :  * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
   16534                 :  * index. Otherwise, it must be InvalidOid.
   16535                 :  *
   16536                 :  * Caller had better hold an exclusive lock on the relation, as the results
   16537 ECB             :  * of running two of these concurrently wouldn't be pretty.
   16538                 :  */
   16539                 : static void
   16540 CBC         186 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
   16541 EUB             :                                bool is_internal)
   16542                 : {
   16543                 :     Relation    pg_index;
   16544                 :     Relation    pg_class;
   16545                 :     HeapTuple   pg_class_tuple;
   16546                 :     HeapTuple   pg_index_tuple;
   16547                 :     Form_pg_class pg_class_form;
   16548                 :     Form_pg_index pg_index_form;
   16549                 :     ListCell   *index;
   16550 ECB             : 
   16551                 :     /*
   16552                 :      * Check whether relreplident has changed, and update it if so.
   16553                 :      */
   16554 GIC         186 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   16555             186 :     pg_class_tuple = SearchSysCacheCopy1(RELOID,
   16556 ECB             :                                          ObjectIdGetDatum(RelationGetRelid(rel)));
   16557 CBC         186 :     if (!HeapTupleIsValid(pg_class_tuple))
   16558 UIC           0 :         elog(ERROR, "cache lookup failed for relation \"%s\"",
   16559 ECB             :              RelationGetRelationName(rel));
   16560 CBC         186 :     pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
   16561 GIC         186 :     if (pg_class_form->relreplident != ri_type)
   16562                 :     {
   16563 CBC         164 :         pg_class_form->relreplident = ri_type;
   16564 GIC         164 :         CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
   16565 ECB             :     }
   16566 CBC         186 :     table_close(pg_class, RowExclusiveLock);
   16567 GIC         186 :     heap_freetuple(pg_class_tuple);
   16568                 : 
   16569                 :     /*
   16570                 :      * Update the per-index indisreplident flags correctly.
   16571                 :      */
   16572 CBC         186 :     pg_index = table_open(IndexRelationId, RowExclusiveLock);
   16573             513 :     foreach(index, RelationGetIndexList(rel))
   16574 ECB             :     {
   16575 GIC         327 :         Oid         thisIndexOid = lfirst_oid(index);
   16576             327 :         bool        dirty = false;
   16577                 : 
   16578             327 :         pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
   16579                 :                                              ObjectIdGetDatum(thisIndexOid));
   16580             327 :         if (!HeapTupleIsValid(pg_index_tuple))
   16581 UIC           0 :             elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
   16582 CBC         327 :         pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
   16583                 : 
   16584 GIC         327 :         if (thisIndexOid == indexOid)
   16585                 :         {
   16586                 :             /* Set the bit if not already set. */
   16587 CBC         104 :             if (!pg_index_form->indisreplident)
   16588 EUB             :             {
   16589 GIC          98 :                 dirty = true;
   16590              98 :                 pg_index_form->indisreplident = true;
   16591                 :             }
   16592                 :         }
   16593                 :         else
   16594                 :         {
   16595                 :             /* Unset the bit if set. */
   16596             223 :             if (pg_index_form->indisreplident)
   16597 ECB             :             {
   16598 GIC          20 :                 dirty = true;
   16599              20 :                 pg_index_form->indisreplident = false;
   16600                 :             }
   16601                 :         }
   16602                 : 
   16603             327 :         if (dirty)
   16604                 :         {
   16605 CBC         118 :             CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
   16606 GIC         118 :             InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
   16607 ECB             :                                          InvalidOid, is_internal);
   16608                 : 
   16609                 :             /*
   16610                 :              * Invalidate the relcache for the table, so that after we commit
   16611                 :              * all sessions will refresh the table's replica identity index
   16612                 :              * before attempting any UPDATE or DELETE on the table.  (If we
   16613                 :              * changed the table's pg_class row above, then a relcache inval
   16614                 :              * is already queued due to that; but we might not have.)
   16615                 :              */
   16616 GIC         118 :             CacheInvalidateRelcache(rel);
   16617                 :         }
   16618             327 :         heap_freetuple(pg_index_tuple);
   16619                 :     }
   16620                 : 
   16621             186 :     table_close(pg_index, RowExclusiveLock);
   16622             186 : }
   16623                 : 
   16624 ECB             : /*
   16625                 :  * ALTER TABLE <name> REPLICA IDENTITY ...
   16626                 :  */
   16627                 : static void
   16628 GIC         207 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
   16629                 : {
   16630 ECB             :     Oid         indexOid;
   16631                 :     Relation    indexRel;
   16632                 :     int         key;
   16633                 : 
   16634 GIC         207 :     if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
   16635                 :     {
   16636               3 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   16637               3 :         return;
   16638 ECB             :     }
   16639 CBC         204 :     else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
   16640                 :     {
   16641 GIC          61 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   16642              61 :         return;
   16643 ECB             :     }
   16644 GIC         143 :     else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
   16645                 :     {
   16646 CBC          18 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   16647 GIC          18 :         return;
   16648                 :     }
   16649             125 :     else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
   16650 ECB             :     {
   16651                 :          /* fallthrough */ ;
   16652                 :     }
   16653                 :     else
   16654 UIC           0 :         elog(ERROR, "unexpected identity type %u", stmt->identity_type);
   16655                 : 
   16656                 :     /* Check that the index exists */
   16657 GIC         125 :     indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
   16658             125 :     if (!OidIsValid(indexOid))
   16659 UIC           0 :         ereport(ERROR,
   16660 ECB             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   16661                 :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   16662                 :                         stmt->name, RelationGetRelationName(rel))));
   16663                 : 
   16664 CBC         125 :     indexRel = index_open(indexOid, ShareLock);
   16665 ECB             : 
   16666                 :     /* Check that the index is on the relation we're altering. */
   16667 CBC         125 :     if (indexRel->rd_index == NULL ||
   16668 GIC         125 :         indexRel->rd_index->indrelid != RelationGetRelid(rel))
   16669               3 :         ereport(ERROR,
   16670 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16671                 :                  errmsg("\"%s\" is not an index for table \"%s\"",
   16672                 :                         RelationGetRelationName(indexRel),
   16673                 :                         RelationGetRelationName(rel))));
   16674                 :     /* The AM must support uniqueness, and the index must in fact be unique. */
   16675 GIC         122 :     if (!indexRel->rd_indam->amcanunique ||
   16676             119 :         !indexRel->rd_index->indisunique)
   16677 CBC           6 :         ereport(ERROR,
   16678 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16679                 :                  errmsg("cannot use non-unique index \"%s\" as replica identity",
   16680                 :                         RelationGetRelationName(indexRel))));
   16681 EUB             :     /* Deferred indexes are not guaranteed to be always unique. */
   16682 GIC         116 :     if (!indexRel->rd_index->indimmediate)
   16683               3 :         ereport(ERROR,
   16684                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16685                 :                  errmsg("cannot use non-immediate index \"%s\" as replica identity",
   16686 ECB             :                         RelationGetRelationName(indexRel))));
   16687                 :     /* Expression indexes aren't supported. */
   16688 GIC         113 :     if (RelationGetIndexExpressions(indexRel) != NIL)
   16689               3 :         ereport(ERROR,
   16690                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16691                 :                  errmsg("cannot use expression index \"%s\" as replica identity",
   16692                 :                         RelationGetRelationName(indexRel))));
   16693                 :     /* Predicate indexes aren't supported. */
   16694             110 :     if (RelationGetIndexPredicate(indexRel) != NIL)
   16695               3 :         ereport(ERROR,
   16696                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16697                 :                  errmsg("cannot use partial index \"%s\" as replica identity",
   16698                 :                         RelationGetRelationName(indexRel))));
   16699                 : 
   16700                 :     /* Check index for nullable columns. */
   16701             237 :     for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
   16702                 :     {
   16703             133 :         int16       attno = indexRel->rd_index->indkey.values[key];
   16704                 :         Form_pg_attribute attr;
   16705                 : 
   16706                 :         /*
   16707 ECB             :          * Reject any other system columns.  (Going forward, we'll disallow
   16708                 :          * indexes containing such columns in the first place, but they might
   16709                 :          * exist in older branches.)
   16710                 :          */
   16711 GIC         133 :         if (attno <= 0)
   16712 UIC           0 :             ereport(ERROR,
   16713                 :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   16714                 :                      errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
   16715                 :                             RelationGetRelationName(indexRel), attno)));
   16716                 : 
   16717 CBC         133 :         attr = TupleDescAttr(rel->rd_att, attno - 1);
   16718 GIC         133 :         if (!attr->attnotnull)
   16719               3 :             ereport(ERROR,
   16720 ECB             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16721                 :                      errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
   16722                 :                             RelationGetRelationName(indexRel),
   16723                 :                             NameStr(attr->attname))));
   16724                 :     }
   16725                 : 
   16726                 :     /* This index is suitable for use as a replica identity. Mark it. */
   16727 CBC         104 :     relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
   16728                 : 
   16729             104 :     index_close(indexRel, NoLock);
   16730 ECB             : }
   16731                 : 
   16732                 : /*
   16733                 :  * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
   16734                 :  */
   16735                 : static void
   16736 CBC         139 : ATExecSetRowSecurity(Relation rel, bool rls)
   16737                 : {
   16738                 :     Relation    pg_class;
   16739                 :     Oid         relid;
   16740                 :     HeapTuple   tuple;
   16741                 : 
   16742 GIC         139 :     relid = RelationGetRelid(rel);
   16743                 : 
   16744                 :     /* Pull the record for this relation and update it */
   16745             139 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   16746 ECB             : 
   16747 CBC         139 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   16748                 : 
   16749 GIC         139 :     if (!HeapTupleIsValid(tuple))
   16750 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   16751 ECB             : 
   16752 GIC         139 :     ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
   16753 CBC         139 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   16754                 : 
   16755             139 :     table_close(pg_class, RowExclusiveLock);
   16756 GIC         139 :     heap_freetuple(tuple);
   16757             139 : }
   16758 ECB             : 
   16759 EUB             : /*
   16760 ECB             :  * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
   16761                 :  */
   16762                 : static void
   16763 CBC          55 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
   16764 ECB             : {
   16765                 :     Relation    pg_class;
   16766                 :     Oid         relid;
   16767                 :     HeapTuple   tuple;
   16768                 : 
   16769 GIC          55 :     relid = RelationGetRelid(rel);
   16770 ECB             : 
   16771 CBC          55 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   16772 ECB             : 
   16773 GIC          55 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   16774 ECB             : 
   16775 CBC          55 :     if (!HeapTupleIsValid(tuple))
   16776 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   16777                 : 
   16778 CBC          55 :     ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
   16779              55 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   16780                 : 
   16781 GIC          55 :     table_close(pg_class, RowExclusiveLock);
   16782              55 :     heap_freetuple(tuple);
   16783              55 : }
   16784                 : 
   16785                 : /*
   16786                 :  * ALTER FOREIGN TABLE <name> OPTIONS (...)
   16787                 :  */
   16788                 : static void
   16789 CBC          23 : ATExecGenericOptions(Relation rel, List *options)
   16790 ECB             : {
   16791                 :     Relation    ftrel;
   16792                 :     ForeignServer *server;
   16793                 :     ForeignDataWrapper *fdw;
   16794                 :     HeapTuple   tuple;
   16795                 :     bool        isnull;
   16796                 :     Datum       repl_val[Natts_pg_foreign_table];
   16797                 :     bool        repl_null[Natts_pg_foreign_table];
   16798                 :     bool        repl_repl[Natts_pg_foreign_table];
   16799                 :     Datum       datum;
   16800                 :     Form_pg_foreign_table tableform;
   16801                 : 
   16802 CBC          23 :     if (options == NIL)
   16803 UIC           0 :         return;
   16804 ECB             : 
   16805 CBC          23 :     ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
   16806 ECB             : 
   16807 CBC          23 :     tuple = SearchSysCacheCopy1(FOREIGNTABLEREL, rel->rd_id);
   16808 GIC          23 :     if (!HeapTupleIsValid(tuple))
   16809 UIC           0 :         ereport(ERROR,
   16810 ECB             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   16811                 :                  errmsg("foreign table \"%s\" does not exist",
   16812                 :                         RelationGetRelationName(rel))));
   16813 CBC          23 :     tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   16814 GIC          23 :     server = GetForeignServer(tableform->ftserver);
   16815              23 :     fdw = GetForeignDataWrapper(server->fdwid);
   16816                 : 
   16817 CBC          23 :     memset(repl_val, 0, sizeof(repl_val));
   16818 GIC          23 :     memset(repl_null, false, sizeof(repl_null));
   16819              23 :     memset(repl_repl, false, sizeof(repl_repl));
   16820 ECB             : 
   16821                 :     /* Extract the current options */
   16822 CBC          23 :     datum = SysCacheGetAttr(FOREIGNTABLEREL,
   16823 ECB             :                             tuple,
   16824                 :                             Anum_pg_foreign_table_ftoptions,
   16825                 :                             &isnull);
   16826 GIC          23 :     if (isnull)
   16827               2 :         datum = PointerGetDatum(NULL);
   16828                 : 
   16829                 :     /* Transform the options */
   16830 CBC          23 :     datum = transformGenericOptions(ForeignTableRelationId,
   16831                 :                                     datum,
   16832 ECB             :                                     options,
   16833                 :                                     fdw->fdwvalidator);
   16834                 : 
   16835 CBC          22 :     if (PointerIsValid(DatumGetPointer(datum)))
   16836 GIC          22 :         repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
   16837 ECB             :     else
   16838 LBC           0 :         repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
   16839                 : 
   16840 GIC          22 :     repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
   16841                 : 
   16842 ECB             :     /* Everything looks good - update the tuple */
   16843                 : 
   16844 CBC          22 :     tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
   16845                 :                               repl_val, repl_null, repl_repl);
   16846 ECB             : 
   16847 GIC          22 :     CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
   16848 ECB             : 
   16849                 :     /*
   16850                 :      * Invalidate relcache so that all sessions will refresh any cached plans
   16851                 :      * that might depend on the old options.
   16852                 :      */
   16853 GIC          22 :     CacheInvalidateRelcache(rel);
   16854                 : 
   16855              22 :     InvokeObjectPostAlterHook(ForeignTableRelationId,
   16856 ECB             :                               RelationGetRelid(rel), 0);
   16857                 : 
   16858 CBC          22 :     table_close(ftrel, RowExclusiveLock);
   16859                 : 
   16860 GIC          22 :     heap_freetuple(tuple);
   16861 ECB             : }
   16862                 : 
   16863                 : /*
   16864                 :  * ALTER TABLE ALTER COLUMN SET COMPRESSION
   16865 EUB             :  *
   16866                 :  * Return value is the address of the modified column
   16867                 :  */
   16868 ECB             : static ObjectAddress
   16869 GNC          33 : ATExecSetCompression(Relation rel,
   16870                 :                      const char *column,
   16871 ECB             :                      Node *newValue,
   16872                 :                      LOCKMODE lockmode)
   16873                 : {
   16874                 :     Relation    attrel;
   16875                 :     HeapTuple   tuple;
   16876                 :     Form_pg_attribute atttableform;
   16877                 :     AttrNumber  attnum;
   16878                 :     char       *compression;
   16879                 :     char        cmethod;
   16880                 :     ObjectAddress address;
   16881                 : 
   16882 GIC          33 :     compression = strVal(newValue);
   16883                 : 
   16884              33 :     attrel = table_open(AttributeRelationId, RowExclusiveLock);
   16885                 : 
   16886                 :     /* copy the cache entry so we can scribble on it below */
   16887              33 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
   16888 CBC          33 :     if (!HeapTupleIsValid(tuple))
   16889 UIC           0 :         ereport(ERROR,
   16890                 :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   16891 ECB             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   16892                 :                         column, RelationGetRelationName(rel))));
   16893                 : 
   16894                 :     /* prevent them from altering a system attribute */
   16895 GIC          33 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   16896              33 :     attnum = atttableform->attnum;
   16897              33 :     if (attnum <= 0)
   16898 UIC           0 :         ereport(ERROR,
   16899                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16900                 :                  errmsg("cannot alter system column \"%s\"", column)));
   16901 ECB             : 
   16902                 :     /*
   16903                 :      * Check that column type is compressible, then get the attribute
   16904                 :      * compression method code
   16905                 :      */
   16906 GIC          33 :     cmethod = GetAttributeCompression(atttableform->atttypid, compression);
   16907                 : 
   16908                 :     /* update pg_attribute entry */
   16909 CBC          30 :     atttableform->attcompression = cmethod;
   16910 GIC          30 :     CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
   16911 ECB             : 
   16912 GIC          30 :     InvokeObjectPostAlterHook(RelationRelationId,
   16913                 :                               RelationGetRelid(rel),
   16914                 :                               attnum);
   16915 ECB             : 
   16916                 :     /*
   16917                 :      * Apply the change to indexes as well (only for simple index columns,
   16918                 :      * matching behavior of index.c ConstructTupleDescriptor()).
   16919                 :      */
   16920 GIC          30 :     SetIndexStorageProperties(rel, attrel, attnum,
   16921                 :                               false, 0,
   16922                 :                               true, cmethod,
   16923                 :                               lockmode);
   16924 ECB             : 
   16925 GIC          30 :     heap_freetuple(tuple);
   16926                 : 
   16927 CBC          30 :     table_close(attrel, RowExclusiveLock);
   16928                 : 
   16929 ECB             :     /* make changes visible */
   16930 GIC          30 :     CommandCounterIncrement();
   16931 ECB             : 
   16932 CBC          30 :     ObjectAddressSubSet(address, RelationRelationId,
   16933 ECB             :                         RelationGetRelid(rel), attnum);
   16934 CBC          30 :     return address;
   16935 ECB             : }
   16936                 : 
   16937                 : 
   16938                 : /*
   16939                 :  * Preparation phase for SET LOGGED/UNLOGGED
   16940                 :  *
   16941                 :  * This verifies that we're not trying to change a temp table.  Also,
   16942                 :  * existing foreign key constraints are checked to avoid ending up with
   16943                 :  * permanent tables referencing unlogged tables.
   16944                 :  *
   16945                 :  * Return value is false if the operation is a no-op (in which case the
   16946                 :  * checks are skipped), otherwise true.
   16947                 :  */
   16948                 : static bool
   16949 GIC          35 : ATPrepChangePersistence(Relation rel, bool toLogged)
   16950                 : {
   16951                 :     Relation    pg_constraint;
   16952                 :     HeapTuple   tuple;
   16953 ECB             :     SysScanDesc scan;
   16954                 :     ScanKeyData skey[1];
   16955                 : 
   16956                 :     /*
   16957                 :      * Disallow changing status for a temp table.  Also verify whether we can
   16958                 :      * get away with doing nothing; in such cases we don't need to run the
   16959                 :      * checks below, either.
   16960                 :      */
   16961 GIC          35 :     switch (rel->rd_rel->relpersistence)
   16962                 :     {
   16963 UIC           0 :         case RELPERSISTENCE_TEMP:
   16964               0 :             ereport(ERROR,
   16965                 :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   16966                 :                      errmsg("cannot change logged status of table \"%s\" because it is temporary",
   16967                 :                             RelationGetRelationName(rel)),
   16968                 :                      errtable(rel)));
   16969                 :             break;
   16970 GIC          19 :         case RELPERSISTENCE_PERMANENT:
   16971              19 :             if (toLogged)
   16972 ECB             :                 /* nothing to do */
   16973 CBC           3 :                 return false;
   16974              16 :             break;
   16975              16 :         case RELPERSISTENCE_UNLOGGED:
   16976 GIC          16 :             if (!toLogged)
   16977                 :                 /* nothing to do */
   16978 CBC           3 :                 return false;
   16979              13 :             break;
   16980                 :     }
   16981                 : 
   16982                 :     /*
   16983                 :      * Check that the table is not part of any publication when changing to
   16984                 :      * UNLOGGED, as UNLOGGED tables can't be published.
   16985 ECB             :      */
   16986 CBC          45 :     if (!toLogged &&
   16987 GNC          16 :         GetRelationPublications(RelationGetRelid(rel)) != NIL)
   16988 UIC           0 :         ereport(ERROR,
   16989 ECB             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   16990                 :                  errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
   16991                 :                         RelationGetRelationName(rel)),
   16992                 :                  errdetail("Unlogged relations cannot be replicated.")));
   16993                 : 
   16994                 :     /*
   16995                 :      * Check existing foreign key constraints to preserve the invariant that
   16996                 :      * permanent tables cannot reference unlogged ones.  Self-referencing
   16997                 :      * foreign keys can safely be ignored.
   16998                 :      */
   16999 CBC          29 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   17000                 : 
   17001                 :     /*
   17002                 :      * Scan conrelid if changing to permanent, else confrelid.  This also
   17003                 :      * determines whether a useful index exists.
   17004                 :      */
   17005 GIC          29 :     ScanKeyInit(&skey[0],
   17006                 :                 toLogged ? Anum_pg_constraint_conrelid :
   17007 ECB             :                 Anum_pg_constraint_confrelid,
   17008                 :                 BTEqualStrategyNumber, F_OIDEQ,
   17009                 :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   17010 CBC          29 :     scan = systable_beginscan(pg_constraint,
   17011                 :                               toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
   17012                 :                               true, NULL, 1, skey);
   17013                 : 
   17014 GIC          53 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   17015 ECB             :     {
   17016 CBC          30 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
   17017                 : 
   17018 GIC          30 :         if (con->contype == CONSTRAINT_FOREIGN)
   17019                 :         {
   17020 ECB             :             Oid         foreignrelid;
   17021                 :             Relation    foreignrel;
   17022                 : 
   17023                 :             /* the opposite end of what we used as scankey */
   17024 GIC          15 :             foreignrelid = toLogged ? con->confrelid : con->conrelid;
   17025                 : 
   17026 ECB             :             /* ignore if self-referencing */
   17027 CBC          15 :             if (RelationGetRelid(rel) == foreignrelid)
   17028 GIC           6 :                 continue;
   17029                 : 
   17030               9 :             foreignrel = relation_open(foreignrelid, AccessShareLock);
   17031                 : 
   17032               9 :             if (toLogged)
   17033 ECB             :             {
   17034 CBC           3 :                 if (!RelationIsPermanent(foreignrel))
   17035               3 :                     ereport(ERROR,
   17036 ECB             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   17037                 :                              errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
   17038                 :                                     RelationGetRelationName(rel),
   17039                 :                                     RelationGetRelationName(foreignrel)),
   17040                 :                              errtableconstraint(rel, NameStr(con->conname))));
   17041                 :             }
   17042                 :             else
   17043                 :             {
   17044 CBC           6 :                 if (RelationIsPermanent(foreignrel))
   17045 GIC           3 :                     ereport(ERROR,
   17046 ECB             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   17047                 :                              errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
   17048                 :                                     RelationGetRelationName(rel),
   17049                 :                                     RelationGetRelationName(foreignrel)),
   17050                 :                              errtableconstraint(rel, NameStr(con->conname))));
   17051                 :             }
   17052                 : 
   17053 GIC           3 :             relation_close(foreignrel, AccessShareLock);
   17054                 :         }
   17055                 :     }
   17056                 : 
   17057 CBC          23 :     systable_endscan(scan);
   17058 ECB             : 
   17059 GIC          23 :     table_close(pg_constraint, AccessShareLock);
   17060                 : 
   17061              23 :     return true;
   17062 ECB             : }
   17063                 : 
   17064                 : /*
   17065                 :  * Execute ALTER TABLE SET SCHEMA
   17066                 :  */
   17067                 : ObjectAddress
   17068 CBC          49 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
   17069                 : {
   17070                 :     Relation    rel;
   17071 ECB             :     Oid         relid;
   17072                 :     Oid         oldNspOid;
   17073                 :     Oid         nspOid;
   17074 EUB             :     RangeVar   *newrv;
   17075 ECB             :     ObjectAddresses *objsMoved;
   17076                 :     ObjectAddress myself;
   17077                 : 
   17078 CBC          49 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
   17079 GIC          49 :                                      stmt->missing_ok ? RVR_MISSING_OK : 0,
   17080 ECB             :                                      RangeVarCallbackForAlterRelation,
   17081                 :                                      (void *) stmt);
   17082                 : 
   17083 CBC          48 :     if (!OidIsValid(relid))
   17084                 :     {
   17085               6 :         ereport(NOTICE,
   17086                 :                 (errmsg("relation \"%s\" does not exist, skipping",
   17087                 :                         stmt->relation->relname)));
   17088 GIC           6 :         return InvalidObjectAddress;
   17089                 :     }
   17090                 : 
   17091              42 :     rel = relation_open(relid, NoLock);
   17092                 : 
   17093              42 :     oldNspOid = RelationGetNamespace(rel);
   17094                 : 
   17095 ECB             :     /* If it's an owned sequence, disallow moving it by itself. */
   17096 GIC          42 :     if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
   17097 ECB             :     {
   17098                 :         Oid         tableId;
   17099                 :         int32       colId;
   17100                 : 
   17101 CBC           2 :         if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
   17102 GBC           1 :             sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
   17103 UIC           0 :             ereport(ERROR,
   17104                 :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17105                 :                      errmsg("cannot move an owned sequence into another schema"),
   17106                 :                      errdetail("Sequence \"%s\" is linked to table \"%s\".",
   17107                 :                                RelationGetRelationName(rel),
   17108                 :                                get_rel_name(tableId))));
   17109                 :     }
   17110                 : 
   17111                 :     /* Get and lock schema OID and check its permissions. */
   17112 CBC          42 :     newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
   17113 GIC          42 :     nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
   17114                 : 
   17115                 :     /* common checks on switching namespaces */
   17116 CBC          42 :     CheckSetNamespace(oldNspOid, nspOid);
   17117 ECB             : 
   17118 CBC          42 :     objsMoved = new_object_addresses();
   17119 GBC          42 :     AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
   17120 CBC          42 :     free_object_addresses(objsMoved);
   17121 ECB             : 
   17122 GIC          42 :     ObjectAddressSet(myself, RelationRelationId, relid);
   17123 ECB             : 
   17124 GIC          42 :     if (oldschema)
   17125 CBC          42 :         *oldschema = oldNspOid;
   17126 ECB             : 
   17127                 :     /* close rel, but keep lock until commit */
   17128 GIC          42 :     relation_close(rel, NoLock);
   17129                 : 
   17130              42 :     return myself;
   17131                 : }
   17132                 : 
   17133                 : /*
   17134                 :  * The guts of relocating a table or materialized view to another namespace:
   17135                 :  * besides moving the relation itself, its dependent objects are relocated to
   17136                 :  * the new schema.
   17137                 :  */
   17138                 : void
   17139 CBC          42 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
   17140                 :                             ObjectAddresses *objsMoved)
   17141                 : {
   17142                 :     Relation    classRel;
   17143                 : 
   17144 GIC          42 :     Assert(objsMoved != NULL);
   17145                 : 
   17146                 :     /* OK, modify the pg_class row and pg_depend entry */
   17147              42 :     classRel = table_open(RelationRelationId, RowExclusiveLock);
   17148                 : 
   17149              42 :     AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
   17150                 :                                    nspOid, true, objsMoved);
   17151                 : 
   17152                 :     /* Fix the table's row type too, if it has one */
   17153 CBC          42 :     if (OidIsValid(rel->rd_rel->reltype))
   17154              41 :         AlterTypeNamespaceInternal(rel->rd_rel->reltype,
   17155                 :                                    nspOid, false, false, objsMoved);
   17156 ECB             : 
   17157 EUB             :     /* Fix other dependent stuff */
   17158 GIC          42 :     if (rel->rd_rel->relkind == RELKIND_RELATION ||
   17159 CBC          10 :         rel->rd_rel->relkind == RELKIND_MATVIEW ||
   17160               7 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   17161                 :     {
   17162              35 :         AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
   17163              35 :         AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
   17164                 :                            objsMoved, AccessExclusiveLock);
   17165              35 :         AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
   17166 ECB             :                                   false, objsMoved);
   17167                 :     }
   17168                 : 
   17169 GIC          42 :     table_close(classRel, RowExclusiveLock);
   17170              42 : }
   17171 ECB             : 
   17172                 : /*
   17173                 :  * The guts of relocating a relation to another namespace: fix the pg_class
   17174                 :  * entry, and the pg_depend entry if any.  Caller must already have
   17175                 :  * opened and write-locked pg_class.
   17176                 :  */
   17177                 : void
   17178 GIC          88 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
   17179 ECB             :                                Oid oldNspOid, Oid newNspOid,
   17180 EUB             :                                bool hasDependEntry,
   17181 ECB             :                                ObjectAddresses *objsMoved)
   17182                 : {
   17183                 :     HeapTuple   classTup;
   17184                 :     Form_pg_class classForm;
   17185                 :     ObjectAddress thisobj;
   17186 CBC          88 :     bool        already_done = false;
   17187                 : 
   17188              88 :     classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
   17189              88 :     if (!HeapTupleIsValid(classTup))
   17190 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", relOid);
   17191 GIC          88 :     classForm = (Form_pg_class) GETSTRUCT(classTup);
   17192                 : 
   17193              88 :     Assert(classForm->relnamespace == oldNspOid);
   17194                 : 
   17195 CBC          88 :     thisobj.classId = RelationRelationId;
   17196 GIC          88 :     thisobj.objectId = relOid;
   17197 CBC          88 :     thisobj.objectSubId = 0;
   17198 ECB             : 
   17199                 :     /*
   17200                 :      * If the object has already been moved, don't move it again.  If it's
   17201                 :      * already in the right place, don't move it, but still fire the object
   17202                 :      * access hook.
   17203                 :      */
   17204 CBC          88 :     already_done = object_address_present(&thisobj, objsMoved);
   17205              88 :     if (!already_done && oldNspOid != newNspOid)
   17206                 :     {
   17207                 :         /* check for duplicate name (more friendly than unique-index failure) */
   17208 GIC          67 :         if (get_relname_relid(NameStr(classForm->relname),
   17209                 :                               newNspOid) != InvalidOid)
   17210 UIC           0 :             ereport(ERROR,
   17211                 :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   17212                 :                      errmsg("relation \"%s\" already exists in schema \"%s\"",
   17213                 :                             NameStr(classForm->relname),
   17214                 :                             get_namespace_name(newNspOid))));
   17215 ECB             : 
   17216                 :         /* classTup is a copy, so OK to scribble on */
   17217 CBC          67 :         classForm->relnamespace = newNspOid;
   17218                 : 
   17219 GIC          67 :         CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
   17220 ECB             : 
   17221                 :         /* Update dependency on schema if caller said so */
   17222 GIC         115 :         if (hasDependEntry &&
   17223              48 :             changeDependencyFor(RelationRelationId,
   17224                 :                                 relOid,
   17225                 :                                 NamespaceRelationId,
   17226                 :                                 oldNspOid,
   17227 ECB             :                                 newNspOid) != 1)
   17228 UIC           0 :             elog(ERROR, "failed to change schema dependency for relation \"%s\"",
   17229                 :                  NameStr(classForm->relname));
   17230                 :     }
   17231 GIC          88 :     if (!already_done)
   17232                 :     {
   17233 CBC          88 :         add_exact_object_address(&thisobj, objsMoved);
   17234                 : 
   17235              88 :         InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
   17236 ECB             :     }
   17237                 : 
   17238 CBC          88 :     heap_freetuple(classTup);
   17239 GIC          88 : }
   17240 ECB             : 
   17241                 : /*
   17242                 :  * Move all indexes for the specified relation to another namespace.
   17243                 :  *
   17244                 :  * Note: we assume adequate permission checking was done by the caller,
   17245                 :  * and that the caller has a suitable lock on the owning relation.
   17246                 :  */
   17247                 : static void
   17248 CBC          35 : AlterIndexNamespaces(Relation classRel, Relation rel,
   17249                 :                      Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
   17250                 : {
   17251                 :     List       *indexList;
   17252                 :     ListCell   *l;
   17253 EUB             : 
   17254 GIC          35 :     indexList = RelationGetIndexList(rel);
   17255                 : 
   17256 CBC          57 :     foreach(l, indexList)
   17257 ECB             :     {
   17258 GBC          22 :         Oid         indexOid = lfirst_oid(l);
   17259                 :         ObjectAddress thisobj;
   17260                 : 
   17261 GIC          22 :         thisobj.classId = RelationRelationId;
   17262              22 :         thisobj.objectId = indexOid;
   17263 CBC          22 :         thisobj.objectSubId = 0;
   17264                 : 
   17265                 :         /*
   17266 ECB             :          * Note: currently, the index will not have its own dependency on the
   17267                 :          * namespace, so we don't need to do changeDependencyFor(). There's no
   17268                 :          * row type in pg_type, either.
   17269                 :          *
   17270                 :          * XXX this objsMoved test may be pointless -- surely we have a single
   17271                 :          * dependency link from a relation to each index?
   17272                 :          */
   17273 GIC          22 :         if (!object_address_present(&thisobj, objsMoved))
   17274 ECB             :         {
   17275 CBC          22 :             AlterRelationNamespaceInternal(classRel, indexOid,
   17276 ECB             :                                            oldNspOid, newNspOid,
   17277                 :                                            false, objsMoved);
   17278 GIC          22 :             add_exact_object_address(&thisobj, objsMoved);
   17279                 :         }
   17280                 :     }
   17281 ECB             : 
   17282 CBC          35 :     list_free(indexList);
   17283 GIC          35 : }
   17284                 : 
   17285                 : /*
   17286                 :  * Move all identity and SERIAL-column sequences of the specified relation to another
   17287 ECB             :  * namespace.
   17288                 :  *
   17289                 :  * Note: we assume adequate permission checking was done by the caller,
   17290                 :  * and that the caller has a suitable lock on the owning relation.
   17291                 :  */
   17292                 : static void
   17293 CBC          35 : AlterSeqNamespaces(Relation classRel, Relation rel,
   17294 ECB             :                    Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
   17295                 :                    LOCKMODE lockmode)
   17296                 : {
   17297                 :     Relation    depRel;
   17298                 :     SysScanDesc scan;
   17299                 :     ScanKeyData key[2];
   17300                 :     HeapTuple   tup;
   17301                 : 
   17302                 :     /*
   17303                 :      * SERIAL sequences are those having an auto dependency on one of the
   17304                 :      * table's columns (we don't care *which* column, exactly).
   17305                 :      */
   17306 GIC          35 :     depRel = table_open(DependRelationId, AccessShareLock);
   17307                 : 
   17308              35 :     ScanKeyInit(&key[0],
   17309                 :                 Anum_pg_depend_refclassid,
   17310 ECB             :                 BTEqualStrategyNumber, F_OIDEQ,
   17311 EUB             :                 ObjectIdGetDatum(RelationRelationId));
   17312 GIC          35 :     ScanKeyInit(&key[1],
   17313                 :                 Anum_pg_depend_refobjid,
   17314                 :                 BTEqualStrategyNumber, F_OIDEQ,
   17315                 :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   17316 ECB             :     /* we leave refobjsubid unspecified */
   17317                 : 
   17318 CBC          35 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   17319                 :                               NULL, 2, key);
   17320                 : 
   17321 GIC         246 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   17322                 :     {
   17323             211 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   17324                 :         Relation    seqRel;
   17325                 : 
   17326 ECB             :         /* skip dependencies other than auto dependencies on columns */
   17327 GIC         211 :         if (depForm->refobjsubid == 0 ||
   17328 CBC         150 :             depForm->classid != RelationRelationId ||
   17329 GIC          18 :             depForm->objsubid != 0 ||
   17330              18 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   17331             193 :             continue;
   17332                 : 
   17333                 :         /* Use relation_open just in case it's an index */
   17334              18 :         seqRel = relation_open(depForm->objid, lockmode);
   17335 ECB             : 
   17336                 :         /* skip non-sequence relations */
   17337 GIC          18 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   17338                 :         {
   17339                 :             /* No need to keep the lock */
   17340 UIC           0 :             relation_close(seqRel, lockmode);
   17341 LBC           0 :             continue;
   17342                 :         }
   17343                 : 
   17344 ECB             :         /* Fix the pg_class and pg_depend entries */
   17345 GIC          18 :         AlterRelationNamespaceInternal(classRel, depForm->objid,
   17346 ECB             :                                        oldNspOid, newNspOid,
   17347                 :                                        true, objsMoved);
   17348                 : 
   17349 EUB             :         /*
   17350                 :          * Sequences used to have entries in pg_type, but no longer do.  If we
   17351 ECB             :          * ever re-instate that, we'll need to move the pg_type entry to the
   17352                 :          * new namespace, too (using AlterTypeNamespaceInternal).
   17353                 :          */
   17354 CBC          18 :         Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
   17355 ECB             : 
   17356                 :         /* Now we can close it.  Keep the lock till end of transaction. */
   17357 GIC          18 :         relation_close(seqRel, NoLock);
   17358                 :     }
   17359                 : 
   17360              35 :     systable_endscan(scan);
   17361                 : 
   17362 CBC          35 :     relation_close(depRel, AccessShareLock);
   17363 GIC          35 : }
   17364                 : 
   17365                 : 
   17366                 : /*
   17367                 :  * This code supports
   17368 ECB             :  *  CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
   17369                 :  *
   17370                 :  * Because we only support this for TEMP tables, it's sufficient to remember
   17371                 :  * the state in a backend-local data structure.
   17372                 :  */
   17373                 : 
   17374                 : /*
   17375 EUB             :  * Register a newly-created relation's ON COMMIT action.
   17376                 :  */
   17377 ECB             : void
   17378 CBC          80 : register_on_commit_action(Oid relid, OnCommitAction action)
   17379                 : {
   17380 ECB             :     OnCommitItem *oc;
   17381                 :     MemoryContext oldcxt;
   17382                 : 
   17383                 :     /*
   17384                 :      * We needn't bother registering the relation unless there is an ON COMMIT
   17385                 :      * action we need to take.
   17386                 :      */
   17387 GIC          80 :     if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
   17388 CBC          12 :         return;
   17389                 : 
   17390 GIC          68 :     oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
   17391                 : 
   17392              68 :     oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
   17393              68 :     oc->relid = relid;
   17394              68 :     oc->oncommit = action;
   17395              68 :     oc->creating_subid = GetCurrentSubTransactionId();
   17396              68 :     oc->deleting_subid = InvalidSubTransactionId;
   17397                 : 
   17398                 :     /*
   17399                 :      * We use lcons() here so that ON COMMIT actions are processed in reverse
   17400                 :      * order of registration.  That might not be essential but it seems
   17401 ECB             :      * reasonable.
   17402 EUB             :      */
   17403 GIC          68 :     on_commits = lcons(oc, on_commits);
   17404 ECB             : 
   17405 GIC          68 :     MemoryContextSwitchTo(oldcxt);
   17406 ECB             : }
   17407                 : 
   17408 EUB             : /*
   17409                 :  * Unregister any ON COMMIT action when a relation is deleted.
   17410                 :  *
   17411                 :  * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
   17412 ECB             :  */
   17413                 : void
   17414 CBC       19167 : remove_on_commit_action(Oid relid)
   17415                 : {
   17416 ECB             :     ListCell   *l;
   17417                 : 
   17418 CBC       19231 :     foreach(l, on_commits)
   17419                 :     {
   17420 GIC         126 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   17421 ECB             : 
   17422 GIC         126 :         if (oc->relid == relid)
   17423                 :         {
   17424              62 :             oc->deleting_subid = GetCurrentSubTransactionId();
   17425 CBC          62 :             break;
   17426 ECB             :         }
   17427                 :     }
   17428 GIC       19167 : }
   17429 ECB             : 
   17430                 : /*
   17431                 :  * Perform ON COMMIT actions.
   17432                 :  *
   17433                 :  * This is invoked just before actually committing, since it's possible
   17434                 :  * to encounter errors.
   17435                 :  */
   17436                 : void
   17437 GBC      465920 : PreCommit_on_commit_actions(void)
   17438                 : {
   17439 ECB             :     ListCell   *l;
   17440 GIC      465920 :     List       *oids_to_truncate = NIL;
   17441          465920 :     List       *oids_to_drop = NIL;
   17442                 : 
   17443 CBC      466275 :     foreach(l, on_commits)
   17444                 :     {
   17445 GIC         355 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   17446 ECB             : 
   17447                 :         /* Ignore entry if already dropped in this xact */
   17448 GIC         355 :         if (oc->deleting_subid != InvalidSubTransactionId)
   17449              34 :             continue;
   17450                 : 
   17451             321 :         switch (oc->oncommit)
   17452 ECB             :         {
   17453 UIC           0 :             case ONCOMMIT_NOOP:
   17454 ECB             :             case ONCOMMIT_PRESERVE_ROWS:
   17455                 :                 /* Do nothing (there shouldn't be such entries, actually) */
   17456 UIC           0 :                 break;
   17457 CBC         299 :             case ONCOMMIT_DELETE_ROWS:
   17458                 : 
   17459 ECB             :                 /*
   17460                 :                  * If this transaction hasn't accessed any temporary
   17461                 :                  * relations, we can skip truncating ON COMMIT DELETE ROWS
   17462                 :                  * tables, as they must still be empty.
   17463                 :                  */
   17464 GIC         299 :                 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
   17465             200 :                     oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
   17466             299 :                 break;
   17467              22 :             case ONCOMMIT_DROP:
   17468 CBC          22 :                 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
   17469 GIC          22 :                 break;
   17470                 :         }
   17471                 :     }
   17472                 : 
   17473                 :     /*
   17474                 :      * Truncate relations before dropping so that all dependencies between
   17475                 :      * relations are removed after they are worked on.  Doing it like this
   17476                 :      * might be a waste as it is possible that a relation being truncated will
   17477                 :      * be dropped anyway due to its parent being dropped, but this makes the
   17478                 :      * code more robust because of not having to re-check that the relation
   17479                 :      * exists at truncation time.
   17480                 :      */
   17481 CBC      465920 :     if (oids_to_truncate != NIL)
   17482 GIC         167 :         heap_truncate(oids_to_truncate);
   17483 ECB             : 
   17484 GIC      465917 :     if (oids_to_drop != NIL)
   17485                 :     {
   17486 CBC          19 :         ObjectAddresses *targetObjects = new_object_addresses();
   17487 EUB             : 
   17488 GIC          41 :         foreach(l, oids_to_drop)
   17489                 :         {
   17490                 :             ObjectAddress object;
   17491                 : 
   17492              22 :             object.classId = RelationRelationId;
   17493 CBC          22 :             object.objectId = lfirst_oid(l);
   17494              22 :             object.objectSubId = 0;
   17495 ECB             : 
   17496 GBC          22 :             Assert(!object_address_present(&object, targetObjects));
   17497                 : 
   17498 GIC          22 :             add_exact_object_address(&object, targetObjects);
   17499                 :         }
   17500                 : 
   17501                 :         /*
   17502                 :          * Since this is an automatic drop, rather than one directly initiated
   17503                 :          * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
   17504 ECB             :          */
   17505 GIC          19 :         performMultipleDeletions(targetObjects, DROP_CASCADE,
   17506                 :                                  PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
   17507 ECB             : 
   17508                 : #ifdef USE_ASSERT_CHECKING
   17509                 : 
   17510                 :         /*
   17511                 :          * Note that table deletion will call remove_on_commit_action, so the
   17512                 :          * entry should get marked as deleted.
   17513                 :          */
   17514 GIC          66 :         foreach(l, on_commits)
   17515                 :         {
   17516              47 :             OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   17517                 : 
   17518 CBC          47 :             if (oc->oncommit != ONCOMMIT_DROP)
   17519 GIC          25 :                 continue;
   17520                 : 
   17521              22 :             Assert(oc->deleting_subid != InvalidSubTransactionId);
   17522                 :         }
   17523 ECB             : #endif
   17524                 :     }
   17525 CBC      465917 : }
   17526                 : 
   17527                 : /*
   17528 ECB             :  * Post-commit or post-abort cleanup for ON COMMIT management.
   17529                 :  *
   17530                 :  * All we do here is remove no-longer-needed OnCommitItem entries.
   17531                 :  *
   17532                 :  * During commit, remove entries that were deleted during this transaction;
   17533                 :  * during abort, remove those created during this transaction.
   17534                 :  */
   17535                 : void
   17536 GIC      485839 : AtEOXact_on_commit_actions(bool isCommit)
   17537                 : {
   17538                 :     ListCell   *cur_item;
   17539                 : 
   17540          486209 :     foreach(cur_item, on_commits)
   17541                 :     {
   17542             370 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   17543                 : 
   17544             421 :         if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
   17545              51 :             oc->creating_subid != InvalidSubTransactionId)
   17546                 :         {
   17547 ECB             :             /* cur_item must be removed */
   17548 GIC          68 :             on_commits = foreach_delete_current(on_commits, cur_item);
   17549              68 :             pfree(oc);
   17550                 :         }
   17551                 :         else
   17552                 :         {
   17553                 :             /* cur_item must be preserved */
   17554             302 :             oc->creating_subid = InvalidSubTransactionId;
   17555             302 :             oc->deleting_subid = InvalidSubTransactionId;
   17556                 :         }
   17557                 :     }
   17558          485839 : }
   17559 ECB             : 
   17560                 : /*
   17561 EUB             :  * Post-subcommit or post-subabort cleanup for ON COMMIT management.
   17562                 :  *
   17563                 :  * During subabort, we can immediately remove entries created during this
   17564                 :  * subtransaction.  During subcommit, just relabel entries marked during
   17565                 :  * this subtransaction as being the parent's responsibility.
   17566                 :  */
   17567                 : void
   17568 CBC        8785 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
   17569 ECB             :                               SubTransactionId parentSubid)
   17570                 : {
   17571                 :     ListCell   *cur_item;
   17572                 : 
   17573 CBC        8785 :     foreach(cur_item, on_commits)
   17574 ECB             :     {
   17575 UIC           0 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   17576 ECB             : 
   17577 LBC           0 :         if (!isCommit && oc->creating_subid == mySubid)
   17578                 :         {
   17579                 :             /* cur_item must be removed */
   17580 UIC           0 :             on_commits = foreach_delete_current(on_commits, cur_item);
   17581               0 :             pfree(oc);
   17582                 :         }
   17583                 :         else
   17584 ECB             :         {
   17585                 :             /* cur_item must be preserved */
   17586 UBC           0 :             if (oc->creating_subid == mySubid)
   17587 UIC           0 :                 oc->creating_subid = parentSubid;
   17588               0 :             if (oc->deleting_subid == mySubid)
   17589               0 :                 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
   17590                 :         }
   17591                 :     }
   17592 GIC        8785 : }
   17593                 : 
   17594                 : /*
   17595                 :  * This is intended as a callback for RangeVarGetRelidExtended().  It allows
   17596                 :  * the relation to be locked only if (1) it's a plain or partitioned table,
   17597 ECB             :  * materialized view, or TOAST table and (2) the current user is the owner (or
   17598                 :  * the superuser) or has been granted MAINTAIN.  This meets the
   17599                 :  * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
   17600                 :  * MATERIALIZED VIEW; we expose it here so that it can be used by all.
   17601                 :  */
   17602                 : void
   17603 GNC         437 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
   17604                 :                                Oid relId, Oid oldRelId, void *arg)
   17605                 : {
   17606                 :     char        relkind;
   17607                 : 
   17608 ECB             :     /* Nothing to do if the relation was not found. */
   17609 GIC         437 :     if (!OidIsValid(relId))
   17610               3 :         return;
   17611                 : 
   17612 ECB             :     /*
   17613                 :      * If the relation does exist, check whether it's an index.  But note that
   17614                 :      * the relation might have been dropped between the time we did the name
   17615                 :      * lookup and now.  In that case, there's nothing to do.
   17616                 :      */
   17617 GIC         434 :     relkind = get_rel_relkind(relId);
   17618             434 :     if (!relkind)
   17619 UIC           0 :         return;
   17620 GIC         434 :     if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
   17621              50 :         relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
   17622 CBC          14 :         ereport(ERROR,
   17623                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17624                 :                  errmsg("\"%s\" is not a table or materialized view", relation->relname)));
   17625 ECB             : 
   17626                 :     /* Check permissions */
   17627 GNC         420 :     if (pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN) != ACLCHECK_OK &&
   17628              12 :         !has_partition_ancestor_privs(relId, GetUserId(), ACL_MAINTAIN))
   17629              12 :         aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TABLE,
   17630              12 :                        relation->relname);
   17631                 : }
   17632                 : 
   17633                 : /*
   17634                 :  * If relid is a partition, returns whether userid has any of the privileges
   17635                 :  * specified in acl on any of its ancestors.  Otherwise, returns false.
   17636                 :  */
   17637                 : bool
   17638             277 : has_partition_ancestor_privs(Oid relid, Oid userid, AclMode acl)
   17639                 : {
   17640                 :     List       *ancestors;
   17641                 :     ListCell   *lc;
   17642                 : 
   17643             277 :     if (!get_rel_relispartition(relid))
   17644             113 :         return false;
   17645                 : 
   17646             164 :     ancestors = get_partition_ancestors(relid);
   17647             218 :     foreach(lc, ancestors)
   17648                 :     {
   17649             164 :         Oid         ancestor = lfirst_oid(lc);
   17650                 : 
   17651             328 :         if (OidIsValid(ancestor) &&
   17652             164 :             pg_class_aclcheck(ancestor, userid, acl) == ACLCHECK_OK)
   17653             110 :             return true;
   17654                 :     }
   17655                 : 
   17656              54 :     return false;
   17657                 : }
   17658 ECB             : 
   17659                 : /*
   17660                 :  * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
   17661                 :  */
   17662                 : static void
   17663 GIC         809 : RangeVarCallbackForTruncate(const RangeVar *relation,
   17664                 :                             Oid relId, Oid oldRelId, void *arg)
   17665                 : {
   17666                 :     HeapTuple   tuple;
   17667                 : 
   17668                 :     /* Nothing to do if the relation was not found. */
   17669             809 :     if (!OidIsValid(relId))
   17670 LBC           0 :         return;
   17671 ECB             : 
   17672 GIC         809 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   17673             809 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   17674 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   17675                 : 
   17676 GIC         809 :     truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
   17677             807 :     truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
   17678                 : 
   17679 CBC         791 :     ReleaseSysCache(tuple);
   17680                 : }
   17681                 : 
   17682                 : /*
   17683 ECB             :  * Callback to RangeVarGetRelidExtended(), similar to
   17684                 :  * RangeVarCallbackOwnsTable() but without checks on the type of the relation.
   17685                 :  */
   17686                 : void
   17687 CBC        6659 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
   17688                 :                              Oid relId, Oid oldRelId, void *arg)
   17689                 : {
   17690                 :     HeapTuple   tuple;
   17691                 : 
   17692                 :     /* Nothing to do if the relation was not found. */
   17693 GIC        6659 :     if (!OidIsValid(relId))
   17694 CBC           7 :         return;
   17695                 : 
   17696 GIC        6652 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   17697            6652 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   17698 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   17699                 : 
   17700 GNC        6652 :     if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
   17701 GIC           3 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
   17702               3 :                        relation->relname);
   17703                 : 
   17704 CBC       13238 :     if (!allowSystemTableMods &&
   17705            6589 :         IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
   17706 GIC           1 :         ereport(ERROR,
   17707                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   17708                 :                  errmsg("permission denied: \"%s\" is a system catalog",
   17709 ECB             :                         relation->relname)));
   17710                 : 
   17711 CBC        6648 :     ReleaseSysCache(tuple);
   17712                 : }
   17713                 : 
   17714 ECB             : /*
   17715                 :  * Common RangeVarGetRelid callback for rename, set schema, and alter table
   17716                 :  * processing.
   17717                 :  */
   17718                 : static void
   17719 CBC       45540 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
   17720                 :                                  void *arg)
   17721                 : {
   17722           45540 :     Node       *stmt = (Node *) arg;
   17723                 :     ObjectType  reltype;
   17724                 :     HeapTuple   tuple;
   17725                 :     Form_pg_class classform;
   17726                 :     AclResult   aclresult;
   17727 ECB             :     char        relkind;
   17728                 : 
   17729 GBC       45540 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   17730 GIC       45540 :     if (!HeapTupleIsValid(tuple))
   17731             110 :         return;                 /* concurrently dropped */
   17732           45430 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   17733           45430 :     relkind = classform->relkind;
   17734                 : 
   17735                 :     /* Must own relation. */
   17736 GNC       45430 :     if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
   17737 GIC          30 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
   17738 ECB             : 
   17739                 :     /* No system table modifications unless explicitly allowed. */
   17740 GIC       45400 :     if (!allowSystemTableMods && IsSystemClass(relid, classform))
   17741              14 :         ereport(ERROR,
   17742 ECB             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   17743                 :                  errmsg("permission denied: \"%s\" is a system catalog",
   17744                 :                         rv->relname)));
   17745                 : 
   17746                 :     /*
   17747                 :      * Extract the specified relation type from the statement parse tree.
   17748                 :      *
   17749                 :      * Also, for ALTER .. RENAME, check permissions: the user must (still)
   17750                 :      * have CREATE rights on the containing namespace.
   17751                 :      */
   17752 GIC       45386 :     if (IsA(stmt, RenameStmt))
   17753                 :     {
   17754 GNC         247 :         aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
   17755                 :                                           GetUserId(), ACL_CREATE);
   17756 CBC         247 :         if (aclresult != ACLCHECK_OK)
   17757 UIC           0 :             aclcheck_error(aclresult, OBJECT_SCHEMA,
   17758               0 :                            get_namespace_name(classform->relnamespace));
   17759 GIC         247 :         reltype = ((RenameStmt *) stmt)->renameType;
   17760                 :     }
   17761           45139 :     else if (IsA(stmt, AlterObjectSchemaStmt))
   17762              42 :         reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
   17763                 : 
   17764           45097 :     else if (IsA(stmt, AlterTableStmt))
   17765 CBC       45097 :         reltype = ((AlterTableStmt *) stmt)->objtype;
   17766                 :     else
   17767                 :     {
   17768 UIC           0 :         elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
   17769                 :         reltype = OBJECT_TABLE; /* placate compiler */
   17770 ECB             :     }
   17771                 : 
   17772                 :     /*
   17773                 :      * For compatibility with prior releases, we allow ALTER TABLE to be used
   17774                 :      * with most other types of relations (but not composite types). We allow
   17775                 :      * similar flexibility for ALTER INDEX in the case of RENAME, but not
   17776                 :      * otherwise.  Otherwise, the user must select the correct form of the
   17777                 :      * command for the relation at issue.
   17778                 :      */
   17779 CBC       45386 :     if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
   17780 LBC           0 :         ereport(ERROR,
   17781                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17782                 :                  errmsg("\"%s\" is not a sequence", rv->relname)));
   17783                 : 
   17784 CBC       45386 :     if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
   17785 LBC           0 :         ereport(ERROR,
   17786 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17787                 :                  errmsg("\"%s\" is not a view", rv->relname)));
   17788                 : 
   17789 CBC       45386 :     if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
   17790 UIC           0 :         ereport(ERROR,
   17791 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17792                 :                  errmsg("\"%s\" is not a materialized view", rv->relname)));
   17793                 : 
   17794 GIC       45386 :     if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
   17795 LBC           0 :         ereport(ERROR,
   17796 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17797                 :                  errmsg("\"%s\" is not a foreign table", rv->relname)));
   17798                 : 
   17799 GIC       45386 :     if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
   17800 UIC           0 :         ereport(ERROR,
   17801                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17802                 :                  errmsg("\"%s\" is not a composite type", rv->relname)));
   17803                 : 
   17804 CBC       45386 :     if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
   17805                 :         relkind != RELKIND_PARTITIONED_INDEX
   17806 GIC          18 :         && !IsA(stmt, RenameStmt))
   17807               3 :         ereport(ERROR,
   17808                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17809                 :                  errmsg("\"%s\" is not an index", rv->relname)));
   17810                 : 
   17811                 :     /*
   17812 ECB             :      * Don't allow ALTER TABLE on composite types. We want people to use ALTER
   17813                 :      * TYPE for that.
   17814                 :      */
   17815 CBC       45383 :     if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
   17816 UBC           0 :         ereport(ERROR,
   17817 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17818                 :                  errmsg("\"%s\" is a composite type", rv->relname),
   17819                 :                  errhint("Use ALTER TYPE instead.")));
   17820                 : 
   17821                 :     /*
   17822                 :      * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
   17823                 :      * to a different schema, such as indexes and TOAST tables.
   17824                 :      */
   17825 GIC       45383 :     if (IsA(stmt, AlterObjectSchemaStmt))
   17826                 :     {
   17827              42 :         if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
   17828 UIC           0 :             ereport(ERROR,
   17829                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17830 ECB             :                      errmsg("cannot change schema of index \"%s\"",
   17831                 :                             rv->relname),
   17832                 :                      errhint("Change the schema of the table instead.")));
   17833 GIC          42 :         else if (relkind == RELKIND_COMPOSITE_TYPE)
   17834 LBC           0 :             ereport(ERROR,
   17835                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17836 EUB             :                      errmsg("cannot change schema of composite type \"%s\"",
   17837                 :                             rv->relname),
   17838                 :                      errhint("Use ALTER TYPE instead.")));
   17839 GIC          42 :         else if (relkind == RELKIND_TOASTVALUE)
   17840 UIC           0 :             ereport(ERROR,
   17841                 :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17842                 :                      errmsg("cannot change schema of TOAST table \"%s\"",
   17843 ECB             :                             rv->relname),
   17844                 :                      errhint("Change the schema of the table instead.")));
   17845                 :     }
   17846                 : 
   17847 GIC       45383 :     ReleaseSysCache(tuple);
   17848 ECB             : }
   17849                 : 
   17850                 : /*
   17851                 :  * Transform any expressions present in the partition key
   17852                 :  *
   17853                 :  * Returns a transformed PartitionSpec.
   17854 EUB             :  */
   17855                 : static PartitionSpec *
   17856 GNC        2163 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
   17857 ECB             : {
   17858                 :     PartitionSpec *newspec;
   17859                 :     ParseState *pstate;
   17860                 :     ParseNamespaceItem *nsitem;
   17861                 :     ListCell   *l;
   17862                 : 
   17863 GIC        2163 :     newspec = makeNode(PartitionSpec);
   17864 ECB             : 
   17865 CBC        2163 :     newspec->strategy = partspec->strategy;
   17866 GIC        2163 :     newspec->partParams = NIL;
   17867            2163 :     newspec->location = partspec->location;
   17868                 : 
   17869 ECB             :     /* Check valid number of columns for strategy */
   17870 GNC        3261 :     if (partspec->strategy == PARTITION_STRATEGY_LIST &&
   17871 CBC        1098 :         list_length(partspec->partParams) != 1)
   17872 GIC           3 :         ereport(ERROR,
   17873                 :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17874 ECB             :                  errmsg("cannot use \"list\" partition strategy with more than one column")));
   17875                 : 
   17876                 :     /*
   17877                 :      * Create a dummy ParseState and insert the target relation as its sole
   17878                 :      * rangetable entry.  We need a ParseState for transformExpr.
   17879                 :      */
   17880 GIC        2160 :     pstate = make_parsestate(NULL);
   17881            2160 :     nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
   17882                 :                                            NULL, false, true);
   17883            2160 :     addNSItemToQuery(pstate, nsitem, true, true, true);
   17884                 : 
   17885                 :     /* take care of any partition expressions */
   17886 CBC        4500 :     foreach(l, partspec->partParams)
   17887                 :     {
   17888            2352 :         PartitionElem *pelem = lfirst_node(PartitionElem, l);
   17889                 : 
   17890 GIC        2352 :         if (pelem->expr)
   17891 ECB             :         {
   17892                 :             /* Copy, to avoid scribbling on the input */
   17893 GIC         143 :             pelem = copyObject(pelem);
   17894                 : 
   17895 ECB             :             /* Now do parse transformation of the expression */
   17896 CBC         143 :             pelem->expr = transformExpr(pstate, pelem->expr,
   17897                 :                                         EXPR_KIND_PARTITION_EXPRESSION);
   17898                 : 
   17899                 :             /* we have to fix its collations too */
   17900 GIC         131 :             assign_expr_collations(pstate, pelem->expr);
   17901                 :         }
   17902                 : 
   17903            2340 :         newspec->partParams = lappend(newspec->partParams, pelem);
   17904                 :     }
   17905                 : 
   17906 CBC        2148 :     return newspec;
   17907                 : }
   17908                 : 
   17909                 : /*
   17910                 :  * Compute per-partition-column information from a list of PartitionElems.
   17911                 :  * Expressions in the PartitionElems must be parse-analyzed already.
   17912                 :  */
   17913                 : static void
   17914 GIC        2148 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
   17915                 :                       List **partexprs, Oid *partopclass, Oid *partcollation,
   17916                 :                       PartitionStrategy strategy)
   17917                 : {
   17918                 :     int         attn;
   17919 ECB             :     ListCell   *lc;
   17920                 :     Oid         am_oid;
   17921                 : 
   17922 GIC        2148 :     attn = 0;
   17923            4446 :     foreach(lc, partParams)
   17924                 :     {
   17925 CBC        2340 :         PartitionElem *pelem = lfirst_node(PartitionElem, lc);
   17926                 :         Oid         atttype;
   17927                 :         Oid         attcollation;
   17928                 : 
   17929 GIC        2340 :         if (pelem->name != NULL)
   17930                 :         {
   17931 ECB             :             /* Simple attribute reference */
   17932                 :             HeapTuple   atttuple;
   17933                 :             Form_pg_attribute attform;
   17934                 : 
   17935 GIC        2209 :             atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
   17936 CBC        2209 :                                              pelem->name);
   17937 GIC        2209 :             if (!HeapTupleIsValid(atttuple))
   17938               6 :                 ereport(ERROR,
   17939                 :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   17940 ECB             :                          errmsg("column \"%s\" named in partition key does not exist",
   17941                 :                                 pelem->name),
   17942                 :                          parser_errposition(pstate, pelem->location)));
   17943 CBC        2203 :             attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   17944 ECB             : 
   17945 GIC        2203 :             if (attform->attnum <= 0)
   17946               3 :                 ereport(ERROR,
   17947 ECB             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17948                 :                          errmsg("cannot use system column \"%s\" in partition key",
   17949                 :                                 pelem->name),
   17950                 :                          parser_errposition(pstate, pelem->location)));
   17951                 : 
   17952                 :             /*
   17953 EUB             :              * Generated columns cannot work: They are computed after BEFORE
   17954                 :              * triggers, but partition routing is done before all triggers.
   17955                 :              */
   17956 GIC        2200 :             if (attform->attgenerated)
   17957               3 :                 ereport(ERROR,
   17958 ECB             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17959                 :                          errmsg("cannot use generated column in partition key"),
   17960                 :                          errdetail("Column \"%s\" is a generated column.",
   17961                 :                                    pelem->name),
   17962                 :                          parser_errposition(pstate, pelem->location)));
   17963                 : 
   17964 GIC        2197 :             partattrs[attn] = attform->attnum;
   17965            2197 :             atttype = attform->atttypid;
   17966            2197 :             attcollation = attform->attcollation;
   17967 CBC        2197 :             ReleaseSysCache(atttuple);
   17968                 :         }
   17969                 :         else
   17970 ECB             :         {
   17971                 :             /* Expression */
   17972 GIC         131 :             Node       *expr = pelem->expr;
   17973 ECB             :             char        partattname[16];
   17974                 : 
   17975 CBC         131 :             Assert(expr != NULL);
   17976             131 :             atttype = exprType(expr);
   17977 GIC         131 :             attcollation = exprCollation(expr);
   17978                 : 
   17979                 :             /*
   17980                 :              * The expression must be of a storable type (e.g., not RECORD).
   17981                 :              * The test is the same as for whether a table column is of a safe
   17982                 :              * type (which is why we needn't check for the non-expression
   17983                 :              * case).
   17984                 :              */
   17985             131 :             snprintf(partattname, sizeof(partattname), "%d", attn + 1);
   17986             131 :             CheckAttributeType(partattname,
   17987                 :                                atttype, attcollation,
   17988                 :                                NIL, CHKATYPE_IS_PARTKEY);
   17989                 : 
   17990                 :             /*
   17991 ECB             :              * Strip any top-level COLLATE clause.  This ensures that we treat
   17992                 :              * "x COLLATE y" and "(x COLLATE y)" alike.
   17993                 :              */
   17994 GIC         125 :             while (IsA(expr, CollateExpr))
   17995 UIC           0 :                 expr = (Node *) ((CollateExpr *) expr)->arg;
   17996                 : 
   17997 GIC         125 :             if (IsA(expr, Var) &&
   17998               6 :                 ((Var *) expr)->varattno > 0)
   17999                 :             {
   18000 ECB             :                 /*
   18001                 :                  * User wrote "(column)" or "(column COLLATE something)".
   18002                 :                  * Treat it like simple attribute anyway.
   18003                 :                  */
   18004 GIC           3 :                 partattrs[attn] = ((Var *) expr)->varattno;
   18005 ECB             :             }
   18006                 :             else
   18007                 :             {
   18008 CBC         122 :                 Bitmapset  *expr_attrs = NULL;
   18009 ECB             :                 int         i;
   18010                 : 
   18011 GIC         122 :                 partattrs[attn] = 0;    /* marks the column as expression */
   18012             122 :                 *partexprs = lappend(*partexprs, expr);
   18013                 : 
   18014                 :                 /*
   18015                 :                  * Try to simplify the expression before checking for
   18016 ECB             :                  * mutability.  The main practical value of doing it in this
   18017                 :                  * order is that an inline-able SQL-language function will be
   18018                 :                  * accepted if its expansion is immutable, whether or not the
   18019                 :                  * function itself is marked immutable.
   18020                 :                  *
   18021                 :                  * Note that expression_planner does not change the passed in
   18022                 :                  * expression destructively and we have already saved the
   18023                 :                  * expression to be stored into the catalog above.
   18024                 :                  */
   18025 GIC         122 :                 expr = (Node *) expression_planner((Expr *) expr);
   18026                 : 
   18027 ECB             :                 /*
   18028                 :                  * Partition expression cannot contain mutable functions,
   18029                 :                  * because a given row must always map to the same partition
   18030                 :                  * as long as there is no change in the partition boundary
   18031                 :                  * structure.
   18032                 :                  */
   18033 CBC         122 :                 if (contain_mutable_functions(expr))
   18034 GIC           3 :                     ereport(ERROR,
   18035 ECB             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18036                 :                              errmsg("functions in partition key expression must be marked IMMUTABLE")));
   18037                 : 
   18038                 :                 /*
   18039                 :                  * transformPartitionSpec() should have already rejected
   18040                 :                  * subqueries, aggregates, window functions, and SRFs, based
   18041                 :                  * on the EXPR_KIND_ for partition expressions.
   18042                 :                  */
   18043                 : 
   18044                 :                 /*
   18045                 :                  * Cannot allow system column references, since that would
   18046                 :                  * make partition routing impossible: their values won't be
   18047                 :                  * known yet when we need to do that.
   18048                 :                  */
   18049 GIC         119 :                 pull_varattnos(expr, 1, &expr_attrs);
   18050 CBC         952 :                 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
   18051                 :                 {
   18052 GIC         833 :                     if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
   18053 ECB             :                                       expr_attrs))
   18054 LBC           0 :                         ereport(ERROR,
   18055                 :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18056 ECB             :                                  errmsg("partition key expressions cannot contain system column references")));
   18057                 :                 }
   18058                 : 
   18059                 :                 /*
   18060                 :                  * Generated columns cannot work: They are computed after
   18061                 :                  * BEFORE triggers, but partition routing is done before all
   18062                 :                  * triggers.
   18063                 :                  */
   18064 CBC         119 :                 i = -1;
   18065 GIC         264 :                 while ((i = bms_next_member(expr_attrs, i)) >= 0)
   18066 EUB             :                 {
   18067 GIC         148 :                     AttrNumber  attno = i + FirstLowInvalidHeapAttributeNumber;
   18068                 : 
   18069 GBC         148 :                     if (attno > 0 &&
   18070 CBC         145 :                         TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
   18071 GIC           3 :                         ereport(ERROR,
   18072                 :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18073                 :                                  errmsg("cannot use generated column in partition key"),
   18074                 :                                  errdetail("Column \"%s\" is a generated column.",
   18075                 :                                            get_attname(RelationGetRelid(rel), attno, false)),
   18076                 :                                  parser_errposition(pstate, pelem->location)));
   18077 ECB             :                 }
   18078                 : 
   18079                 :                 /*
   18080                 :                  * While it is not exactly *wrong* for a partition expression
   18081                 :                  * to be a constant, it seems better to reject such keys.
   18082                 :                  */
   18083 GIC         116 :                 if (IsA(expr, Const))
   18084               6 :                     ereport(ERROR,
   18085                 :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18086                 :                              errmsg("cannot use constant expression as partition key")));
   18087                 :             }
   18088                 :         }
   18089                 : 
   18090                 :         /*
   18091                 :          * Apply collation override if any
   18092                 :          */
   18093            2310 :         if (pelem->collation)
   18094 CBC          15 :             attcollation = get_collation_oid(pelem->collation, false);
   18095 ECB             : 
   18096                 :         /*
   18097                 :          * Check we have a collation iff it's a collatable type.  The only
   18098                 :          * expected failures here are (1) COLLATE applied to a noncollatable
   18099                 :          * type, or (2) partition expression had an unresolved collation. But
   18100                 :          * we might as well code this to be a complete consistency check.
   18101                 :          */
   18102 GIC        2310 :         if (type_is_collatable(atttype))
   18103                 :         {
   18104             283 :             if (!OidIsValid(attcollation))
   18105 LBC           0 :                 ereport(ERROR,
   18106 ECB             :                         (errcode(ERRCODE_INDETERMINATE_COLLATION),
   18107                 :                          errmsg("could not determine which collation to use for partition expression"),
   18108                 :                          errhint("Use the COLLATE clause to set the collation explicitly.")));
   18109                 :         }
   18110                 :         else
   18111                 :         {
   18112 GIC        2027 :             if (OidIsValid(attcollation))
   18113 UIC           0 :                 ereport(ERROR,
   18114                 :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18115                 :                          errmsg("collations are not supported by type %s",
   18116                 :                                 format_type_be(atttype))));
   18117                 :         }
   18118 ECB             : 
   18119 GIC        2310 :         partcollation[attn] = attcollation;
   18120                 : 
   18121                 :         /*
   18122                 :          * Identify the appropriate operator class.  For list and range
   18123                 :          * partitioning, we use a btree operator class; hash partitioning uses
   18124                 :          * a hash operator class.
   18125                 :          */
   18126            2310 :         if (strategy == PARTITION_STRATEGY_HASH)
   18127 CBC         131 :             am_oid = HASH_AM_OID;
   18128                 :         else
   18129            2179 :             am_oid = BTREE_AM_OID;
   18130                 : 
   18131            2310 :         if (!pelem->opclass)
   18132 ECB             :         {
   18133 GIC        2244 :             partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
   18134 ECB             : 
   18135 GIC        2244 :             if (!OidIsValid(partopclass[attn]))
   18136                 :             {
   18137               6 :                 if (strategy == PARTITION_STRATEGY_HASH)
   18138 LBC           0 :                     ereport(ERROR,
   18139                 :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
   18140                 :                              errmsg("data type %s has no default operator class for access method \"%s\"",
   18141                 :                                     format_type_be(atttype), "hash"),
   18142                 :                              errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
   18143                 :                 else
   18144 GIC           6 :                     ereport(ERROR,
   18145                 :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
   18146                 :                              errmsg("data type %s has no default operator class for access method \"%s\"",
   18147                 :                                     format_type_be(atttype), "btree"),
   18148                 :                              errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
   18149 ECB             :             }
   18150                 :         }
   18151                 :         else
   18152 GIC          66 :             partopclass[attn] = ResolveOpClass(pelem->opclass,
   18153 ECB             :                                                atttype,
   18154                 :                                                am_oid == HASH_AM_OID ? "hash" : "btree",
   18155                 :                                                am_oid);
   18156                 : 
   18157 CBC        2298 :         attn++;
   18158 ECB             :     }
   18159 GIC        2106 : }
   18160                 : 
   18161 ECB             : /*
   18162                 :  * PartConstraintImpliedByRelConstraint
   18163                 :  *      Do scanrel's existing constraints imply the partition constraint?
   18164                 :  *
   18165                 :  * "Existing constraints" include its check constraints and column-level
   18166                 :  * NOT NULL constraints.  partConstraint describes the partition constraint,
   18167                 :  * in implicit-AND form.
   18168                 :  */
   18169                 : bool
   18170 GIC        1442 : PartConstraintImpliedByRelConstraint(Relation scanrel,
   18171 ECB             :                                      List *partConstraint)
   18172                 : {
   18173 GIC        1442 :     List       *existConstraint = NIL;
   18174            1442 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   18175                 :     int         i;
   18176                 : 
   18177            1442 :     if (constr && constr->has_not_null)
   18178                 :     {
   18179             312 :         int         natts = scanrel->rd_att->natts;
   18180                 : 
   18181 CBC        1019 :         for (i = 1; i <= natts; i++)
   18182                 :         {
   18183 GIC         707 :             Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
   18184                 : 
   18185             707 :             if (att->attnotnull && !att->attisdropped)
   18186 ECB             :             {
   18187 GIC         414 :                 NullTest   *ntest = makeNode(NullTest);
   18188 EUB             : 
   18189 GIC         414 :                 ntest->arg = (Expr *) makeVar(1,
   18190 EUB             :                                               i,
   18191                 :                                               att->atttypid,
   18192                 :                                               att->atttypmod,
   18193                 :                                               att->attcollation,
   18194                 :                                               0);
   18195 GIC         414 :                 ntest->nulltesttype = IS_NOT_NULL;
   18196                 : 
   18197                 :                 /*
   18198                 :                  * argisrow=false is correct even for a composite column,
   18199 EUB             :                  * because attnotnull does not represent a SQL-spec IS NOT
   18200                 :                  * NULL test in such a case, just IS DISTINCT FROM NULL.
   18201                 :                  */
   18202 GBC         414 :                 ntest->argisrow = false;
   18203 GIC         414 :                 ntest->location = -1;
   18204             414 :                 existConstraint = lappend(existConstraint, ntest);
   18205 ECB             :             }
   18206                 :         }
   18207                 :     }
   18208                 : 
   18209 GIC        1442 :     return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
   18210                 : }
   18211                 : 
   18212                 : /*
   18213                 :  * ConstraintImpliedByRelConstraint
   18214                 :  *      Do scanrel's existing constraints imply the given constraint?
   18215                 :  *
   18216 ECB             :  * testConstraint is the constraint to validate. provenConstraint is a
   18217                 :  * caller-provided list of conditions which this function may assume
   18218                 :  * to be true. Both provenConstraint and testConstraint must be in
   18219                 :  * implicit-AND form, must only contain immutable clauses, and must
   18220                 :  * contain only Vars with varno = 1.
   18221                 :  */
   18222                 : bool
   18223 CBC        1995 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
   18224                 : {
   18225 GIC        1995 :     List       *existConstraint = list_copy(provenConstraint);
   18226            1995 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   18227                 :     int         num_check,
   18228                 :                 i;
   18229                 : 
   18230 CBC        1995 :     num_check = (constr != NULL) ? constr->num_check : 0;
   18231            2253 :     for (i = 0; i < num_check; i++)
   18232 EUB             :     {
   18233 ECB             :         Node       *cexpr;
   18234                 : 
   18235                 :         /*
   18236                 :          * If this constraint hasn't been fully validated yet, we must ignore
   18237                 :          * it here.
   18238                 :          */
   18239 GIC         258 :         if (!constr->check[i].ccvalid)
   18240 CBC           4 :             continue;
   18241 ECB             : 
   18242 CBC         254 :         cexpr = stringToNode(constr->check[i].ccbin);
   18243 ECB             : 
   18244                 :         /*
   18245                 :          * Run each expression through const-simplification and
   18246                 :          * canonicalization.  It is necessary, because we will be comparing it
   18247                 :          * to similarly-processed partition constraint expressions, and may
   18248                 :          * fail to detect valid matches without this.
   18249                 :          */
   18250 GIC         254 :         cexpr = eval_const_expressions(NULL, cexpr);
   18251 CBC         254 :         cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
   18252                 : 
   18253 GIC         254 :         existConstraint = list_concat(existConstraint,
   18254             254 :                                       make_ands_implicit((Expr *) cexpr));
   18255                 :     }
   18256 ECB             : 
   18257                 :     /*
   18258                 :      * Try to make the proof.  Since we are comparing CHECK constraints, we
   18259                 :      * need to use weak implication, i.e., we assume existConstraint is
   18260                 :      * not-false and try to prove the same for testConstraint.
   18261                 :      *
   18262                 :      * Note that predicate_implied_by assumes its first argument is known
   18263                 :      * immutable.  That should always be true for both NOT NULL and partition
   18264                 :      * constraints, so we don't test it here.
   18265                 :      */
   18266 CBC        1995 :     return predicate_implied_by(testConstraint, existConstraint, true);
   18267                 : }
   18268                 : 
   18269 ECB             : /*
   18270                 :  * QueuePartitionConstraintValidation
   18271                 :  *
   18272                 :  * Add an entry to wqueue to have the given partition constraint validated by
   18273                 :  * Phase 3, for the given relation, and all its children.
   18274                 :  *
   18275                 :  * We first verify whether the given constraint is implied by pre-existing
   18276                 :  * relation constraints; if it is, there's no need to scan the table to
   18277                 :  * validate, so don't queue in that case.
   18278                 :  */
   18279                 : static void
   18280 GIC        1139 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
   18281                 :                                    List *partConstraint,
   18282 ECB             :                                    bool validate_default)
   18283 EUB             : {
   18284                 :     /*
   18285 ECB             :      * Based on the table's existing constraints, determine whether or not we
   18286                 :      * may skip scanning the table.
   18287 EUB             :      */
   18288 GIC        1139 :     if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
   18289 ECB             :     {
   18290 CBC          45 :         if (!validate_default)
   18291 GIC          34 :             ereport(DEBUG1,
   18292 ECB             :                     (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
   18293                 :                                      RelationGetRelationName(scanrel))));
   18294                 :         else
   18295 GIC          11 :             ereport(DEBUG1,
   18296                 :                     (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
   18297                 :                                      RelationGetRelationName(scanrel))));
   18298              45 :         return;
   18299                 :     }
   18300 ECB             : 
   18301                 :     /*
   18302                 :      * Constraints proved insufficient. For plain relations, queue a
   18303                 :      * validation item now; for partitioned tables, recurse to process each
   18304                 :      * partition.
   18305                 :      */
   18306 CBC        1094 :     if (scanrel->rd_rel->relkind == RELKIND_RELATION)
   18307 ECB             :     {
   18308                 :         AlteredTableInfo *tab;
   18309                 : 
   18310                 :         /* Grab a work queue entry. */
   18311 GBC         909 :         tab = ATGetQueueEntry(wqueue, scanrel);
   18312 GIC         909 :         Assert(tab->partition_constraint == NULL);
   18313 CBC         909 :         tab->partition_constraint = (Expr *) linitial(partConstraint);
   18314             909 :         tab->validate_default = validate_default;
   18315 ECB             :     }
   18316 GIC         185 :     else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   18317 ECB             :     {
   18318 CBC         161 :         PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
   18319 ECB             :         int         i;
   18320                 : 
   18321 GIC         334 :         for (i = 0; i < partdesc->nparts; i++)
   18322                 :         {
   18323                 :             Relation    part_rel;
   18324 ECB             :             List       *thisPartConstraint;
   18325                 : 
   18326                 :             /*
   18327                 :              * This is the minimum lock we need to prevent deadlocks.
   18328                 :              */
   18329 GIC         173 :             part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
   18330                 : 
   18331                 :             /*
   18332 ECB             :              * Adjust the constraint for scanrel so that it matches this
   18333                 :              * partition's attribute numbers.
   18334                 :              */
   18335                 :             thisPartConstraint =
   18336 GIC         173 :                 map_partition_varattnos(partConstraint, 1,
   18337                 :                                         part_rel, scanrel);
   18338                 : 
   18339             173 :             QueuePartitionConstraintValidation(wqueue, part_rel,
   18340                 :                                                thisPartConstraint,
   18341                 :                                                validate_default);
   18342 CBC         173 :             table_close(part_rel, NoLock);  /* keep lock till commit */
   18343 ECB             :         }
   18344                 :     }
   18345                 : }
   18346                 : 
   18347                 : /*
   18348                 :  * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
   18349                 :  *
   18350                 :  * Return the address of the newly attached partition.
   18351                 :  */
   18352                 : static ObjectAddress
   18353 CBC        1034 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
   18354 ECB             :                       AlterTableUtilityContext *context)
   18355                 : {
   18356                 :     Relation    attachrel,
   18357                 :                 catalog;
   18358                 :     List       *attachrel_children;
   18359                 :     List       *partConstraint;
   18360                 :     SysScanDesc scan;
   18361                 :     ScanKeyData skey;
   18362                 :     AttrNumber  attno;
   18363                 :     int         natts;
   18364                 :     TupleDesc   tupleDesc;
   18365                 :     ObjectAddress address;
   18366                 :     const char *trigger_name;
   18367                 :     Oid         defaultPartOid;
   18368                 :     List       *partBoundConstraint;
   18369 CBC        1034 :     ParseState *pstate = make_parsestate(NULL);
   18370 EUB             : 
   18371 GBC        1034 :     pstate->p_sourcetext = context->queryString;
   18372 ECB             : 
   18373                 :     /*
   18374                 :      * We must lock the default partition if one exists, because attaching a
   18375                 :      * new partition will change its partition constraint.
   18376                 :      */
   18377                 :     defaultPartOid =
   18378 CBC        1034 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   18379 GIC        1034 :     if (OidIsValid(defaultPartOid))
   18380              91 :         LockRelationOid(defaultPartOid, AccessExclusiveLock);
   18381 EUB             : 
   18382 GIC        1034 :     attachrel = table_openrv(cmd->name, AccessExclusiveLock);
   18383                 : 
   18384                 :     /*
   18385                 :      * XXX I think it'd be a good idea to grab locks on all tables referenced
   18386                 :      * by FKs at this point also.
   18387                 :      */
   18388                 : 
   18389                 :     /*
   18390                 :      * Must be owner of both parent and source table -- parent was checked by
   18391                 :      * ATSimplePermissions call in ATPrepCmd
   18392 ECB             :      */
   18393 GBC        1031 :     ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
   18394                 : 
   18395                 :     /* A partition can only have one parent */
   18396 GIC        1028 :     if (attachrel->rd_rel->relispartition)
   18397 CBC           3 :         ereport(ERROR,
   18398 EUB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18399                 :                  errmsg("\"%s\" is already a partition",
   18400                 :                         RelationGetRelationName(attachrel))));
   18401                 : 
   18402 CBC        1025 :     if (OidIsValid(attachrel->rd_rel->reloftype))
   18403 GBC           3 :         ereport(ERROR,
   18404                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18405                 :                  errmsg("cannot attach a typed table as partition")));
   18406                 : 
   18407 ECB             :     /*
   18408 EUB             :      * Table being attached should not already be part of inheritance; either
   18409                 :      * as a child table...
   18410                 :      */
   18411 GIC        1022 :     catalog = table_open(InheritsRelationId, AccessShareLock);
   18412 CBC        1022 :     ScanKeyInit(&skey,
   18413 EUB             :                 Anum_pg_inherits_inhrelid,
   18414                 :                 BTEqualStrategyNumber, F_OIDEQ,
   18415                 :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   18416 GIC        1022 :     scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
   18417 ECB             :                               NULL, 1, &skey);
   18418 GIC        1022 :     if (HeapTupleIsValid(systable_getnext(scan)))
   18419 CBC           3 :         ereport(ERROR,
   18420 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18421                 :                  errmsg("cannot attach inheritance child as partition")));
   18422 GIC        1019 :     systable_endscan(scan);
   18423                 : 
   18424                 :     /* ...or as a parent table (except the case when it is partitioned) */
   18425            1019 :     ScanKeyInit(&skey,
   18426                 :                 Anum_pg_inherits_inhparent,
   18427                 :                 BTEqualStrategyNumber, F_OIDEQ,
   18428 ECB             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   18429 GBC        1019 :     scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
   18430                 :                               1, &skey);
   18431 GIC        1019 :     if (HeapTupleIsValid(systable_getnext(scan)) &&
   18432             112 :         attachrel->rd_rel->relkind == RELKIND_RELATION)
   18433               3 :         ereport(ERROR,
   18434                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18435                 :                  errmsg("cannot attach inheritance parent as partition")));
   18436            1016 :     systable_endscan(scan);
   18437            1016 :     table_close(catalog, AccessShareLock);
   18438 ECB             : 
   18439                 :     /*
   18440                 :      * Prevent circularity by seeing if rel is a partition of attachrel. (In
   18441 EUB             :      * particular, this disallows making a rel a partition of itself.)
   18442                 :      *
   18443                 :      * We do that by checking if rel is a member of the list of attachrel's
   18444                 :      * partitions provided the latter is partitioned at all.  We want to avoid
   18445                 :      * having to construct this list again, so we request the strongest lock
   18446 ECB             :      * on all partitions.  We need the strongest lock, because we may decide
   18447 EUB             :      * to scan them if we find out that the table being attached (or its leaf
   18448                 :      * partitions) may contain rows that violate the partition constraint. If
   18449                 :      * the table has a constraint that would prevent such rows, which by
   18450                 :      * definition is present in all the partitions, we need not scan the
   18451                 :      * table, nor its partitions.  But we cannot risk a deadlock by taking a
   18452 ECB             :      * weaker lock now and the stronger one only when needed.
   18453 EUB             :      */
   18454 GIC        1016 :     attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
   18455                 :                                              AccessExclusiveLock, NULL);
   18456            1016 :     if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
   18457               6 :         ereport(ERROR,
   18458                 :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   18459                 :                  errmsg("circular inheritance not allowed"),
   18460 ECB             :                  errdetail("\"%s\" is already a child of \"%s\".",
   18461                 :                            RelationGetRelationName(rel),
   18462                 :                            RelationGetRelationName(attachrel))));
   18463                 : 
   18464                 :     /* If the parent is permanent, so must be all of its partitions. */
   18465 GIC        1010 :     if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
   18466             998 :         attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
   18467               3 :         ereport(ERROR,
   18468                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18469 ECB             :                  errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
   18470                 :                         RelationGetRelationName(rel))));
   18471                 : 
   18472                 :     /* Temp parent cannot have a partition that is itself not a temp */
   18473 GIC        1007 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   18474              12 :         attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   18475               9 :         ereport(ERROR,
   18476 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18477                 :                  errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
   18478                 :                         RelationGetRelationName(rel))));
   18479                 : 
   18480                 :     /* If the parent is temp, it must belong to this session */
   18481 GIC         998 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   18482               3 :         !rel->rd_islocaltemp)
   18483 LBC           0 :         ereport(ERROR,
   18484 ECB             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18485                 :                  errmsg("cannot attach as partition of temporary relation of another session")));
   18486                 : 
   18487                 :     /* Ditto for the partition */
   18488 GIC         998 :     if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   18489               3 :         !attachrel->rd_islocaltemp)
   18490 UIC           0 :         ereport(ERROR,
   18491                 :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18492                 :                  errmsg("cannot attach temporary relation of another session as partition")));
   18493 ECB             : 
   18494                 :     /* Check if there are any columns in attachrel that aren't in the parent */
   18495 GIC         998 :     tupleDesc = RelationGetDescr(attachrel);
   18496 CBC         998 :     natts = tupleDesc->natts;
   18497 GIC        3427 :     for (attno = 1; attno <= natts; attno++)
   18498                 :     {
   18499 CBC        2438 :         Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
   18500 GIC        2438 :         char       *attributeName = NameStr(attribute->attname);
   18501 ECB             : 
   18502                 :         /* Ignore dropped */
   18503 CBC        2438 :         if (attribute->attisdropped)
   18504 GIC         278 :             continue;
   18505                 : 
   18506 ECB             :         /* Try to find the column in parent (matching on column name) */
   18507 GIC        2160 :         if (!SearchSysCacheExists2(ATTNAME,
   18508                 :                                    ObjectIdGetDatum(RelationGetRelid(rel)),
   18509 ECB             :                                    CStringGetDatum(attributeName)))
   18510 GIC           9 :             ereport(ERROR,
   18511                 :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   18512                 :                      errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
   18513 ECB             :                             RelationGetRelationName(attachrel), attributeName,
   18514                 :                             RelationGetRelationName(rel)),
   18515                 :                      errdetail("The new partition may contain only the columns present in parent.")));
   18516                 :     }
   18517                 : 
   18518                 :     /*
   18519                 :      * If child_rel has row-level triggers with transition tables, we
   18520                 :      * currently don't allow it to become a partition.  See also prohibitions
   18521                 :      * in ATExecAddInherit() and CreateTrigger().
   18522                 :      */
   18523 GIC         989 :     trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
   18524             989 :     if (trigger_name != NULL)
   18525               3 :         ereport(ERROR,
   18526                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   18527 ECB             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
   18528                 :                         trigger_name, RelationGetRelationName(attachrel)),
   18529                 :                  errdetail("ROW triggers with transition tables are not supported on partitions.")));
   18530                 : 
   18531                 :     /*
   18532                 :      * Check that the new partition's bound is valid and does not overlap any
   18533                 :      * of existing partitions of the parent - note that it does not return on
   18534                 :      * error.
   18535                 :      */
   18536 CBC         986 :     check_new_partition_bound(RelationGetRelationName(attachrel), rel,
   18537                 :                               cmd->bound, pstate);
   18538 ECB             : 
   18539                 :     /* OK to create inheritance.  Rest of the checks performed there */
   18540 GIC         968 :     CreateInheritance(attachrel, rel);
   18541                 : 
   18542 ECB             :     /* Update the pg_class entry. */
   18543 GIC         935 :     StorePartitionBound(attachrel, rel, cmd->bound);
   18544                 : 
   18545                 :     /* Ensure there exists a correct set of indexes in the partition. */
   18546 GNC         935 :     AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
   18547                 : 
   18548 ECB             :     /* and triggers */
   18549 CBC         920 :     CloneRowTriggersToPartition(rel, attachrel);
   18550 ECB             : 
   18551                 :     /*
   18552                 :      * Clone foreign key constraints.  Callee is responsible for setting up
   18553                 :      * for phase 3 constraint verification.
   18554                 :      */
   18555 GIC         917 :     CloneForeignKeyConstraints(wqueue, rel, attachrel);
   18556 ECB             : 
   18557                 :     /*
   18558                 :      * Generate partition constraint from the partition bound specification.
   18559                 :      * If the parent itself is a partition, make sure to include its
   18560                 :      * constraint as well.
   18561                 :      */
   18562 GIC         917 :     partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
   18563             917 :     partConstraint = list_concat(partBoundConstraint,
   18564             917 :                                  RelationGetPartitionQual(rel));
   18565                 : 
   18566                 :     /* Skip validation if there are no constraints to validate. */
   18567             917 :     if (partConstraint)
   18568                 :     {
   18569 ECB             :         /*
   18570                 :          * Run the partition quals through const-simplification similar to
   18571                 :          * check constraints.  We skip canonicalize_qual, though, because
   18572                 :          * partition quals should be in canonical form already.
   18573                 :          */
   18574                 :         partConstraint =
   18575 GIC         893 :             (List *) eval_const_expressions(NULL,
   18576                 :                                             (Node *) partConstraint);
   18577 ECB             : 
   18578                 :         /* XXX this sure looks wrong */
   18579 CBC         893 :         partConstraint = list_make1(make_ands_explicit(partConstraint));
   18580 ECB             : 
   18581                 :         /*
   18582                 :          * Adjust the generated constraint to match this partition's attribute
   18583                 :          * numbers.
   18584                 :          */
   18585 CBC         893 :         partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
   18586                 :                                                  rel);
   18587                 : 
   18588 ECB             :         /* Validate partition constraints against the table being attached. */
   18589 CBC         893 :         QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
   18590 ECB             :                                            false);
   18591                 :     }
   18592                 : 
   18593                 :     /*
   18594                 :      * If we're attaching a partition other than the default partition and a
   18595                 :      * default one exists, then that partition's partition constraint changes,
   18596                 :      * so add an entry to the work queue to validate it, too.  (We must not do
   18597                 :      * this when the partition being attached is the default one; we already
   18598                 :      * did it above!)
   18599                 :      */
   18600 GIC         917 :     if (OidIsValid(defaultPartOid))
   18601                 :     {
   18602                 :         Relation    defaultrel;
   18603                 :         List       *defPartConstraint;
   18604                 : 
   18605              73 :         Assert(!cmd->bound->is_default);
   18606                 : 
   18607 ECB             :         /* we already hold a lock on the default partition */
   18608 GBC          73 :         defaultrel = table_open(defaultPartOid, NoLock);
   18609                 :         defPartConstraint =
   18610 CBC          73 :             get_proposed_default_constraint(partBoundConstraint);
   18611 ECB             : 
   18612                 :         /*
   18613                 :          * Map the Vars in the constraint expression from rel's attnos to
   18614                 :          * defaultrel's.
   18615                 :          */
   18616                 :         defPartConstraint =
   18617 CBC          73 :             map_partition_varattnos(defPartConstraint,
   18618                 :                                     1, defaultrel, rel);
   18619 GIC          73 :         QueuePartitionConstraintValidation(wqueue, defaultrel,
   18620                 :                                            defPartConstraint, true);
   18621 ECB             : 
   18622                 :         /* keep our lock until commit. */
   18623 GIC          73 :         table_close(defaultrel, NoLock);
   18624 ECB             :     }
   18625                 : 
   18626 GIC         917 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
   18627                 : 
   18628                 :     /*
   18629                 :      * If the partition we just attached is partitioned itself, invalidate
   18630                 :      * relcache for all descendent partitions too to ensure that their
   18631                 :      * rd_partcheck expression trees are rebuilt; partitions already locked at
   18632                 :      * the beginning of this function.
   18633                 :      */
   18634             917 :     if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   18635                 :     {
   18636                 :         ListCell   *l;
   18637                 : 
   18638 CBC         450 :         foreach(l, attachrel_children)
   18639                 :         {
   18640 GIC         301 :             CacheInvalidateRelcacheByRelid(lfirst_oid(l));
   18641                 :         }
   18642                 :     }
   18643                 : 
   18644                 :     /* keep our lock until commit */
   18645             917 :     table_close(attachrel, NoLock);
   18646 ECB             : 
   18647 CBC         917 :     return address;
   18648                 : }
   18649                 : 
   18650                 : /*
   18651                 :  * AttachPartitionEnsureIndexes
   18652                 :  *      subroutine for ATExecAttachPartition to create/match indexes
   18653                 :  *
   18654                 :  * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
   18655                 :  * PARTITION: every partition must have an index attached to each index on the
   18656                 :  * partitioned table.
   18657                 :  */
   18658                 : static void
   18659 GNC         935 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
   18660                 : {
   18661                 :     List       *idxes;
   18662 ECB             :     List       *attachRelIdxs;
   18663                 :     Relation   *attachrelIdxRels;
   18664                 :     IndexInfo **attachInfos;
   18665                 :     ListCell   *cell;
   18666 EUB             :     MemoryContext cxt;
   18667                 :     MemoryContext oldcxt;
   18668                 : 
   18669 GIC         935 :     cxt = AllocSetContextCreate(CurrentMemoryContext,
   18670                 :                                 "AttachPartitionEnsureIndexes",
   18671                 :                                 ALLOCSET_DEFAULT_SIZES);
   18672             935 :     oldcxt = MemoryContextSwitchTo(cxt);
   18673                 : 
   18674             935 :     idxes = RelationGetIndexList(rel);
   18675             935 :     attachRelIdxs = RelationGetIndexList(attachrel);
   18676 CBC         935 :     attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
   18677             935 :     attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
   18678                 : 
   18679 ECB             :     /* Build arrays of all existing indexes and their IndexInfos */
   18680 CBC        1084 :     foreach(cell, attachRelIdxs)
   18681 ECB             :     {
   18682 CBC         149 :         Oid         cldIdxId = lfirst_oid(cell);
   18683 GNC         149 :         int         i = foreach_current_index(cell);
   18684                 : 
   18685 GIC         149 :         attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
   18686             149 :         attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
   18687                 :     }
   18688                 : 
   18689                 :     /*
   18690                 :      * If we're attaching a foreign table, we must fail if any of the indexes
   18691                 :      * is a constraint index; otherwise, there's nothing to do here.  Do this
   18692                 :      * before starting work, to avoid wasting the effort of building a few
   18693                 :      * non-unique indexes before coming across a unique one.
   18694 ECB             :      */
   18695 CBC         935 :     if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   18696                 :     {
   18697 GIC          43 :         foreach(cell, idxes)
   18698                 :         {
   18699              18 :             Oid         idx = lfirst_oid(cell);
   18700              18 :             Relation    idxRel = index_open(idx, AccessShareLock);
   18701                 : 
   18702              18 :             if (idxRel->rd_index->indisunique ||
   18703              12 :                 idxRel->rd_index->indisprimary)
   18704 CBC           6 :                 ereport(ERROR,
   18705 ECB             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18706                 :                          errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
   18707                 :                                 RelationGetRelationName(attachrel),
   18708                 :                                 RelationGetRelationName(rel)),
   18709                 :                          errdetail("Partitioned table \"%s\" contains unique indexes.",
   18710                 :                                    RelationGetRelationName(rel))));
   18711 GIC          12 :             index_close(idxRel, AccessShareLock);
   18712                 :         }
   18713 ECB             : 
   18714 GIC          25 :         goto out;
   18715 ECB             :     }
   18716 EUB             : 
   18717                 :     /*
   18718                 :      * For each index on the partitioned table, find a matching one in the
   18719                 :      * partition-to-be; if one is not found, create one.
   18720                 :      */
   18721 GIC        1088 :     foreach(cell, idxes)
   18722                 :     {
   18723 CBC         193 :         Oid         idx = lfirst_oid(cell);
   18724 GBC         193 :         Relation    idxRel = index_open(idx, AccessShareLock);
   18725                 :         IndexInfo  *info;
   18726                 :         AttrMap    *attmap;
   18727 GIC         193 :         bool        found = false;
   18728                 :         Oid         constraintOid;
   18729                 : 
   18730 ECB             :         /*
   18731                 :          * Ignore indexes in the partitioned table other than partitioned
   18732                 :          * indexes.
   18733                 :          */
   18734 GIC         193 :         if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   18735                 :         {
   18736 UIC           0 :             index_close(idxRel, AccessShareLock);
   18737 LBC           0 :             continue;
   18738 ECB             :         }
   18739                 : 
   18740                 :         /* construct an indexinfo to compare existing indexes against */
   18741 GIC         193 :         info = BuildIndexInfo(idxRel);
   18742 CBC         193 :         attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
   18743                 :                                        RelationGetDescr(rel),
   18744                 :                                        false);
   18745             193 :         constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
   18746                 : 
   18747 ECB             :         /*
   18748                 :          * Scan the list of existing indexes in the partition-to-be, and mark
   18749                 :          * the first matching, unattached one we find, if any, as partition of
   18750 EUB             :          * the parent index.  If we find one, we're done.
   18751                 :          */
   18752 GNC         220 :         for (int i = 0; i < list_length(attachRelIdxs); i++)
   18753                 :         {
   18754 GIC         110 :             Oid         cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
   18755             110 :             Oid         cldConstrOid = InvalidOid;
   18756 ECB             : 
   18757                 :             /* does this index have a parent?  if so, can't use it */
   18758 GIC         110 :             if (attachrelIdxRels[i]->rd_rel->relispartition)
   18759               6 :                 continue;
   18760                 : 
   18761             104 :             if (CompareIndexInfo(attachInfos[i], info,
   18762             104 :                                  attachrelIdxRels[i]->rd_indcollation,
   18763                 :                                  idxRel->rd_indcollation,
   18764 CBC         104 :                                  attachrelIdxRels[i]->rd_opfamily,
   18765                 :                                  idxRel->rd_opfamily,
   18766                 :                                  attmap))
   18767                 :             {
   18768                 :                 /*
   18769 ECB             :                  * If this index is being created in the parent because of a
   18770                 :                  * constraint, then the child needs to have a constraint also,
   18771                 :                  * so look for one.  If there is no such constraint, this
   18772                 :                  * index is no good, so keep looking.
   18773                 :                  */
   18774 GIC          86 :                 if (OidIsValid(constraintOid))
   18775                 :                 {
   18776                 :                     cldConstrOid =
   18777              43 :                         get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
   18778                 :                                                         cldIdxId);
   18779                 :                     /* no dice */
   18780              43 :                     if (!OidIsValid(cldConstrOid))
   18781               3 :                         continue;
   18782 ECB             :                 }
   18783                 : 
   18784                 :                 /* bingo. */
   18785 CBC          83 :                 IndexSetParentIndex(attachrelIdxRels[i], idx);
   18786              83 :                 if (OidIsValid(constraintOid))
   18787 GIC          40 :                     ConstraintSetParentConstraint(cldConstrOid, constraintOid,
   18788                 :                                                   RelationGetRelid(attachrel));
   18789 CBC          83 :                 found = true;
   18790                 : 
   18791              83 :                 CommandCounterIncrement();
   18792 GIC          83 :                 break;
   18793 ECB             :             }
   18794                 :         }
   18795                 : 
   18796                 :         /*
   18797                 :          * If no suitable index was found in the partition-to-be, create one
   18798                 :          * now.
   18799                 :          */
   18800 GIC         193 :         if (!found)
   18801 ECB             :         {
   18802                 :             IndexStmt  *stmt;
   18803                 :             Oid         conOid;
   18804                 : 
   18805 GIC         110 :             stmt = generateClonedIndexStmt(NULL,
   18806                 :                                            idxRel, attmap,
   18807                 :                                            &conOid);
   18808                 : 
   18809                 :             /*
   18810                 :              * If the index is a primary key, mark all columns as NOT NULL if
   18811                 :              * they aren't already.
   18812                 :              */
   18813 GNC         110 :             if (stmt->primary)
   18814                 :             {
   18815              59 :                 MemoryContextSwitchTo(oldcxt);
   18816             124 :                 for (int j = 0; j < info->ii_NumIndexKeyAttrs; j++)
   18817                 :                 {
   18818                 :                     AttrNumber  childattno;
   18819                 : 
   18820              65 :                     childattno = get_attnum(RelationGetRelid(attachrel),
   18821              65 :                                             get_attname(RelationGetRelid(rel),
   18822              65 :                                                         info->ii_IndexAttrNumbers[j],
   18823                 :                                                         false));
   18824              65 :                     set_attnotnull(wqueue, attachrel, childattno,
   18825                 :                                    true, AccessExclusiveLock);
   18826                 :                 }
   18827              59 :                 MemoryContextSwitchTo(cxt);
   18828                 :             }
   18829                 : 
   18830 GIC         110 :             DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
   18831                 :                         RelationGetRelid(idxRel),
   18832                 :                         conOid,
   18833                 :                         -1,
   18834                 :                         true, false, false, false, false);
   18835                 :         }
   18836                 : 
   18837 CBC         184 :         index_close(idxRel, AccessShareLock);
   18838 ECB             :     }
   18839                 : 
   18840 GIC         920 : out:
   18841                 :     /* Clean up. */
   18842 GNC        1063 :     for (int i = 0; i < list_length(attachRelIdxs); i++)
   18843 GIC         143 :         index_close(attachrelIdxRels[i], AccessShareLock);
   18844 CBC         920 :     MemoryContextSwitchTo(oldcxt);
   18845 GIC         920 :     MemoryContextDelete(cxt);
   18846             920 : }
   18847                 : 
   18848                 : /*
   18849                 :  * CloneRowTriggersToPartition
   18850                 :  *      subroutine for ATExecAttachPartition/DefineRelation to create row
   18851                 :  *      triggers on partitions
   18852                 :  */
   18853                 : static void
   18854            1108 : CloneRowTriggersToPartition(Relation parent, Relation partition)
   18855                 : {
   18856                 :     Relation    pg_trigger;
   18857                 :     ScanKeyData key;
   18858 ECB             :     SysScanDesc scan;
   18859                 :     HeapTuple   tuple;
   18860                 :     MemoryContext perTupCxt;
   18861                 : 
   18862 GIC        1108 :     ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   18863                 :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
   18864            1108 :     pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
   18865 CBC        1108 :     scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
   18866 ECB             :                               true, NULL, 1, &key);
   18867                 : 
   18868 GIC        1108 :     perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   18869                 :                                       "clone trig", ALLOCSET_SMALL_SIZES);
   18870                 : 
   18871            1788 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   18872                 :     {
   18873             683 :         Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
   18874 ECB             :         CreateTrigStmt *trigStmt;
   18875 CBC         683 :         Node       *qual = NULL;
   18876                 :         Datum       value;
   18877 ECB             :         bool        isnull;
   18878 GIC         683 :         List       *cols = NIL;
   18879             683 :         List       *trigargs = NIL;
   18880                 :         MemoryContext oldcxt;
   18881                 : 
   18882                 :         /*
   18883                 :          * Ignore statement-level triggers; those are not cloned.
   18884                 :          */
   18885 CBC         683 :         if (!TRIGGER_FOR_ROW(trigForm->tgtype))
   18886             605 :             continue;
   18887                 : 
   18888 ECB             :         /*
   18889                 :          * Don't clone internal triggers, because the constraint cloning code
   18890                 :          * will.
   18891                 :          */
   18892 GIC         683 :         if (trigForm->tgisinternal)
   18893             605 :             continue;
   18894                 : 
   18895                 :         /*
   18896                 :          * Complain if we find an unexpected trigger type.
   18897                 :          */
   18898              78 :         if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
   18899              69 :             !TRIGGER_FOR_AFTER(trigForm->tgtype))
   18900 UIC           0 :             elog(ERROR, "unexpected trigger \"%s\" found",
   18901 ECB             :                  NameStr(trigForm->tgname));
   18902                 : 
   18903                 :         /* Use short-lived context for CREATE TRIGGER */
   18904 GIC          78 :         oldcxt = MemoryContextSwitchTo(perTupCxt);
   18905                 : 
   18906                 :         /*
   18907                 :          * If there is a WHEN clause, generate a 'cooked' version of it that's
   18908                 :          * appropriate for the partition.
   18909                 :          */
   18910              78 :         value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
   18911                 :                              RelationGetDescr(pg_trigger), &isnull);
   18912              78 :         if (!isnull)
   18913                 :         {
   18914               3 :             qual = stringToNode(TextDatumGetCString(value));
   18915 CBC           3 :             qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
   18916                 :                                                     partition, parent);
   18917 GIC           3 :             qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
   18918                 :                                                     partition, parent);
   18919                 :         }
   18920                 : 
   18921                 :         /*
   18922                 :          * If there is a column list, transform it to a list of column names.
   18923 ECB             :          * Note we don't need to map this list in any way ...
   18924                 :          */
   18925 CBC          78 :         if (trigForm->tgattr.dim1 > 0)
   18926 ECB             :         {
   18927                 :             int         i;
   18928                 : 
   18929 GIC           6 :             for (i = 0; i < trigForm->tgattr.dim1; i++)
   18930 ECB             :             {
   18931                 :                 Form_pg_attribute col;
   18932                 : 
   18933 CBC           3 :                 col = TupleDescAttr(parent->rd_att,
   18934                 :                                     trigForm->tgattr.values[i] - 1);
   18935 GIC           3 :                 cols = lappend(cols,
   18936               3 :                                makeString(pstrdup(NameStr(col->attname))));
   18937                 :             }
   18938                 :         }
   18939                 : 
   18940                 :         /* Reconstruct trigger arguments list. */
   18941 CBC          78 :         if (trigForm->tgnargs > 0)
   18942                 :         {
   18943                 :             char       *p;
   18944                 : 
   18945 GIC           6 :             value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
   18946 ECB             :                                  RelationGetDescr(pg_trigger), &isnull);
   18947 CBC           6 :             if (isnull)
   18948 LBC           0 :                 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
   18949 ECB             :                      NameStr(trigForm->tgname), RelationGetRelationName(partition));
   18950                 : 
   18951 CBC           6 :             p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
   18952                 : 
   18953              18 :             for (int i = 0; i < trigForm->tgnargs; i++)
   18954                 :             {
   18955 GIC          12 :                 trigargs = lappend(trigargs, makeString(pstrdup(p)));
   18956 CBC          12 :                 p += strlen(p) + 1;
   18957                 :             }
   18958                 :         }
   18959                 : 
   18960 GIC          78 :         trigStmt = makeNode(CreateTrigStmt);
   18961              78 :         trigStmt->replace = false;
   18962              78 :         trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
   18963              78 :         trigStmt->trigname = NameStr(trigForm->tgname);
   18964 CBC          78 :         trigStmt->relation = NULL;
   18965 GIC          78 :         trigStmt->funcname = NULL;   /* passed separately */
   18966              78 :         trigStmt->args = trigargs;
   18967              78 :         trigStmt->row = true;
   18968              78 :         trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
   18969              78 :         trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
   18970              78 :         trigStmt->columns = cols;
   18971 CBC          78 :         trigStmt->whenClause = NULL; /* passed separately */
   18972 GIC          78 :         trigStmt->transitionRels = NIL; /* not supported at present */
   18973              78 :         trigStmt->deferrable = trigForm->tgdeferrable;
   18974 CBC          78 :         trigStmt->initdeferred = trigForm->tginitdeferred;
   18975 GIC          78 :         trigStmt->constrrel = NULL; /* passed separately */
   18976                 : 
   18977 CBC          78 :         CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
   18978                 :                               trigForm->tgconstrrelid, InvalidOid, InvalidOid,
   18979                 :                               trigForm->tgfoid, trigForm->oid, qual,
   18980 GIC          78 :                               false, true, trigForm->tgenabled);
   18981                 : 
   18982              75 :         MemoryContextSwitchTo(oldcxt);
   18983              75 :         MemoryContextReset(perTupCxt);
   18984                 :     }
   18985                 : 
   18986            1105 :     MemoryContextDelete(perTupCxt);
   18987                 : 
   18988 CBC        1105 :     systable_endscan(scan);
   18989 GIC        1105 :     table_close(pg_trigger, RowExclusiveLock);
   18990            1105 : }
   18991                 : 
   18992                 : /*
   18993                 :  * ALTER TABLE DETACH PARTITION
   18994                 :  *
   18995                 :  * Return the address of the relation that is no longer a partition of rel.
   18996                 :  *
   18997                 :  * If concurrent mode is requested, we run in two transactions.  A side-
   18998                 :  * effect is that this command cannot run in a multi-part ALTER TABLE.
   18999                 :  * Currently, that's enforced by the grammar.
   19000                 :  *
   19001                 :  * The strategy for concurrency is to first modify the partition's
   19002                 :  * pg_inherit catalog row to make it visible to everyone that the
   19003                 :  * partition is detached, lock the partition against writes, and commit
   19004 ECB             :  * the transaction; anyone who requests the partition descriptor from
   19005                 :  * that point onwards has to ignore such a partition.  In a second
   19006                 :  * transaction, we wait until all transactions that could have seen the
   19007                 :  * partition as attached are gone, then we remove the rest of partition
   19008                 :  * metadata (pg_inherits and pg_class.relpartbounds).
   19009                 :  */
   19010                 : static ObjectAddress
   19011 GIC         252 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
   19012                 :                       RangeVar *name, bool concurrent)
   19013 ECB             : {
   19014                 :     Relation    partRel;
   19015                 :     ObjectAddress address;
   19016                 :     Oid         defaultPartOid;
   19017                 : 
   19018                 :     /*
   19019                 :      * We must lock the default partition, because detaching this partition
   19020                 :      * will change its partition constraint.
   19021                 :      */
   19022                 :     defaultPartOid =
   19023 GIC         252 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   19024             252 :     if (OidIsValid(defaultPartOid))
   19025                 :     {
   19026                 :         /*
   19027                 :          * Concurrent detaching when a default partition exists is not
   19028 ECB             :          * supported. The main problem is that the default partition
   19029                 :          * constraint would change.  And there's a definitional problem: what
   19030                 :          * should happen to the tuples that are being inserted that belong to
   19031                 :          * the partition being detached?  Putting them on the partition being
   19032                 :          * detached would be wrong, since they'd become "lost" after the
   19033                 :          * detaching completes but we cannot put them in the default partition
   19034                 :          * either until we alter its partition constraint.
   19035                 :          *
   19036                 :          * I think we could solve this problem if we effected the constraint
   19037                 :          * change before committing the first transaction.  But the lock would
   19038                 :          * have to remain AEL and it would cause concurrent query planning to
   19039                 :          * be blocked, so changing it that way would be even worse.
   19040                 :          */
   19041 GIC          53 :         if (concurrent)
   19042               6 :             ereport(ERROR,
   19043                 :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19044                 :                      errmsg("cannot detach partitions concurrently when a default partition exists")));
   19045              47 :         LockRelationOid(defaultPartOid, AccessExclusiveLock);
   19046 ECB             :     }
   19047                 : 
   19048                 :     /*
   19049                 :      * In concurrent mode, the partition is locked with share-update-exclusive
   19050                 :      * in the first transaction.  This allows concurrent transactions to be
   19051                 :      * doing DML to the partition.
   19052                 :      */
   19053 CBC         246 :     partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
   19054 ECB             :                            AccessExclusiveLock);
   19055                 : 
   19056                 :     /*
   19057                 :      * Check inheritance conditions and either delete the pg_inherits row (in
   19058                 :      * non-concurrent mode) or just set the inhdetachpending flag.
   19059                 :      */
   19060 CBC         240 :     if (!concurrent)
   19061 GIC         167 :         RemoveInheritance(partRel, rel, false);
   19062                 :     else
   19063              73 :         MarkInheritDetached(partRel, rel);
   19064 ECB             : 
   19065                 :     /*
   19066                 :      * Ensure that foreign keys still hold after this detach.  This keeps
   19067                 :      * locks on the referencing tables, which prevents concurrent transactions
   19068                 :      * from adding rows that we wouldn't see.  For this to work in concurrent
   19069                 :      * mode, it is critical that the partition appears as no longer attached
   19070                 :      * for the RI queries as soon as the first transaction commits.
   19071                 :      */
   19072 CBC         230 :     ATDetachCheckNoForeignKeyRefs(partRel);
   19073                 : 
   19074                 :     /*
   19075                 :      * Concurrent mode has to work harder; first we add a new constraint to
   19076                 :      * the partition that matches the partition constraint.  Then we close our
   19077                 :      * existing transaction, and in a new one wait for all processes to catch
   19078                 :      * up on the catalog updates we've done so far; at that point we can
   19079                 :      * complete the operation.
   19080                 :      */
   19081 GIC         213 :     if (concurrent)
   19082                 :     {
   19083                 :         Oid         partrelid,
   19084                 :                     parentrelid;
   19085                 :         LOCKTAG     tag;
   19086                 :         char       *parentrelname;
   19087                 :         char       *partrelname;
   19088                 : 
   19089 ECB             :         /*
   19090                 :          * Add a new constraint to the partition being detached, which
   19091                 :          * supplants the partition constraint (unless there is one already).
   19092                 :          */
   19093 GIC          70 :         DetachAddConstraintIfNeeded(wqueue, partRel);
   19094                 : 
   19095                 :         /*
   19096                 :          * We're almost done now; the only traces that remain are the
   19097                 :          * pg_inherits tuple and the partition's relpartbounds.  Before we can
   19098                 :          * remove those, we need to wait until all transactions that know that
   19099                 :          * this is a partition are gone.
   19100 ECB             :          */
   19101                 : 
   19102                 :         /*
   19103                 :          * Remember relation OIDs to re-acquire them later; and relation names
   19104                 :          * too, for error messages if something is dropped in between.
   19105                 :          */
   19106 GIC          70 :         partrelid = RelationGetRelid(partRel);
   19107              70 :         parentrelid = RelationGetRelid(rel);
   19108 CBC          70 :         parentrelname = MemoryContextStrdup(PortalContext,
   19109              70 :                                             RelationGetRelationName(rel));
   19110              70 :         partrelname = MemoryContextStrdup(PortalContext,
   19111 GIC          70 :                                           RelationGetRelationName(partRel));
   19112                 : 
   19113                 :         /* Invalidate relcache entries for the parent -- must be before close */
   19114              70 :         CacheInvalidateRelcache(rel);
   19115                 : 
   19116 CBC          70 :         table_close(partRel, NoLock);
   19117              70 :         table_close(rel, NoLock);
   19118 GBC          70 :         tab->rel = NULL;
   19119                 : 
   19120                 :         /* Make updated catalog entry visible */
   19121 GIC          70 :         PopActiveSnapshot();
   19122              70 :         CommitTransactionCommand();
   19123 ECB             : 
   19124 CBC          70 :         StartTransactionCommand();
   19125 EUB             : 
   19126                 :         /*
   19127                 :          * Now wait.  This ensures that all queries that were planned
   19128                 :          * including the partition are finished before we remove the rest of
   19129                 :          * catalog entries.  We don't need or indeed want to acquire this
   19130 ECB             :          * lock, though -- that would block later queries.
   19131                 :          *
   19132                 :          * We don't need to concern ourselves with waiting for a lock on the
   19133                 :          * partition itself, since we will acquire AccessExclusiveLock below.
   19134                 :          */
   19135 CBC          70 :         SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
   19136 GIC          70 :         WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
   19137                 : 
   19138 ECB             :         /*
   19139                 :          * Now acquire locks in both relations again.  Note they may have been
   19140                 :          * removed in the meantime, so care is required.
   19141                 :          */
   19142 CBC          45 :         rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
   19143 GIC          45 :         partRel = try_relation_open(partrelid, AccessExclusiveLock);
   19144                 : 
   19145 ECB             :         /* If the relations aren't there, something bad happened; bail out */
   19146 GIC          45 :         if (rel == NULL)
   19147                 :         {
   19148 UIC           0 :             if (partRel != NULL)    /* shouldn't happen */
   19149               0 :                 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
   19150                 :                      partrelname);
   19151               0 :             ereport(ERROR,
   19152                 :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19153                 :                      errmsg("partitioned table \"%s\" was removed concurrently",
   19154                 :                             parentrelname)));
   19155                 :         }
   19156 GIC          45 :         if (partRel == NULL)
   19157 UIC           0 :             ereport(ERROR,
   19158 ECB             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19159                 :                      errmsg("partition \"%s\" was removed concurrently", partrelname)));
   19160                 : 
   19161 GIC          45 :         tab->rel = rel;
   19162                 :     }
   19163                 : 
   19164                 :     /* Do the final part of detaching */
   19165             188 :     DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
   19166                 : 
   19167             187 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   19168                 : 
   19169                 :     /* keep our lock until commit */
   19170             187 :     table_close(partRel, NoLock);
   19171 ECB             : 
   19172 GIC         187 :     return address;
   19173                 : }
   19174                 : 
   19175 ECB             : /*
   19176                 :  * Second part of ALTER TABLE .. DETACH.
   19177                 :  *
   19178                 :  * This is separate so that it can be run independently when the second
   19179                 :  * transaction of the concurrent algorithm fails (crash or abort).
   19180                 :  */
   19181                 : static void
   19182 GIC         195 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
   19183                 :                         Oid defaultPartOid)
   19184 ECB             : {
   19185                 :     Relation    classRel;
   19186                 :     List       *fks;
   19187                 :     ListCell   *cell;
   19188                 :     List       *indexes;
   19189                 :     Datum       new_val[Natts_pg_class];
   19190                 :     bool        new_null[Natts_pg_class],
   19191                 :                 new_repl[Natts_pg_class];
   19192                 :     HeapTuple   tuple,
   19193                 :                 newtuple;
   19194 GIC         195 :     Relation    trigrel = NULL;
   19195                 : 
   19196             195 :     if (concurrent)
   19197 ECB             :     {
   19198                 :         /*
   19199                 :          * We can remove the pg_inherits row now. (In the non-concurrent case,
   19200                 :          * this was already done).
   19201                 :          */
   19202 CBC          52 :         RemoveInheritance(partRel, rel, true);
   19203                 :     }
   19204                 : 
   19205                 :     /* Drop any triggers that were cloned on creation/attach. */
   19206 GIC         195 :     DropClonedTriggersFromPartition(RelationGetRelid(partRel));
   19207                 : 
   19208                 :     /*
   19209                 :      * Detach any foreign keys that are inherited.  This includes creating
   19210 ECB             :      * additional action triggers.
   19211                 :      */
   19212 GIC         195 :     fks = copyObject(RelationGetFKeyList(partRel));
   19213             195 :     if (fks != NIL)
   19214 CBC          24 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   19215 GIC         234 :     foreach(cell, fks)
   19216                 :     {
   19217              39 :         ForeignKeyCacheInfo *fk = lfirst(cell);
   19218                 :         HeapTuple   contup;
   19219                 :         Form_pg_constraint conform;
   19220 ECB             :         Constraint *fkconstraint;
   19221                 :         Oid         insertTriggerOid,
   19222                 :                     updateTriggerOid;
   19223                 : 
   19224 CBC          39 :         contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
   19225 GIC          39 :         if (!HeapTupleIsValid(contup))
   19226 UIC           0 :             elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   19227 GIC          39 :         conform = (Form_pg_constraint) GETSTRUCT(contup);
   19228                 : 
   19229                 :         /* consider only the inherited foreign keys */
   19230              39 :         if (conform->contype != CONSTRAINT_FOREIGN ||
   19231              39 :             !OidIsValid(conform->conparentid))
   19232                 :         {
   19233               9 :             ReleaseSysCache(contup);
   19234               9 :             continue;
   19235 ECB             :         }
   19236                 : 
   19237                 :         /* unset conparentid and adjust conislocal, coninhcount, etc. */
   19238 GIC          30 :         ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
   19239                 : 
   19240 ECB             :         /*
   19241                 :          * Also, look up the partition's "check" triggers corresponding to the
   19242                 :          * constraint being detached and detach them from the parent triggers.
   19243                 :          */
   19244 GIC          30 :         GetForeignKeyCheckTriggers(trigrel,
   19245 ECB             :                                    fk->conoid, fk->confrelid, fk->conrelid,
   19246                 :                                    &insertTriggerOid, &updateTriggerOid);
   19247 GIC          30 :         Assert(OidIsValid(insertTriggerOid));
   19248              30 :         TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
   19249                 :                                 RelationGetRelid(partRel));
   19250              30 :         Assert(OidIsValid(updateTriggerOid));
   19251              30 :         TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
   19252 ECB             :                                 RelationGetRelid(partRel));
   19253                 : 
   19254                 :         /*
   19255                 :          * Make the action triggers on the referenced relation.  When this was
   19256                 :          * a partition the action triggers pointed to the parent rel (they
   19257                 :          * still do), but now we need separate ones of our own.
   19258                 :          */
   19259 GIC          30 :         fkconstraint = makeNode(Constraint);
   19260              30 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   19261 CBC          30 :         fkconstraint->conname = pstrdup(NameStr(conform->conname));
   19262 GIC          30 :         fkconstraint->deferrable = conform->condeferrable;
   19263              30 :         fkconstraint->initdeferred = conform->condeferred;
   19264              30 :         fkconstraint->location = -1;
   19265              30 :         fkconstraint->pktable = NULL;
   19266              30 :         fkconstraint->fk_attrs = NIL;
   19267              30 :         fkconstraint->pk_attrs = NIL;
   19268              30 :         fkconstraint->fk_matchtype = conform->confmatchtype;
   19269 CBC          30 :         fkconstraint->fk_upd_action = conform->confupdtype;
   19270 GIC          30 :         fkconstraint->fk_del_action = conform->confdeltype;
   19271              30 :         fkconstraint->fk_del_set_cols = NIL;
   19272              30 :         fkconstraint->old_conpfeqop = NIL;
   19273 CBC          30 :         fkconstraint->old_pktable_oid = InvalidOid;
   19274 GIC          30 :         fkconstraint->skip_validation = false;
   19275 CBC          30 :         fkconstraint->initially_valid = true;
   19276                 : 
   19277 GIC          30 :         createForeignKeyActionTriggers(partRel, conform->confrelid,
   19278                 :                                        fkconstraint, fk->conoid,
   19279                 :                                        conform->conindid,
   19280 ECB             :                                        InvalidOid, InvalidOid,
   19281                 :                                        NULL, NULL);
   19282                 : 
   19283 GIC          30 :         ReleaseSysCache(contup);
   19284                 :     }
   19285             195 :     list_free_deep(fks);
   19286             195 :     if (trigrel)
   19287              24 :         table_close(trigrel, RowExclusiveLock);
   19288                 : 
   19289                 :     /*
   19290                 :      * Any sub-constraints that are in the referenced-side of a larger
   19291                 :      * constraint have to be removed.  This partition is no longer part of the
   19292                 :      * key space of the constraint.
   19293                 :      */
   19294 CBC         213 :     foreach(cell, GetParentedForeignKeyRefs(partRel))
   19295                 :     {
   19296 GIC          19 :         Oid         constrOid = lfirst_oid(cell);
   19297                 :         ObjectAddress constraint;
   19298                 : 
   19299              19 :         ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   19300              19 :         deleteDependencyRecordsForClass(ConstraintRelationId,
   19301                 :                                         constrOid,
   19302                 :                                         ConstraintRelationId,
   19303                 :                                         DEPENDENCY_INTERNAL);
   19304 CBC          19 :         CommandCounterIncrement();
   19305                 : 
   19306 GIC          19 :         ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
   19307 CBC          19 :         performDeletion(&constraint, DROP_RESTRICT, 0);
   19308                 :     }
   19309 ECB             : 
   19310                 :     /* Now we can detach indexes */
   19311 CBC         194 :     indexes = RelationGetIndexList(partRel);
   19312             264 :     foreach(cell, indexes)
   19313                 :     {
   19314 GIC          70 :         Oid         idxid = lfirst_oid(cell);
   19315 ECB             :         Relation    idx;
   19316                 :         Oid         constrOid;
   19317                 : 
   19318 CBC          70 :         if (!has_superclass(idxid))
   19319 GIC           6 :             continue;
   19320 ECB             : 
   19321 CBC          64 :         Assert((IndexGetRelation(get_partition_parent(idxid, false), false) ==
   19322                 :                 RelationGetRelid(rel)));
   19323                 : 
   19324 GIC          64 :         idx = index_open(idxid, AccessExclusiveLock);
   19325              64 :         IndexSetParentIndex(idx, InvalidOid);
   19326                 : 
   19327                 :         /* If there's a constraint associated with the index, detach it too */
   19328              64 :         constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
   19329                 :                                                     idxid);
   19330 CBC          64 :         if (OidIsValid(constrOid))
   19331 GIC          30 :             ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   19332 ECB             : 
   19333 GIC          64 :         index_close(idx, NoLock);
   19334 ECB             :     }
   19335                 : 
   19336                 :     /* Update pg_class tuple */
   19337 CBC         194 :     classRel = table_open(RelationRelationId, RowExclusiveLock);
   19338             194 :     tuple = SearchSysCacheCopy1(RELOID,
   19339 ECB             :                                 ObjectIdGetDatum(RelationGetRelid(partRel)));
   19340 GIC         194 :     if (!HeapTupleIsValid(tuple))
   19341 UIC           0 :         elog(ERROR, "cache lookup failed for relation %u",
   19342                 :              RelationGetRelid(partRel));
   19343 GIC         194 :     Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
   19344                 : 
   19345                 :     /* Clear relpartbound and reset relispartition */
   19346 CBC         194 :     memset(new_val, 0, sizeof(new_val));
   19347 GIC         194 :     memset(new_null, false, sizeof(new_null));
   19348             194 :     memset(new_repl, false, sizeof(new_repl));
   19349 CBC         194 :     new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
   19350 GIC         194 :     new_null[Anum_pg_class_relpartbound - 1] = true;
   19351             194 :     new_repl[Anum_pg_class_relpartbound - 1] = true;
   19352             194 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
   19353                 :                                  new_val, new_null, new_repl);
   19354                 : 
   19355             194 :     ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
   19356 CBC         194 :     CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
   19357 GIC         194 :     heap_freetuple(newtuple);
   19358 CBC         194 :     table_close(classRel, RowExclusiveLock);
   19359 ECB             : 
   19360 GIC         194 :     if (OidIsValid(defaultPartOid))
   19361                 :     {
   19362 ECB             :         /*
   19363                 :          * If the relation being detached is the default partition itself,
   19364                 :          * remove it from the parent's pg_partitioned_table entry.
   19365                 :          *
   19366                 :          * If not, we must invalidate default partition's relcache entry, as
   19367                 :          * in StorePartitionBound: its partition constraint depends on every
   19368                 :          * other partition's partition constraint.
   19369                 :          */
   19370 GIC          23 :         if (RelationGetRelid(partRel) == defaultPartOid)
   19371 GBC           1 :             update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
   19372 EUB             :         else
   19373 GIC          22 :             CacheInvalidateRelcacheByRelid(defaultPartOid);
   19374                 :     }
   19375                 : 
   19376 ECB             :     /*
   19377                 :      * Invalidate the parent's relcache so that the partition is no longer
   19378                 :      * included in its partition descriptor.
   19379                 :      */
   19380 CBC         194 :     CacheInvalidateRelcache(rel);
   19381                 : 
   19382                 :     /*
   19383                 :      * If the partition we just detached is partitioned itself, invalidate
   19384                 :      * relcache for all descendent partitions too to ensure that their
   19385                 :      * rd_partcheck expression trees are rebuilt; must lock partitions before
   19386                 :      * doing so, using the same lockmode as what partRel has been locked with
   19387 ECB             :      * by the caller.
   19388                 :      */
   19389 CBC         194 :     if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   19390 ECB             :     {
   19391                 :         List       *children;
   19392                 : 
   19393 CBC          25 :         children = find_all_inheritors(RelationGetRelid(partRel),
   19394 ECB             :                                        AccessExclusiveLock, NULL);
   19395 GIC          81 :         foreach(cell, children)
   19396 ECB             :         {
   19397 CBC          56 :             CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
   19398                 :         }
   19399 ECB             :     }
   19400 GIC         194 : }
   19401                 : 
   19402                 : /*
   19403                 :  * ALTER TABLE ... DETACH PARTITION ... FINALIZE
   19404                 :  *
   19405                 :  * To use when a DETACH PARTITION command previously did not run to
   19406                 :  * completion; this completes the detaching process.
   19407                 :  */
   19408                 : static ObjectAddress
   19409 CBC           7 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
   19410                 : {
   19411                 :     Relation    partRel;
   19412 ECB             :     ObjectAddress address;
   19413 GIC           7 :     Snapshot    snap = GetActiveSnapshot();
   19414                 : 
   19415 CBC           7 :     partRel = table_openrv(name, AccessExclusiveLock);
   19416 ECB             : 
   19417                 :     /*
   19418                 :      * Wait until existing snapshots are gone.  This is important if the
   19419                 :      * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
   19420                 :      * user could immediately run DETACH FINALIZE without actually waiting for
   19421                 :      * existing transactions.  We must not complete the detach action until
   19422                 :      * all such queries are complete (otherwise we would present them with an
   19423                 :      * inconsistent view of catalogs).
   19424                 :      */
   19425 GIC           7 :     WaitForOlderSnapshots(snap->xmin, false);
   19426 ECB             : 
   19427 CBC           7 :     DetachPartitionFinalize(rel, partRel, true, InvalidOid);
   19428                 : 
   19429 GIC           7 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   19430                 : 
   19431               7 :     table_close(partRel, NoLock);
   19432                 : 
   19433               7 :     return address;
   19434                 : }
   19435 ECB             : 
   19436                 : /*
   19437                 :  * DetachAddConstraintIfNeeded
   19438                 :  *      Subroutine for ATExecDetachPartition.  Create a constraint that
   19439                 :  *      takes the place of the partition constraint, but avoid creating
   19440                 :  *      a dupe if an constraint already exists which implies the needed
   19441                 :  *      constraint.
   19442                 :  */
   19443                 : static void
   19444 GIC          70 : DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
   19445                 : {
   19446                 :     List       *constraintExpr;
   19447                 : 
   19448 CBC          70 :     constraintExpr = RelationGetPartitionQual(partRel);
   19449 GIC          70 :     constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
   19450 ECB             : 
   19451                 :     /*
   19452                 :      * Avoid adding a new constraint if the needed constraint is implied by an
   19453                 :      * existing constraint
   19454                 :      */
   19455 CBC          70 :     if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
   19456 ECB             :     {
   19457                 :         AlteredTableInfo *tab;
   19458                 :         Constraint *n;
   19459                 : 
   19460 GIC          67 :         tab = ATGetQueueEntry(wqueue, partRel);
   19461                 : 
   19462 ECB             :         /* Add constraint on partition, equivalent to the partition constraint */
   19463 GIC          67 :         n = makeNode(Constraint);
   19464              67 :         n->contype = CONSTR_CHECK;
   19465 CBC          67 :         n->conname = NULL;
   19466 GIC          67 :         n->location = -1;
   19467              67 :         n->is_no_inherit = false;
   19468              67 :         n->raw_expr = NULL;
   19469              67 :         n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
   19470              67 :         n->initially_valid = true;
   19471              67 :         n->skip_validation = true;
   19472 ECB             :         /* It's a re-add, since it nominally already exists */
   19473 GIC          67 :         ATAddCheckConstraint(wqueue, tab, partRel, n,
   19474                 :                              true, false, true, ShareUpdateExclusiveLock);
   19475 ECB             :     }
   19476 GIC          70 : }
   19477 ECB             : 
   19478                 : /*
   19479                 :  * DropClonedTriggersFromPartition
   19480                 :  *      subroutine for ATExecDetachPartition to remove any triggers that were
   19481                 :  *      cloned to the partition when it was created-as-partition or attached.
   19482                 :  *      This undoes what CloneRowTriggersToPartition did.
   19483                 :  */
   19484                 : static void
   19485 GIC         195 : DropClonedTriggersFromPartition(Oid partitionId)
   19486                 : {
   19487                 :     ScanKeyData skey;
   19488                 :     SysScanDesc scan;
   19489 ECB             :     HeapTuple   trigtup;
   19490                 :     Relation    tgrel;
   19491                 :     ObjectAddresses *objects;
   19492                 : 
   19493 GIC         195 :     objects = new_object_addresses();
   19494                 : 
   19495                 :     /*
   19496                 :      * Scan pg_trigger to search for all triggers on this rel.
   19497 ECB             :      */
   19498 GIC         195 :     ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   19499 ECB             :                 F_OIDEQ, ObjectIdGetDatum(partitionId));
   19500 CBC         195 :     tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   19501 GIC         195 :     scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
   19502                 :                               true, NULL, 1, &skey);
   19503 CBC         320 :     while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
   19504                 :     {
   19505 GIC         125 :         Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
   19506 ECB             :         ObjectAddress trig;
   19507                 : 
   19508                 :         /* Ignore triggers that weren't cloned */
   19509 GIC         125 :         if (!OidIsValid(pg_trigger->tgparentid))
   19510 CBC         116 :             continue;
   19511                 : 
   19512                 :         /*
   19513 ECB             :          * Ignore internal triggers that are implementation objects of foreign
   19514                 :          * keys, because these will be detached when the foreign keys
   19515                 :          * themselves are.
   19516                 :          */
   19517 GIC         107 :         if (OidIsValid(pg_trigger->tgconstrrelid))
   19518              98 :             continue;
   19519                 : 
   19520 ECB             :         /*
   19521                 :          * This is ugly, but necessary: remove the dependency markings on the
   19522                 :          * trigger so that it can be removed.
   19523                 :          */
   19524 GIC           9 :         deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   19525                 :                                         TriggerRelationId,
   19526                 :                                         DEPENDENCY_PARTITION_PRI);
   19527 CBC           9 :         deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   19528 ECB             :                                         RelationRelationId,
   19529                 :                                         DEPENDENCY_PARTITION_SEC);
   19530                 : 
   19531                 :         /* remember this trigger to remove it below */
   19532 GIC           9 :         ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
   19533 CBC           9 :         add_exact_object_address(&trig, objects);
   19534 ECB             :     }
   19535 EUB             : 
   19536                 :     /* make the dependency removal visible to the deletion below */
   19537 GIC         195 :     CommandCounterIncrement();
   19538             195 :     performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   19539 ECB             : 
   19540                 :     /* done */
   19541 GIC         195 :     free_object_addresses(objects);
   19542             195 :     systable_endscan(scan);
   19543             195 :     table_close(tgrel, RowExclusiveLock);
   19544             195 : }
   19545 ECB             : 
   19546                 : /*
   19547                 :  * Before acquiring lock on an index, acquire the same lock on the owning
   19548                 :  * table.
   19549                 :  */
   19550                 : struct AttachIndexCallbackState
   19551                 : {
   19552                 :     Oid         partitionOid;
   19553                 :     Oid         parentTblOid;
   19554                 :     bool        lockedParentTbl;
   19555                 : };
   19556                 : 
   19557                 : static void
   19558 GIC         193 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
   19559                 :                                void *arg)
   19560 ECB             : {
   19561                 :     struct AttachIndexCallbackState *state;
   19562                 :     Form_pg_class classform;
   19563                 :     HeapTuple   tuple;
   19564                 : 
   19565 GIC         193 :     state = (struct AttachIndexCallbackState *) arg;
   19566                 : 
   19567             193 :     if (!state->lockedParentTbl)
   19568 ECB             :     {
   19569 GIC         186 :         LockRelationOid(state->parentTblOid, AccessShareLock);
   19570 CBC         186 :         state->lockedParentTbl = true;
   19571 ECB             :     }
   19572                 : 
   19573                 :     /*
   19574                 :      * If we previously locked some other heap, and the name we're looking up
   19575                 :      * no longer refers to an index on that relation, release the now-useless
   19576                 :      * lock.  XXX maybe we should do *after* we verify whether the index does
   19577                 :      * not actually belong to the same relation ...
   19578                 :      */
   19579 GIC         193 :     if (relOid != oldRelOid && OidIsValid(state->partitionOid))
   19580 ECB             :     {
   19581 UIC           0 :         UnlockRelationOid(state->partitionOid, AccessShareLock);
   19582 LBC           0 :         state->partitionOid = InvalidOid;
   19583 EUB             :     }
   19584                 : 
   19585                 :     /* Didn't find a relation, so no need for locking or permission checks. */
   19586 CBC         193 :     if (!OidIsValid(relOid))
   19587 GIC           3 :         return;
   19588 ECB             : 
   19589 GIC         190 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
   19590 CBC         190 :     if (!HeapTupleIsValid(tuple))
   19591 LBC           0 :         return;                 /* concurrently dropped, so nothing to do */
   19592 GIC         190 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   19593             190 :     if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
   19594             147 :         classform->relkind != RELKIND_INDEX)
   19595 CBC           3 :         ereport(ERROR,
   19596 ECB             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19597                 :                  errmsg("\"%s\" is not an index", rv->relname)));
   19598 CBC         187 :     ReleaseSysCache(tuple);
   19599 ECB             : 
   19600                 :     /*
   19601                 :      * Since we need only examine the heap's tupledesc, an access share lock
   19602                 :      * on it (preventing any DDL) is sufficient.
   19603                 :      */
   19604 CBC         187 :     state->partitionOid = IndexGetRelation(relOid, false);
   19605             187 :     LockRelationOid(state->partitionOid, AccessShareLock);
   19606 ECB             : }
   19607                 : 
   19608                 : /*
   19609                 :  * ALTER INDEX i1 ATTACH PARTITION i2
   19610                 :  */
   19611                 : static ObjectAddress
   19612 CBC         186 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
   19613                 : {
   19614                 :     Relation    partIdx;
   19615 ECB             :     Relation    partTbl;
   19616                 :     Relation    parentTbl;
   19617                 :     ObjectAddress address;
   19618                 :     Oid         partIdxId;
   19619                 :     Oid         currParent;
   19620                 :     struct AttachIndexCallbackState state;
   19621                 : 
   19622                 :     /*
   19623                 :      * We need to obtain lock on the index 'name' to modify it, but we also
   19624                 :      * need to read its owning table's tuple descriptor -- so we need to lock
   19625                 :      * both.  To avoid deadlocks, obtain lock on the table before doing so on
   19626                 :      * the index.  Furthermore, we need to examine the parent table of the
   19627                 :      * partition, so lock that one too.
   19628                 :      */
   19629 GIC         186 :     state.partitionOid = InvalidOid;
   19630             186 :     state.parentTblOid = parentIdx->rd_index->indrelid;
   19631             186 :     state.lockedParentTbl = false;
   19632                 :     partIdxId =
   19633             186 :         RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
   19634                 :                                  RangeVarCallbackForAttachIndex,
   19635                 :                                  (void *) &state);
   19636                 :     /* Not there? */
   19637             180 :     if (!OidIsValid(partIdxId))
   19638 UIC           0 :         ereport(ERROR,
   19639                 :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   19640                 :                  errmsg("index \"%s\" does not exist", name->relname)));
   19641                 : 
   19642                 :     /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
   19643 GIC         180 :     partIdx = relation_open(partIdxId, AccessExclusiveLock);
   19644                 : 
   19645                 :     /* we already hold locks on both tables, so this is safe: */
   19646 CBC         180 :     parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
   19647 GIC         180 :     partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
   19648                 : 
   19649             180 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
   19650                 : 
   19651                 :     /* Silently do nothing if already in the right state */
   19652             360 :     currParent = partIdx->rd_rel->relispartition ?
   19653             180 :         get_partition_parent(partIdxId, false) : InvalidOid;
   19654             180 :     if (currParent != RelationGetRelid(parentIdx))
   19655                 :     {
   19656                 :         IndexInfo  *childInfo;
   19657                 :         IndexInfo  *parentInfo;
   19658 ECB             :         AttrMap    *attmap;
   19659                 :         bool        found;
   19660                 :         int         i;
   19661                 :         PartitionDesc partDesc;
   19662                 :         Oid         constraintOid,
   19663 GIC         174 :                     cldConstrId = InvalidOid;
   19664                 : 
   19665                 :         /*
   19666                 :          * If this partition already has an index attached, refuse the
   19667                 :          * operation.
   19668                 :          */
   19669             174 :         refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
   19670                 : 
   19671             171 :         if (OidIsValid(currParent))
   19672 UIC           0 :             ereport(ERROR,
   19673                 :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19674                 :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   19675                 :                             RelationGetRelationName(partIdx),
   19676 ECB             :                             RelationGetRelationName(parentIdx)),
   19677                 :                      errdetail("Index \"%s\" is already attached to another index.",
   19678                 :                                RelationGetRelationName(partIdx))));
   19679                 : 
   19680                 :         /* Make sure it indexes a partition of the other index's table */
   19681 GIC         171 :         partDesc = RelationGetPartitionDesc(parentTbl, true);
   19682             171 :         found = false;
   19683             272 :         for (i = 0; i < partDesc->nparts; i++)
   19684                 :         {
   19685             269 :             if (partDesc->oids[i] == state.partitionOid)
   19686                 :             {
   19687             168 :                 found = true;
   19688 CBC         168 :                 break;
   19689                 :             }
   19690                 :         }
   19691 GIC         171 :         if (!found)
   19692               3 :             ereport(ERROR,
   19693                 :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19694                 :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   19695 ECB             :                             RelationGetRelationName(partIdx),
   19696                 :                             RelationGetRelationName(parentIdx)),
   19697                 :                      errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
   19698                 :                                RelationGetRelationName(partIdx),
   19699                 :                                RelationGetRelationName(parentTbl))));
   19700                 : 
   19701                 :         /* Ensure the indexes are compatible */
   19702 GIC         168 :         childInfo = BuildIndexInfo(partIdx);
   19703             168 :         parentInfo = BuildIndexInfo(parentIdx);
   19704             168 :         attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
   19705                 :                                        RelationGetDescr(parentTbl),
   19706                 :                                        false);
   19707             168 :         if (!CompareIndexInfo(childInfo, parentInfo,
   19708 ECB             :                               partIdx->rd_indcollation,
   19709                 :                               parentIdx->rd_indcollation,
   19710                 :                               partIdx->rd_opfamily,
   19711                 :                               parentIdx->rd_opfamily,
   19712                 :                               attmap))
   19713 GIC          21 :             ereport(ERROR,
   19714                 :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19715                 :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   19716                 :                             RelationGetRelationName(partIdx),
   19717 ECB             :                             RelationGetRelationName(parentIdx)),
   19718                 :                      errdetail("The index definitions do not match.")));
   19719                 : 
   19720                 :         /*
   19721                 :          * If there is a constraint in the parent, make sure there is one in
   19722                 :          * the child too.
   19723                 :          */
   19724 GIC         147 :         constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
   19725                 :                                                         RelationGetRelid(parentIdx));
   19726                 : 
   19727             147 :         if (OidIsValid(constraintOid))
   19728                 :         {
   19729 CBC          64 :             cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
   19730                 :                                                           partIdxId);
   19731 GIC          64 :             if (!OidIsValid(cldConstrId))
   19732               3 :                 ereport(ERROR,
   19733                 :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19734                 :                          errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   19735                 :                                 RelationGetRelationName(partIdx),
   19736                 :                                 RelationGetRelationName(parentIdx)),
   19737                 :                          errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
   19738                 :                                    RelationGetRelationName(parentIdx),
   19739                 :                                    RelationGetRelationName(parentTbl),
   19740                 :                                    RelationGetRelationName(partIdx))));
   19741                 :         }
   19742 ECB             : 
   19743                 :         /*
   19744                 :          * If it's a primary key, make sure the columns in the partition are
   19745                 :          * NOT NULL.
   19746                 :          */
   19747 GNC         144 :         if (parentIdx->rd_index->indisprimary)
   19748              49 :             verifyPartitionIndexNotNull(childInfo, partTbl);
   19749                 : 
   19750 ECB             :         /* All good -- do it */
   19751 CBC         141 :         IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
   19752             141 :         if (OidIsValid(constraintOid))
   19753              58 :             ConstraintSetParentConstraint(cldConstrId, constraintOid,
   19754 ECB             :                                           RelationGetRelid(partTbl));
   19755                 : 
   19756 GIC         141 :         free_attrmap(attmap);
   19757 ECB             : 
   19758 GIC         141 :         validatePartitionedIndex(parentIdx, parentTbl);
   19759 ECB             :     }
   19760                 : 
   19761 CBC         147 :     relation_close(parentTbl, AccessShareLock);
   19762                 :     /* keep these locks till commit */
   19763 GIC         147 :     relation_close(partTbl, NoLock);
   19764 CBC         147 :     relation_close(partIdx, NoLock);
   19765 ECB             : 
   19766 GIC         147 :     return address;
   19767 ECB             : }
   19768                 : 
   19769                 : /*
   19770                 :  * Verify whether the given partition already contains an index attached
   19771                 :  * to the given partitioned index.  If so, raise an error.
   19772                 :  */
   19773                 : static void
   19774 GIC         174 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
   19775                 : {
   19776                 :     Oid         existingIdx;
   19777                 : 
   19778 CBC         174 :     existingIdx = index_get_partition(partitionTbl,
   19779 ECB             :                                       RelationGetRelid(parentIdx));
   19780 GIC         174 :     if (OidIsValid(existingIdx))
   19781               3 :         ereport(ERROR,
   19782                 :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19783                 :                  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   19784                 :                         RelationGetRelationName(partIdx),
   19785 ECB             :                         RelationGetRelationName(parentIdx)),
   19786                 :                  errdetail("Another index is already attached for partition \"%s\".",
   19787                 :                            RelationGetRelationName(partitionTbl))));
   19788 GIC         171 : }
   19789 ECB             : 
   19790                 : /*
   19791 EUB             :  * Verify whether the set of attached partition indexes to a parent index on
   19792                 :  * a partitioned table is complete.  If it is, mark the parent index valid.
   19793                 :  *
   19794                 :  * This should be called each time a partition index is attached.
   19795                 :  */
   19796                 : static void
   19797 GIC         159 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
   19798                 : {
   19799 ECB             :     Relation    inheritsRel;
   19800 EUB             :     SysScanDesc scan;
   19801                 :     ScanKeyData key;
   19802 GIC         159 :     int         tuples = 0;
   19803                 :     HeapTuple   inhTup;
   19804 CBC         159 :     bool        updated = false;
   19805                 : 
   19806 GIC         159 :     Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
   19807                 : 
   19808 ECB             :     /*
   19809                 :      * Scan pg_inherits for this parent index.  Count each valid index we find
   19810                 :      * (verifying the pg_index entry for each), and if we reach the total
   19811                 :      * amount we expect, we can mark this parent index as valid.
   19812                 :      */
   19813 CBC         159 :     inheritsRel = table_open(InheritsRelationId, AccessShareLock);
   19814 GIC         159 :     ScanKeyInit(&key, Anum_pg_inherits_inhparent,
   19815 ECB             :                 BTEqualStrategyNumber, F_OIDEQ,
   19816                 :                 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   19817 GIC         159 :     scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
   19818                 :                               NULL, 1, &key);
   19819             420 :     while ((inhTup = systable_getnext(scan)) != NULL)
   19820                 :     {
   19821             261 :         Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
   19822                 :         HeapTuple   indTup;
   19823                 :         Form_pg_index indexForm;
   19824                 : 
   19825 CBC         261 :         indTup = SearchSysCache1(INDEXRELID,
   19826                 :                                  ObjectIdGetDatum(inhForm->inhrelid));
   19827 GIC         261 :         if (!HeapTupleIsValid(indTup))
   19828 UIC           0 :             elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
   19829 GIC         261 :         indexForm = (Form_pg_index) GETSTRUCT(indTup);
   19830             261 :         if (indexForm->indisvalid)
   19831             235 :             tuples += 1;
   19832             261 :         ReleaseSysCache(indTup);
   19833                 :     }
   19834                 : 
   19835                 :     /* Done with pg_inherits */
   19836             159 :     systable_endscan(scan);
   19837 CBC         159 :     table_close(inheritsRel, AccessShareLock);
   19838                 : 
   19839 ECB             :     /*
   19840                 :      * If we found as many inherited indexes as the partitioned table has
   19841                 :      * partitions, we're good; update pg_index to set indisvalid.
   19842                 :      */
   19843 GIC         159 :     if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
   19844                 :     {
   19845 ECB             :         Relation    idxRel;
   19846                 :         HeapTuple   newtup;
   19847                 : 
   19848 GIC          77 :         idxRel = table_open(IndexRelationId, RowExclusiveLock);
   19849 ECB             : 
   19850 GIC          77 :         newtup = heap_copytuple(partedIdx->rd_indextuple);
   19851              77 :         ((Form_pg_index) GETSTRUCT(newtup))->indisvalid = true;
   19852              77 :         updated = true;
   19853                 : 
   19854              77 :         CatalogTupleUpdate(idxRel, &partedIdx->rd_indextuple->t_self, newtup);
   19855 ECB             : 
   19856 CBC          77 :         table_close(idxRel, RowExclusiveLock);
   19857 ECB             :     }
   19858                 : 
   19859                 :     /*
   19860                 :      * If this index is in turn a partition of a larger index, validating it
   19861                 :      * might cause the parent to become valid also.  Try that.
   19862                 :      */
   19863 GIC         159 :     if (updated && partedIdx->rd_rel->relispartition)
   19864                 :     {
   19865                 :         Oid         parentIdxId,
   19866                 :                     parentTblId;
   19867 ECB             :         Relation    parentIdx,
   19868                 :                     parentTbl;
   19869 EUB             : 
   19870 ECB             :         /* make sure we see the validation we just did */
   19871 GIC          18 :         CommandCounterIncrement();
   19872                 : 
   19873 CBC          18 :         parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
   19874              18 :         parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
   19875 GIC          18 :         parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
   19876 CBC          18 :         parentTbl = relation_open(parentTblId, AccessExclusiveLock);
   19877              18 :         Assert(!parentIdx->rd_index->indisvalid);
   19878                 : 
   19879 GIC          18 :         validatePartitionedIndex(parentIdx, parentTbl);
   19880                 : 
   19881 CBC          18 :         relation_close(parentIdx, AccessExclusiveLock);
   19882 GIC          18 :         relation_close(parentTbl, AccessExclusiveLock);
   19883                 :     }
   19884             159 : }
   19885                 : 
   19886                 : /*
   19887                 :  * When attaching an index as a partition of a partitioned index which is a
   19888                 :  * primary key, verify that all the columns in the partition are marked NOT
   19889                 :  * NULL.
   19890                 :  */
   19891                 : static void
   19892 GNC          49 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
   19893                 : {
   19894              96 :     for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
   19895                 :     {
   19896              50 :         Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
   19897                 :                                               iinfo->ii_IndexAttrNumbers[i] - 1);
   19898                 : 
   19899              50 :         if (!att->attnotnull)
   19900               3 :             ereport(ERROR,
   19901                 :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   19902                 :                     errmsg("invalid primary key definition"),
   19903                 :                     errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
   19904                 :                               NameStr(att->attname),
   19905                 :                               RelationGetRelationName(partition)));
   19906                 :     }
   19907              46 : }
   19908                 : 
   19909                 : /*
   19910 ECB             :  * Return an OID list of constraints that reference the given relation
   19911                 :  * that are marked as having a parent constraints.
   19912                 :  */
   19913                 : static List *
   19914 CBC         425 : GetParentedForeignKeyRefs(Relation partition)
   19915                 : {
   19916 ECB             :     Relation    pg_constraint;
   19917                 :     HeapTuple   tuple;
   19918                 :     SysScanDesc scan;
   19919                 :     ScanKeyData key[2];
   19920 GIC         425 :     List       *constraints = NIL;
   19921                 : 
   19922                 :     /*
   19923                 :      * If no indexes, or no columns are referenceable by FKs, we can avoid the
   19924                 :      * scan.
   19925 ECB             :      */
   19926 CBC         591 :     if (RelationGetIndexList(partition) == NIL ||
   19927             166 :         bms_is_empty(RelationGetIndexAttrBitmap(partition,
   19928 ECB             :                                                 INDEX_ATTR_BITMAP_KEY)))
   19929 CBC         337 :         return NIL;
   19930 ECB             : 
   19931                 :     /* Search for constraints referencing this table */
   19932 CBC          88 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   19933              88 :     ScanKeyInit(&key[0],
   19934 ECB             :                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   19935                 :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
   19936 CBC          88 :     ScanKeyInit(&key[1],
   19937 ECB             :                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   19938                 :                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   19939                 : 
   19940                 :     /* XXX This is a seqscan, as we don't have a usable index */
   19941 CBC          88 :     scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
   19942 GIC         150 :     while ((tuple = systable_getnext(scan)) != NULL)
   19943 ECB             :     {
   19944 GIC          62 :         Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   19945                 : 
   19946                 :         /*
   19947                 :          * We only need to process constraints that are part of larger ones.
   19948                 :          */
   19949 CBC          62 :         if (!OidIsValid(constrForm->conparentid))
   19950 UIC           0 :             continue;
   19951 ECB             : 
   19952 CBC          62 :         constraints = lappend_oid(constraints, constrForm->oid);
   19953 ECB             :     }
   19954                 : 
   19955 GIC          88 :     systable_endscan(scan);
   19956              88 :     table_close(pg_constraint, AccessShareLock);
   19957                 : 
   19958              88 :     return constraints;
   19959                 : }
   19960 ECB             : 
   19961                 : /*
   19962                 :  * During DETACH PARTITION, verify that any foreign keys pointing to the
   19963                 :  * partitioned table would not become invalid.  An error is raised if any
   19964                 :  * referenced values exist.
   19965                 :  */
   19966                 : static void
   19967 GIC         230 : ATDetachCheckNoForeignKeyRefs(Relation partition)
   19968                 : {
   19969                 :     List       *constraints;
   19970 ECB             :     ListCell   *cell;
   19971                 : 
   19972 CBC         230 :     constraints = GetParentedForeignKeyRefs(partition);
   19973 ECB             : 
   19974 GIC         256 :     foreach(cell, constraints)
   19975                 :     {
   19976              43 :         Oid         constrOid = lfirst_oid(cell);
   19977 ECB             :         HeapTuple   tuple;
   19978                 :         Form_pg_constraint constrForm;
   19979                 :         Relation    rel;
   19980 GNC          43 :         Trigger     trig = {0};
   19981                 : 
   19982 GIC          43 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   19983              43 :         if (!HeapTupleIsValid(tuple))
   19984 LBC           0 :             elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   19985 CBC          43 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   19986                 : 
   19987              43 :         Assert(OidIsValid(constrForm->conparentid));
   19988 GIC          43 :         Assert(constrForm->confrelid == RelationGetRelid(partition));
   19989                 : 
   19990 ECB             :         /* prevent data changes into the referencing table until commit */
   19991 CBC          43 :         rel = table_open(constrForm->conrelid, ShareLock);
   19992                 : 
   19993              43 :         trig.tgoid = InvalidOid;
   19994 GIC          43 :         trig.tgname = NameStr(constrForm->conname);
   19995 CBC          43 :         trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   19996              43 :         trig.tgisinternal = true;
   19997 GIC          43 :         trig.tgconstrrelid = RelationGetRelid(partition);
   19998 CBC          43 :         trig.tgconstrindid = constrForm->conindid;
   19999 GIC          43 :         trig.tgconstraint = constrForm->oid;
   20000              43 :         trig.tgdeferrable = false;
   20001              43 :         trig.tginitdeferred = false;
   20002 ECB             :         /* we needn't fill in remaining fields */
   20003                 : 
   20004 GIC          43 :         RI_PartitionRemove_Check(&trig, rel, partition);
   20005 ECB             : 
   20006 GBC          26 :         ReleaseSysCache(tuple);
   20007                 : 
   20008 CBC          26 :         table_close(rel, NoLock);
   20009                 :     }
   20010 GIC         213 : }
   20011 ECB             : 
   20012                 : /*
   20013                 :  * resolve column compression specification to compression method.
   20014                 :  */
   20015                 : static char
   20016 CBC        1195 : GetAttributeCompression(Oid atttypid, char *compression)
   20017 ECB             : {
   20018                 :     char        cmethod;
   20019                 : 
   20020 CBC        1195 :     if (compression == NULL || strcmp(compression, "default") == 0)
   20021            1124 :         return InvalidCompressionMethod;
   20022 ECB             : 
   20023                 :     /*
   20024                 :      * To specify a nondefault method, the column data type must be toastable.
   20025                 :      * Note this says nothing about whether the column's attstorage setting
   20026                 :      * permits compression; we intentionally allow attstorage and
   20027                 :      * attcompression to be independent.  But with a non-toastable type,
   20028                 :      * attstorage could not be set to a value that would permit compression.
   20029                 :      *
   20030                 :      * We don't actually need to enforce this, since nothing bad would happen
   20031                 :      * if attcompression were non-default; it would never be consulted.  But
   20032                 :      * it seems more user-friendly to complain about a certainly-useless
   20033                 :      * attempt to set the property.
   20034                 :      */
   20035 CBC          71 :     if (!TypeIsToastable(atttypid))
   20036               3 :         ereport(ERROR,
   20037                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   20038 ECB             :                  errmsg("column data type %s does not support compression",
   20039                 :                         format_type_be(atttypid))));
   20040                 : 
   20041 GIC          68 :     cmethod = CompressionNameToMethod(compression);
   20042              68 :     if (!CompressionMethodIsValid(cmethod))
   20043               6 :         ereport(ERROR,
   20044                 :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   20045 ECB             :                  errmsg("invalid compression method \"%s\"", compression)));
   20046                 : 
   20047 GIC          62 :     return cmethod;
   20048                 : }
   20049                 : 
   20050                 : /*
   20051                 :  * resolve column storage specification
   20052                 :  */
   20053                 : static char
   20054 GNC         115 : GetAttributeStorage(Oid atttypid, const char *storagemode)
   20055                 : {
   20056             115 :     char        cstorage = 0;
   20057                 : 
   20058             115 :     if (pg_strcasecmp(storagemode, "plain") == 0)
   20059              23 :         cstorage = TYPSTORAGE_PLAIN;
   20060              92 :     else if (pg_strcasecmp(storagemode, "external") == 0)
   20061              75 :         cstorage = TYPSTORAGE_EXTERNAL;
   20062              17 :     else if (pg_strcasecmp(storagemode, "extended") == 0)
   20063               7 :         cstorage = TYPSTORAGE_EXTENDED;
   20064              10 :     else if (pg_strcasecmp(storagemode, "main") == 0)
   20065               7 :         cstorage = TYPSTORAGE_MAIN;
   20066               3 :     else if (pg_strcasecmp(storagemode, "default") == 0)
   20067               3 :         cstorage = get_typstorage(atttypid);
   20068                 :     else
   20069 UNC           0 :         ereport(ERROR,
   20070                 :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   20071                 :                  errmsg("invalid storage type \"%s\"",
   20072                 :                         storagemode)));
   20073                 : 
   20074                 :     /*
   20075                 :      * safety check: do not allow toasted storage modes unless column datatype
   20076                 :      * is TOAST-aware.
   20077                 :      */
   20078 GNC         115 :     if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
   20079               3 :         ereport(ERROR,
   20080                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   20081                 :                  errmsg("column data type %s can only have storage PLAIN",
   20082                 :                         format_type_be(atttypid))));
   20083                 : 
   20084             112 :     return cstorage;
   20085                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a