LCOV - code coverage report
Current view: top level - src/backend/commands - tablecmds.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 92.8 % 9526 8840
Test Date: 2026-01-26 10:56:24 Functions: 99.1 % 220 218
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 64.2 % 7243 4647

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * tablecmds.c
       4                 :             :  *        Commands for creating and altering table structures and settings
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  *
      10                 :             :  * IDENTIFICATION
      11                 :             :  *        src/backend/commands/tablecmds.c
      12                 :             :  *
      13                 :             :  *-------------------------------------------------------------------------
      14                 :             :  */
      15                 :             : #include "postgres.h"
      16                 :             : 
      17                 :             : #include "access/attmap.h"
      18                 :             : #include "access/genam.h"
      19                 :             : #include "access/gist.h"
      20                 :             : #include "access/heapam.h"
      21                 :             : #include "access/heapam_xlog.h"
      22                 :             : #include "access/multixact.h"
      23                 :             : #include "access/reloptions.h"
      24                 :             : #include "access/relscan.h"
      25                 :             : #include "access/sysattr.h"
      26                 :             : #include "access/tableam.h"
      27                 :             : #include "access/toast_compression.h"
      28                 :             : #include "access/xact.h"
      29                 :             : #include "access/xlog.h"
      30                 :             : #include "access/xloginsert.h"
      31                 :             : #include "catalog/catalog.h"
      32                 :             : #include "catalog/heap.h"
      33                 :             : #include "catalog/index.h"
      34                 :             : #include "catalog/namespace.h"
      35                 :             : #include "catalog/objectaccess.h"
      36                 :             : #include "catalog/partition.h"
      37                 :             : #include "catalog/pg_am.h"
      38                 :             : #include "catalog/pg_attrdef.h"
      39                 :             : #include "catalog/pg_collation.h"
      40                 :             : #include "catalog/pg_constraint.h"
      41                 :             : #include "catalog/pg_depend.h"
      42                 :             : #include "catalog/pg_foreign_table.h"
      43                 :             : #include "catalog/pg_inherits.h"
      44                 :             : #include "catalog/pg_largeobject.h"
      45                 :             : #include "catalog/pg_largeobject_metadata.h"
      46                 :             : #include "catalog/pg_namespace.h"
      47                 :             : #include "catalog/pg_opclass.h"
      48                 :             : #include "catalog/pg_policy.h"
      49                 :             : #include "catalog/pg_proc.h"
      50                 :             : #include "catalog/pg_publication_rel.h"
      51                 :             : #include "catalog/pg_rewrite.h"
      52                 :             : #include "catalog/pg_statistic_ext.h"
      53                 :             : #include "catalog/pg_tablespace.h"
      54                 :             : #include "catalog/pg_trigger.h"
      55                 :             : #include "catalog/pg_type.h"
      56                 :             : #include "catalog/storage.h"
      57                 :             : #include "catalog/storage_xlog.h"
      58                 :             : #include "catalog/toasting.h"
      59                 :             : #include "commands/cluster.h"
      60                 :             : #include "commands/comment.h"
      61                 :             : #include "commands/defrem.h"
      62                 :             : #include "commands/event_trigger.h"
      63                 :             : #include "commands/sequence.h"
      64                 :             : #include "commands/tablecmds.h"
      65                 :             : #include "commands/tablespace.h"
      66                 :             : #include "commands/trigger.h"
      67                 :             : #include "commands/typecmds.h"
      68                 :             : #include "commands/user.h"
      69                 :             : #include "commands/vacuum.h"
      70                 :             : #include "common/int.h"
      71                 :             : #include "executor/executor.h"
      72                 :             : #include "foreign/fdwapi.h"
      73                 :             : #include "foreign/foreign.h"
      74                 :             : #include "miscadmin.h"
      75                 :             : #include "nodes/makefuncs.h"
      76                 :             : #include "nodes/nodeFuncs.h"
      77                 :             : #include "nodes/parsenodes.h"
      78                 :             : #include "optimizer/optimizer.h"
      79                 :             : #include "parser/parse_coerce.h"
      80                 :             : #include "parser/parse_collate.h"
      81                 :             : #include "parser/parse_expr.h"
      82                 :             : #include "parser/parse_relation.h"
      83                 :             : #include "parser/parse_type.h"
      84                 :             : #include "parser/parse_utilcmd.h"
      85                 :             : #include "parser/parser.h"
      86                 :             : #include "partitioning/partbounds.h"
      87                 :             : #include "partitioning/partdesc.h"
      88                 :             : #include "pgstat.h"
      89                 :             : #include "rewrite/rewriteDefine.h"
      90                 :             : #include "rewrite/rewriteHandler.h"
      91                 :             : #include "rewrite/rewriteManip.h"
      92                 :             : #include "storage/bufmgr.h"
      93                 :             : #include "storage/lmgr.h"
      94                 :             : #include "storage/lock.h"
      95                 :             : #include "storage/predicate.h"
      96                 :             : #include "storage/smgr.h"
      97                 :             : #include "tcop/utility.h"
      98                 :             : #include "utils/acl.h"
      99                 :             : #include "utils/builtins.h"
     100                 :             : #include "utils/fmgroids.h"
     101                 :             : #include "utils/inval.h"
     102                 :             : #include "utils/lsyscache.h"
     103                 :             : #include "utils/memutils.h"
     104                 :             : #include "utils/partcache.h"
     105                 :             : #include "utils/relcache.h"
     106                 :             : #include "utils/ruleutils.h"
     107                 :             : #include "utils/snapmgr.h"
     108                 :             : #include "utils/syscache.h"
     109                 :             : #include "utils/timestamp.h"
     110                 :             : #include "utils/typcache.h"
     111                 :             : #include "utils/usercontext.h"
     112                 :             : 
     113                 :             : /*
     114                 :             :  * ON COMMIT action list
     115                 :             :  */
     116                 :             : typedef struct OnCommitItem
     117                 :             : {
     118                 :             :         Oid                     relid;                  /* relid of relation */
     119                 :             :         OnCommitAction oncommit;        /* what to do at end of xact */
     120                 :             : 
     121                 :             :         /*
     122                 :             :          * If this entry was created during the current transaction,
     123                 :             :          * creating_subid is the ID of the creating subxact; if created in a prior
     124                 :             :          * transaction, creating_subid is zero.  If deleted during the current
     125                 :             :          * transaction, deleting_subid is the ID of the deleting subxact; if no
     126                 :             :          * deletion request is pending, deleting_subid is zero.
     127                 :             :          */
     128                 :             :         SubTransactionId creating_subid;
     129                 :             :         SubTransactionId deleting_subid;
     130                 :             : } OnCommitItem;
     131                 :             : 
     132                 :             : static List *on_commits = NIL;
     133                 :             : 
     134                 :             : 
     135                 :             : /*
     136                 :             :  * State information for ALTER TABLE
     137                 :             :  *
     138                 :             :  * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
     139                 :             :  * structs, one for each table modified by the operation (the named table
     140                 :             :  * plus any child tables that are affected).  We save lists of subcommands
     141                 :             :  * to apply to this table (possibly modified by parse transformation steps);
     142                 :             :  * these lists will be executed in Phase 2.  If a Phase 3 step is needed,
     143                 :             :  * necessary information is stored in the constraints and newvals lists.
     144                 :             :  *
     145                 :             :  * Phase 2 is divided into multiple passes; subcommands are executed in
     146                 :             :  * a pass determined by subcommand type.
     147                 :             :  */
     148                 :             : 
     149                 :             : typedef enum AlterTablePass
     150                 :             : {
     151                 :             :         AT_PASS_UNSET = -1,                     /* UNSET will cause ERROR */
     152                 :             :         AT_PASS_DROP,                           /* DROP (all flavors) */
     153                 :             :         AT_PASS_ALTER_TYPE,                     /* ALTER COLUMN TYPE */
     154                 :             :         AT_PASS_ADD_COL,                        /* ADD COLUMN */
     155                 :             :         AT_PASS_SET_EXPRESSION,         /* ALTER SET EXPRESSION */
     156                 :             :         AT_PASS_OLD_INDEX,                      /* re-add existing indexes */
     157                 :             :         AT_PASS_OLD_CONSTR,                     /* re-add existing constraints */
     158                 :             :         /* We could support a RENAME COLUMN pass here, but not currently used */
     159                 :             :         AT_PASS_ADD_CONSTR,                     /* ADD constraints (initial examination) */
     160                 :             :         AT_PASS_COL_ATTRS,                      /* set column attributes, eg NOT NULL */
     161                 :             :         AT_PASS_ADD_INDEXCONSTR,        /* ADD index-based constraints */
     162                 :             :         AT_PASS_ADD_INDEX,                      /* ADD indexes */
     163                 :             :         AT_PASS_ADD_OTHERCONSTR,        /* ADD other constraints, defaults */
     164                 :             :         AT_PASS_MISC,                           /* other stuff */
     165                 :             : } AlterTablePass;
     166                 :             : 
     167                 :             : #define AT_NUM_PASSES                   (AT_PASS_MISC + 1)
     168                 :             : 
     169                 :             : typedef struct AlteredTableInfo
     170                 :             : {
     171                 :             :         /* Information saved before any work commences: */
     172                 :             :         Oid                     relid;                  /* Relation to work on */
     173                 :             :         char            relkind;                /* Its relkind */
     174                 :             :         TupleDesc       oldDesc;                /* Pre-modification tuple descriptor */
     175                 :             : 
     176                 :             :         /*
     177                 :             :          * Transiently set during Phase 2, normally set to NULL.
     178                 :             :          *
     179                 :             :          * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
     180                 :             :          * returns control.  This can be exploited by ATExecCmd subroutines to
     181                 :             :          * close/reopen across transaction boundaries.
     182                 :             :          */
     183                 :             :         Relation        rel;
     184                 :             : 
     185                 :             :         /* Information saved by Phase 1 for Phase 2: */
     186                 :             :         List       *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
     187                 :             :         /* Information saved by Phases 1/2 for Phase 3: */
     188                 :             :         List       *constraints;        /* List of NewConstraint */
     189                 :             :         List       *newvals;            /* List of NewColumnValue */
     190                 :             :         List       *afterStmts;         /* List of utility command parsetrees */
     191                 :             :         bool            verify_new_notnull; /* T if we should recheck NOT NULL */
     192                 :             :         int                     rewrite;                /* Reason for forced rewrite, if any */
     193                 :             :         bool            chgAccessMethod;        /* T if SET ACCESS METHOD is used */
     194                 :             :         Oid                     newAccessMethod;        /* new access method; 0 means no change,
     195                 :             :                                                                          * if above is true */
     196                 :             :         Oid                     newTableSpace;  /* new tablespace; 0 means no change */
     197                 :             :         bool            chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
     198                 :             :         char            newrelpersistence;      /* if above is true */
     199                 :             :         Expr       *partition_constraint;       /* for attach partition validation */
     200                 :             :         /* true, if validating default due to some other attach/detach */
     201                 :             :         bool            validate_default;
     202                 :             :         /* Objects to rebuild after completing ALTER TYPE operations */
     203                 :             :         List       *changedConstraintOids;      /* OIDs of constraints to rebuild */
     204                 :             :         List       *changedConstraintDefs;      /* string definitions of same */
     205                 :             :         List       *changedIndexOids;   /* OIDs of indexes to rebuild */
     206                 :             :         List       *changedIndexDefs;   /* string definitions of same */
     207                 :             :         char       *replicaIdentityIndex;       /* index to reset as REPLICA IDENTITY */
     208                 :             :         char       *clusterOnIndex; /* index to use for CLUSTER */
     209                 :             :         List       *changedStatisticsOids;      /* OIDs of statistics to rebuild */
     210                 :             :         List       *changedStatisticsDefs;      /* string definitions of same */
     211                 :             : } AlteredTableInfo;
     212                 :             : 
     213                 :             : /* Struct describing one new constraint to check in Phase 3 scan */
     214                 :             : /* Note: new not-null constraints are handled elsewhere */
     215                 :             : typedef struct NewConstraint
     216                 :             : {
     217                 :             :         char       *name;                       /* Constraint name, or NULL if none */
     218                 :             :         ConstrType      contype;                /* CHECK or FOREIGN */
     219                 :             :         Oid                     refrelid;               /* PK rel, if FOREIGN */
     220                 :             :         Oid                     refindid;               /* OID of PK's index, if FOREIGN */
     221                 :             :         bool            conwithperiod;  /* Whether the new FOREIGN KEY uses PERIOD */
     222                 :             :         Oid                     conid;                  /* OID of pg_constraint entry, if FOREIGN */
     223                 :             :         Node       *qual;                       /* Check expr or CONSTR_FOREIGN Constraint */
     224                 :             :         ExprState  *qualstate;          /* Execution state for CHECK expr */
     225                 :             : } NewConstraint;
     226                 :             : 
     227                 :             : /*
     228                 :             :  * Struct describing one new column value that needs to be computed during
     229                 :             :  * Phase 3 copy (this could be either a new column with a non-null default, or
     230                 :             :  * a column that we're changing the type of).  Columns without such an entry
     231                 :             :  * are just copied from the old table during ATRewriteTable.  Note that the
     232                 :             :  * expr is an expression over *old* table values, except when is_generated
     233                 :             :  * is true; then it is an expression over columns of the *new* tuple.
     234                 :             :  */
     235                 :             : typedef struct NewColumnValue
     236                 :             : {
     237                 :             :         AttrNumber      attnum;                 /* which column */
     238                 :             :         Expr       *expr;                       /* expression to compute */
     239                 :             :         ExprState  *exprstate;          /* execution state */
     240                 :             :         bool            is_generated;   /* is it a GENERATED expression? */
     241                 :             : } NewColumnValue;
     242                 :             : 
     243                 :             : /*
     244                 :             :  * Error-reporting support for RemoveRelations
     245                 :             :  */
     246                 :             : struct dropmsgstrings
     247                 :             : {
     248                 :             :         char            kind;
     249                 :             :         int                     nonexistent_code;
     250                 :             :         const char *nonexistent_msg;
     251                 :             :         const char *skipping_msg;
     252                 :             :         const char *nota_msg;
     253                 :             :         const char *drophint_msg;
     254                 :             : };
     255                 :             : 
     256                 :             : static const struct dropmsgstrings dropmsgstringarray[] = {
     257                 :             :         {RELKIND_RELATION,
     258                 :             :                 ERRCODE_UNDEFINED_TABLE,
     259                 :             :                 gettext_noop("table \"%s\" does not exist"),
     260                 :             :                 gettext_noop("table \"%s\" does not exist, skipping"),
     261                 :             :                 gettext_noop("\"%s\" is not a table"),
     262                 :             :         gettext_noop("Use DROP TABLE to remove a table.")},
     263                 :             :         {RELKIND_SEQUENCE,
     264                 :             :                 ERRCODE_UNDEFINED_TABLE,
     265                 :             :                 gettext_noop("sequence \"%s\" does not exist"),
     266                 :             :                 gettext_noop("sequence \"%s\" does not exist, skipping"),
     267                 :             :                 gettext_noop("\"%s\" is not a sequence"),
     268                 :             :         gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
     269                 :             :         {RELKIND_VIEW,
     270                 :             :                 ERRCODE_UNDEFINED_TABLE,
     271                 :             :                 gettext_noop("view \"%s\" does not exist"),
     272                 :             :                 gettext_noop("view \"%s\" does not exist, skipping"),
     273                 :             :                 gettext_noop("\"%s\" is not a view"),
     274                 :             :         gettext_noop("Use DROP VIEW to remove a view.")},
     275                 :             :         {RELKIND_MATVIEW,
     276                 :             :                 ERRCODE_UNDEFINED_TABLE,
     277                 :             :                 gettext_noop("materialized view \"%s\" does not exist"),
     278                 :             :                 gettext_noop("materialized view \"%s\" does not exist, skipping"),
     279                 :             :                 gettext_noop("\"%s\" is not a materialized view"),
     280                 :             :         gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
     281                 :             :         {RELKIND_INDEX,
     282                 :             :                 ERRCODE_UNDEFINED_OBJECT,
     283                 :             :                 gettext_noop("index \"%s\" does not exist"),
     284                 :             :                 gettext_noop("index \"%s\" does not exist, skipping"),
     285                 :             :                 gettext_noop("\"%s\" is not an index"),
     286                 :             :         gettext_noop("Use DROP INDEX to remove an index.")},
     287                 :             :         {RELKIND_COMPOSITE_TYPE,
     288                 :             :                 ERRCODE_UNDEFINED_OBJECT,
     289                 :             :                 gettext_noop("type \"%s\" does not exist"),
     290                 :             :                 gettext_noop("type \"%s\" does not exist, skipping"),
     291                 :             :                 gettext_noop("\"%s\" is not a type"),
     292                 :             :         gettext_noop("Use DROP TYPE to remove a type.")},
     293                 :             :         {RELKIND_FOREIGN_TABLE,
     294                 :             :                 ERRCODE_UNDEFINED_OBJECT,
     295                 :             :                 gettext_noop("foreign table \"%s\" does not exist"),
     296                 :             :                 gettext_noop("foreign table \"%s\" does not exist, skipping"),
     297                 :             :                 gettext_noop("\"%s\" is not a foreign table"),
     298                 :             :         gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
     299                 :             :         {RELKIND_PARTITIONED_TABLE,
     300                 :             :                 ERRCODE_UNDEFINED_TABLE,
     301                 :             :                 gettext_noop("table \"%s\" does not exist"),
     302                 :             :                 gettext_noop("table \"%s\" does not exist, skipping"),
     303                 :             :                 gettext_noop("\"%s\" is not a table"),
     304                 :             :         gettext_noop("Use DROP TABLE to remove a table.")},
     305                 :             :         {RELKIND_PARTITIONED_INDEX,
     306                 :             :                 ERRCODE_UNDEFINED_OBJECT,
     307                 :             :                 gettext_noop("index \"%s\" does not exist"),
     308                 :             :                 gettext_noop("index \"%s\" does not exist, skipping"),
     309                 :             :                 gettext_noop("\"%s\" is not an index"),
     310                 :             :         gettext_noop("Use DROP INDEX to remove an index.")},
     311                 :             :         {'\0', 0, NULL, NULL, NULL, NULL}
     312                 :             : };
     313                 :             : 
     314                 :             : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
     315                 :             : struct DropRelationCallbackState
     316                 :             : {
     317                 :             :         /* These fields are set by RemoveRelations: */
     318                 :             :         char            expected_relkind;
     319                 :             :         LOCKMODE        heap_lockmode;
     320                 :             :         /* These fields are state to track which subsidiary locks are held: */
     321                 :             :         Oid                     heapOid;
     322                 :             :         Oid                     partParentOid;
     323                 :             :         /* These fields are passed back by RangeVarCallbackForDropRelation: */
     324                 :             :         char            actual_relkind;
     325                 :             :         char            actual_relpersistence;
     326                 :             : };
     327                 :             : 
     328                 :             : /* Alter table target-type flags for ATSimplePermissions */
     329                 :             : #define         ATT_TABLE                               0x0001
     330                 :             : #define         ATT_VIEW                                0x0002
     331                 :             : #define         ATT_MATVIEW                             0x0004
     332                 :             : #define         ATT_INDEX                               0x0008
     333                 :             : #define         ATT_COMPOSITE_TYPE              0x0010
     334                 :             : #define         ATT_FOREIGN_TABLE               0x0020
     335                 :             : #define         ATT_PARTITIONED_INDEX   0x0040
     336                 :             : #define         ATT_SEQUENCE                    0x0080
     337                 :             : #define         ATT_PARTITIONED_TABLE   0x0100
     338                 :             : 
     339                 :             : /*
     340                 :             :  * ForeignTruncateInfo
     341                 :             :  *
     342                 :             :  * Information related to truncation of foreign tables.  This is used for
     343                 :             :  * the elements in a hash table. It uses the server OID as lookup key,
     344                 :             :  * and includes a per-server list of all foreign tables involved in the
     345                 :             :  * truncation.
     346                 :             :  */
     347                 :             : typedef struct ForeignTruncateInfo
     348                 :             : {
     349                 :             :         Oid                     serverid;
     350                 :             :         List       *rels;
     351                 :             : } ForeignTruncateInfo;
     352                 :             : 
     353                 :             : /* Partial or complete FK creation in addFkConstraint() */
     354                 :             : typedef enum addFkConstraintSides
     355                 :             : {
     356                 :             :         addFkReferencedSide,
     357                 :             :         addFkReferencingSide,
     358                 :             :         addFkBothSides,
     359                 :             : } addFkConstraintSides;
     360                 :             : 
     361                 :             : /*
     362                 :             :  * Partition tables are expected to be dropped when the parent partitioned
     363                 :             :  * table gets dropped. Hence for partitioning we use AUTO dependency.
     364                 :             :  * Otherwise, for regular inheritance use NORMAL dependency.
     365                 :             :  */
     366                 :             : #define child_dependency_type(child_is_partition)       \
     367                 :             :         ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
     368                 :             : 
     369                 :             : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
     370                 :             : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
     371                 :             : static void truncate_check_activity(Relation rel);
     372                 :             : static void RangeVarCallbackForTruncate(const RangeVar *relation,
     373                 :             :                                                                                 Oid relId, Oid oldRelId, void *arg);
     374                 :             : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
     375                 :             :                                                          bool is_partition, List **supconstr,
     376                 :             :                                                          List **supnotnulls);
     377                 :             : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
     378                 :             : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
     379                 :             : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
     380                 :             : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
     381                 :             : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
     382                 :             : static void StoreCatalogInheritance(Oid relationId, List *supers,
     383                 :             :                                                                         bool child_is_partition);
     384                 :             : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
     385                 :             :                                                                          int32 seqNumber, Relation inhRelation,
     386                 :             :                                                                          bool child_is_partition);
     387                 :             : static int      findAttrByName(const char *attributeName, const List *columns);
     388                 :             : static void AlterIndexNamespaces(Relation classRel, Relation rel,
     389                 :             :                                                                  Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
     390                 :             : static void AlterSeqNamespaces(Relation classRel, Relation rel,
     391                 :             :                                                            Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
     392                 :             :                                                            LOCKMODE lockmode);
     393                 :             : static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel,
     394                 :             :                                                                                    ATAlterConstraint *cmdcon,
     395                 :             :                                                                                    bool recurse, LOCKMODE lockmode);
     396                 :             : static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
     397                 :             :                                                                                   Relation tgrel, Relation rel, HeapTuple contuple,
     398                 :             :                                                                                   bool recurse, LOCKMODE lockmode);
     399                 :             : static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
     400                 :             :                                                                                         Relation conrel, Relation tgrel,
     401                 :             :                                                                                         Oid fkrelid, Oid pkrelid,
     402                 :             :                                                                                         HeapTuple contuple, LOCKMODE lockmode,
     403                 :             :                                                                                         Oid ReferencedParentDelTrigger,
     404                 :             :                                                                                         Oid ReferencedParentUpdTrigger,
     405                 :             :                                                                                         Oid ReferencingParentInsTrigger,
     406                 :             :                                                                                         Oid ReferencingParentUpdTrigger);
     407                 :             : static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
     408                 :             :                                                                                    Relation conrel, Relation tgrel, Relation rel,
     409                 :             :                                                                                    HeapTuple contuple, bool recurse,
     410                 :             :                                                                                    List **otherrelids, LOCKMODE lockmode);
     411                 :             : static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
     412                 :             :                                                                                         Relation conrel, Relation rel,
     413                 :             :                                                                                         HeapTuple contuple, LOCKMODE lockmode);
     414                 :             : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
     415                 :             :                                                                                         bool deferrable, bool initdeferred,
     416                 :             :                                                                                         List **otherrelids);
     417                 :             : static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
     418                 :             :                                                                                          Relation conrel, Relation tgrel,
     419                 :             :                                                                                          Oid fkrelid, Oid pkrelid,
     420                 :             :                                                                                          HeapTuple contuple, LOCKMODE lockmode,
     421                 :             :                                                                                          Oid ReferencedParentDelTrigger,
     422                 :             :                                                                                          Oid ReferencedParentUpdTrigger,
     423                 :             :                                                                                          Oid ReferencingParentInsTrigger,
     424                 :             :                                                                                          Oid ReferencingParentUpdTrigger);
     425                 :             : static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
     426                 :             :                                                                                         Relation conrel, Relation tgrel, Relation rel,
     427                 :             :                                                                                         HeapTuple contuple, bool recurse,
     428                 :             :                                                                                         List **otherrelids, LOCKMODE lockmode);
     429                 :             : static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
     430                 :             :                                                                                          HeapTuple contuple);
     431                 :             : static ObjectAddress ATExecValidateConstraint(List **wqueue,
     432                 :             :                                                                                           Relation rel, char *constrName,
     433                 :             :                                                                                           bool recurse, bool recursing, LOCKMODE lockmode);
     434                 :             : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
     435                 :             :                                                                                 Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode);
     436                 :             : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
     437                 :             :                                                                                    char *constrName, HeapTuple contuple,
     438                 :             :                                                                                    bool recurse, bool recursing, LOCKMODE lockmode);
     439                 :             : static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
     440                 :             :                                                                                 HeapTuple contuple, bool recurse, bool recursing,
     441                 :             :                                                                                 LOCKMODE lockmode);
     442                 :             : static int      transformColumnNameList(Oid relId, List *colList,
     443                 :             :                                                                         int16 *attnums, Oid *atttypids, Oid *attcollids);
     444                 :             : static int      transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
     445                 :             :                                                                            List **attnamelist,
     446                 :             :                                                                            int16 *attnums, Oid *atttypids, Oid *attcollids,
     447                 :             :                                                                            Oid *opclasses, bool *pk_has_without_overlaps);
     448                 :             : static Oid      transformFkeyCheckAttrs(Relation pkrel,
     449                 :             :                                                                         int numattrs, int16 *attnums,
     450                 :             :                                                                         bool with_period, Oid *opclasses,
     451                 :             :                                                                         bool *pk_has_without_overlaps);
     452                 :             : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
     453                 :             : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
     454                 :             :                                                                          Oid *funcid);
     455                 :             : static void validateForeignKeyConstraint(char *conname,
     456                 :             :                                                                                  Relation rel, Relation pkrel,
     457                 :             :                                                                                  Oid pkindOid, Oid constraintOid, bool hasperiod);
     458                 :             : static void CheckAlterTableIsSafe(Relation rel);
     459                 :             : static void ATController(AlterTableStmt *parsetree,
     460                 :             :                                                  Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
     461                 :             :                                                  AlterTableUtilityContext *context);
     462                 :             : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
     463                 :             :                                           bool recurse, bool recursing, LOCKMODE lockmode,
     464                 :             :                                           AlterTableUtilityContext *context);
     465                 :             : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
     466                 :             :                                                           AlterTableUtilityContext *context);
     467                 :             : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
     468                 :             :                                           AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
     469                 :             :                                           AlterTableUtilityContext *context);
     470                 :             : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
     471                 :             :                                                                                   Relation rel, AlterTableCmd *cmd,
     472                 :             :                                                                                   bool recurse, LOCKMODE lockmode,
     473                 :             :                                                                                   AlterTablePass cur_pass,
     474                 :             :                                                                                   AlterTableUtilityContext *context);
     475                 :             : static void ATRewriteTables(AlterTableStmt *parsetree,
     476                 :             :                                                         List **wqueue, LOCKMODE lockmode,
     477                 :             :                                                         AlterTableUtilityContext *context);
     478                 :             : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
     479                 :             : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
     480                 :             : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
     481                 :             : static void ATSimpleRecursion(List **wqueue, Relation rel,
     482                 :             :                                                           AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
     483                 :             :                                                           AlterTableUtilityContext *context);
     484                 :             : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
     485                 :             : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
     486                 :             :                                                                   LOCKMODE lockmode,
     487                 :             :                                                                   AlterTableUtilityContext *context);
     488                 :             : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
     489                 :             :                                                                                    DropBehavior behavior);
     490                 :             : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     491                 :             :                                                         bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
     492                 :             :                                                         AlterTableUtilityContext *context);
     493                 :             : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
     494                 :             :                                                                          Relation rel, AlterTableCmd **cmd,
     495                 :             :                                                                          bool recurse, bool recursing,
     496                 :             :                                                                          LOCKMODE lockmode, AlterTablePass cur_pass,
     497                 :             :                                                                          AlterTableUtilityContext *context);
     498                 :             : static bool check_for_column_name_collision(Relation rel, const char *colname,
     499                 :             :                                                                                         bool if_not_exists);
     500                 :             : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
     501                 :             : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
     502                 :             : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
     503                 :             :                                                                            LOCKMODE lockmode);
     504                 :             : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
     505                 :             :                                                    bool is_valid, bool queue_validation);
     506                 :             : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
     507                 :             :                                                                           char *conName, char *colName,
     508                 :             :                                                                           bool recurse, bool recursing,
     509                 :             :                                                                           LOCKMODE lockmode);
     510                 :             : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
     511                 :             : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
     512                 :             :                                                                                          List *testConstraint, List *provenConstraint);
     513                 :             : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
     514                 :             :                                                                                  Node *newDefault, LOCKMODE lockmode);
     515                 :             : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
     516                 :             :                                                                                            Node *newDefault);
     517                 :             : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
     518                 :             :                                                                            Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
     519                 :             : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
     520                 :             :                                                                            Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
     521                 :             : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
     522                 :             :                                                                                 bool recurse, bool recursing);
     523                 :             : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
     524                 :             :                                                                                  Node *newExpr, LOCKMODE lockmode);
     525                 :             : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
     526                 :             : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
     527                 :             : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
     528                 :             :                                                                                  Node *newValue, LOCKMODE lockmode);
     529                 :             : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
     530                 :             :                                                                           Node *options, bool isReset, LOCKMODE lockmode);
     531                 :             : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
     532                 :             :                                                                           Node *newValue, LOCKMODE lockmode);
     533                 :             : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     534                 :             :                                                          AlterTableCmd *cmd, LOCKMODE lockmode,
     535                 :             :                                                          AlterTableUtilityContext *context);
     536                 :             : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
     537                 :             :                                                                           DropBehavior behavior,
     538                 :             :                                                                           bool recurse, bool recursing,
     539                 :             :                                                                           bool missing_ok, LOCKMODE lockmode,
     540                 :             :                                                                           ObjectAddresses *addrs);
     541                 :             : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
     542                 :             :                                                                 bool recurse, LOCKMODE lockmode,
     543                 :             :                                                                 AlterTableUtilityContext *context);
     544                 :             : static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname);
     545                 :             : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
     546                 :             :                                                                         IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     547                 :             : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
     548                 :             :                                                                                  CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     549                 :             : static ObjectAddress ATExecAddConstraint(List **wqueue,
     550                 :             :                                                                                  AlteredTableInfo *tab, Relation rel,
     551                 :             :                                                                                  Constraint *newConstraint, bool recurse, bool is_readd,
     552                 :             :                                                                                  LOCKMODE lockmode);
     553                 :             : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
     554                 :             : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
     555                 :             :                                                                                           IndexStmt *stmt, LOCKMODE lockmode);
     556                 :             : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
     557                 :             :                                                                                         AlteredTableInfo *tab, Relation rel,
     558                 :             :                                                                                         Constraint *constr,
     559                 :             :                                                                                         bool recurse, bool recursing, bool is_readd,
     560                 :             :                                                                                         LOCKMODE lockmode);
     561                 :             : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
     562                 :             :                                                                                            Relation rel, Constraint *fkconstraint,
     563                 :             :                                                                                            bool recurse, bool recursing,
     564                 :             :                                                                                            LOCKMODE lockmode);
     565                 :             : static int      validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
     566                 :             :                                                                                  int numfksetcols, int16 *fksetcolsattnums,
     567                 :             :                                                                                  List *fksetcols);
     568                 :             : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
     569                 :             :                                                                          char *constraintname,
     570                 :             :                                                                          Constraint *fkconstraint, Relation rel,
     571                 :             :                                                                          Relation pkrel, Oid indexOid,
     572                 :             :                                                                          Oid parentConstr,
     573                 :             :                                                                          int numfks, int16 *pkattnum, int16 *fkattnum,
     574                 :             :                                                                          Oid *pfeqoperators, Oid *ppeqoperators,
     575                 :             :                                                                          Oid *ffeqoperators, int numfkdelsetcols,
     576                 :             :                                                                          int16 *fkdelsetcols, bool is_internal,
     577                 :             :                                                                          bool with_period);
     578                 :             : static void addFkRecurseReferenced(Constraint *fkconstraint,
     579                 :             :                                                                    Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     580                 :             :                                                                    int numfks, int16 *pkattnum, int16 *fkattnum,
     581                 :             :                                                                    Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     582                 :             :                                                                    int numfkdelsetcols, int16 *fkdelsetcols,
     583                 :             :                                                                    bool old_check_ok,
     584                 :             :                                                                    Oid parentDelTrigger, Oid parentUpdTrigger,
     585                 :             :                                                                    bool with_period);
     586                 :             : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
     587                 :             :                                                                         Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     588                 :             :                                                                         int numfks, int16 *pkattnum, int16 *fkattnum,
     589                 :             :                                                                         Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     590                 :             :                                                                         int numfkdelsetcols, int16 *fkdelsetcols,
     591                 :             :                                                                         bool old_check_ok, LOCKMODE lockmode,
     592                 :             :                                                                         Oid parentInsTrigger, Oid parentUpdTrigger,
     593                 :             :                                                                         bool with_period);
     594                 :             : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
     595                 :             :                                                                            Relation partitionRel);
     596                 :             : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
     597                 :             : static void CloneFkReferencing(List **wqueue, Relation parentRel,
     598                 :             :                                                            Relation partRel);
     599                 :             : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
     600                 :             :                                                                                   Constraint *fkconstraint, Oid constraintOid,
     601                 :             :                                                                                   Oid indexOid,
     602                 :             :                                                                                   Oid parentInsTrigger, Oid parentUpdTrigger,
     603                 :             :                                                                                   Oid *insertTrigOid, Oid *updateTrigOid);
     604                 :             : static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
     605                 :             :                                                                                    Constraint *fkconstraint, Oid constraintOid,
     606                 :             :                                                                                    Oid indexOid,
     607                 :             :                                                                                    Oid parentDelTrigger, Oid parentUpdTrigger,
     608                 :             :                                                                                    Oid *deleteTrigOid, Oid *updateTrigOid);
     609                 :             : static bool tryAttachPartitionForeignKey(List **wqueue,
     610                 :             :                                                                                  ForeignKeyCacheInfo *fk,
     611                 :             :                                                                                  Relation partition,
     612                 :             :                                                                                  Oid parentConstrOid, int numfks,
     613                 :             :                                                                                  AttrNumber *mapped_conkey, AttrNumber *confkey,
     614                 :             :                                                                                  Oid *conpfeqop,
     615                 :             :                                                                                  Oid parentInsTrigger,
     616                 :             :                                                                                  Oid parentUpdTrigger,
     617                 :             :                                                                                  Relation trigrel);
     618                 :             : static void AttachPartitionForeignKey(List **wqueue, Relation partition,
     619                 :             :                                                                           Oid partConstrOid, Oid parentConstrOid,
     620                 :             :                                                                           Oid parentInsTrigger, Oid parentUpdTrigger,
     621                 :             :                                                                           Relation trigrel);
     622                 :             : static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
     623                 :             :                                                                           Oid conoid, Oid conrelid);
     624                 :             : static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
     625                 :             :                                                                                          Oid confrelid, Oid conrelid);
     626                 :             : static void GetForeignKeyActionTriggers(Relation trigrel,
     627                 :             :                                                                                 Oid conoid, Oid confrelid, Oid conrelid,
     628                 :             :                                                                                 Oid *deleteTriggerOid,
     629                 :             :                                                                                 Oid *updateTriggerOid);
     630                 :             : static void GetForeignKeyCheckTriggers(Relation trigrel,
     631                 :             :                                                                            Oid conoid, Oid confrelid, Oid conrelid,
     632                 :             :                                                                            Oid *insertTriggerOid,
     633                 :             :                                                                            Oid *updateTriggerOid);
     634                 :             : static void ATExecDropConstraint(Relation rel, const char *constrName,
     635                 :             :                                                                  DropBehavior behavior, bool recurse,
     636                 :             :                                                                  bool missing_ok, LOCKMODE lockmode);
     637                 :             : static ObjectAddress dropconstraint_internal(Relation rel,
     638                 :             :                                                                                          HeapTuple constraintTup, DropBehavior behavior,
     639                 :             :                                                                                          bool recurse, bool recursing,
     640                 :             :                                                                                          bool missing_ok, LOCKMODE lockmode);
     641                 :             : static void ATPrepAlterColumnType(List **wqueue,
     642                 :             :                                                                   AlteredTableInfo *tab, Relation rel,
     643                 :             :                                                                   bool recurse, bool recursing,
     644                 :             :                                                                   AlterTableCmd *cmd, LOCKMODE lockmode,
     645                 :             :                                                                   AlterTableUtilityContext *context);
     646                 :             : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
     647                 :             : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
     648                 :             :                                                                                    AlterTableCmd *cmd, LOCKMODE lockmode);
     649                 :             : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
     650                 :             :                                                                                           Relation rel, AttrNumber attnum, const char *colName);
     651                 :             : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
     652                 :             : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
     653                 :             : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
     654                 :             : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
     655                 :             :                                                                    LOCKMODE lockmode);
     656                 :             : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
     657                 :             :                                                                  char *cmd, List **wqueue, LOCKMODE lockmode,
     658                 :             :                                                                  bool rewrite);
     659                 :             : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
     660                 :             :                                                                          Oid objid, Relation rel, List *domname,
     661                 :             :                                                                          const char *conname);
     662                 :             : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
     663                 :             : static void TryReuseForeignKey(Oid oldId, Constraint *con);
     664                 :             : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
     665                 :             :                                                                                                          List *options, LOCKMODE lockmode);
     666                 :             : static void change_owner_fix_column_acls(Oid relationOid,
     667                 :             :                                                                                  Oid oldOwnerId, Oid newOwnerId);
     668                 :             : static void change_owner_recurse_to_sequences(Oid relationOid,
     669                 :             :                                                                                           Oid newOwnerId, LOCKMODE lockmode);
     670                 :             : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
     671                 :             :                                                                          LOCKMODE lockmode);
     672                 :             : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
     673                 :             : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
     674                 :             : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
     675                 :             : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
     676                 :             :                                                                         bool toLogged);
     677                 :             : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
     678                 :             :                                                                 const char *tablespacename, LOCKMODE lockmode);
     679                 :             : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
     680                 :             : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
     681                 :             : static void ATExecSetRelOptions(Relation rel, List *defList,
     682                 :             :                                                                 AlterTableType operation,
     683                 :             :                                                                 LOCKMODE lockmode);
     684                 :             : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
     685                 :             :                                                                            char fires_when, bool skip_system, bool recurse,
     686                 :             :                                                                            LOCKMODE lockmode);
     687                 :             : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
     688                 :             :                                                                         char fires_when, LOCKMODE lockmode);
     689                 :             : static void ATPrepAddInherit(Relation child_rel);
     690                 :             : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
     691                 :             : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
     692                 :             : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
     693                 :             :                                                                    DependencyType deptype);
     694                 :             : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
     695                 :             : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
     696                 :             : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
     697                 :             : static void ATExecGenericOptions(Relation rel, List *options);
     698                 :             : static void ATExecSetRowSecurity(Relation rel, bool rls);
     699                 :             : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
     700                 :             : static ObjectAddress ATExecSetCompression(Relation rel,
     701                 :             :                                                                                   const char *column, Node *newValue, LOCKMODE lockmode);
     702                 :             : 
     703                 :             : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
     704                 :             : static const char *storage_name(char c);
     705                 :             : 
     706                 :             : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
     707                 :             :                                                                                         Oid oldRelOid, void *arg);
     708                 :             : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
     709                 :             :                                                                                          Oid oldrelid, void *arg);
     710                 :             : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
     711                 :             : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
     712                 :             :                                                                   List **partexprs, Oid *partopclass, Oid *partcollation,
     713                 :             :                                                                   PartitionStrategy strategy);
     714                 :             : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
     715                 :             : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
     716                 :             :                                                           bool expect_detached);
     717                 :             : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
     718                 :             :                                                                                    PartitionCmd *cmd,
     719                 :             :                                                                                    AlterTableUtilityContext *context);
     720                 :             : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
     721                 :             : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
     722                 :             :                                                                                            List *partConstraint,
     723                 :             :                                                                                            bool validate_default);
     724                 :             : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
     725                 :             : static void DropClonedTriggersFromPartition(Oid partitionId);
     726                 :             : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
     727                 :             :                                                                                    Relation rel, RangeVar *name,
     728                 :             :                                                                                    bool concurrent);
     729                 :             : static void DetachPartitionFinalize(Relation rel, Relation partRel,
     730                 :             :                                                                         bool concurrent, Oid defaultPartOid);
     731                 :             : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
     732                 :             : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
     733                 :             :                                                                                           RangeVar *name);
     734                 :             : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
     735                 :             : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
     736                 :             :                                                                   Relation partitionTbl);
     737                 :             : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
     738                 :             : static List *GetParentedForeignKeyRefs(Relation partition);
     739                 :             : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
     740                 :             : static char GetAttributeCompression(Oid atttypid, const char *compression);
     741                 :             : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
     742                 :             : 
     743                 :             : static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
     744                 :             :                                                                   PartitionCmd *cmd, AlterTableUtilityContext *context);
     745                 :             : static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
     746                 :             :                                                                  Relation rel, PartitionCmd *cmd,
     747                 :             :                                                                  AlterTableUtilityContext *context);
     748                 :             : 
     749                 :             : /* ----------------------------------------------------------------
     750                 :             :  *              DefineRelation
     751                 :             :  *                              Creates a new relation.
     752                 :             :  *
     753                 :             :  * stmt carries parsetree information from an ordinary CREATE TABLE statement.
     754                 :             :  * The other arguments are used to extend the behavior for other cases:
     755                 :             :  * relkind: relkind to assign to the new relation
     756                 :             :  * ownerId: if not InvalidOid, use this as the new relation's owner.
     757                 :             :  * typaddress: if not null, it's set to the pg_type entry's address.
     758                 :             :  * queryString: for error reporting
     759                 :             :  *
     760                 :             :  * Note that permissions checks are done against current user regardless of
     761                 :             :  * ownerId.  A nonzero ownerId is used when someone is creating a relation
     762                 :             :  * "on behalf of" someone else, so we still want to see that the current user
     763                 :             :  * has permissions to do it.
     764                 :             :  *
     765                 :             :  * If successful, returns the address of the new relation.
     766                 :             :  * ----------------------------------------------------------------
     767                 :             :  */
     768                 :             : ObjectAddress
     769                 :        5512 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
     770                 :             :                            ObjectAddress *typaddress, const char *queryString)
     771                 :             : {
     772                 :        5512 :         char            relname[NAMEDATALEN];
     773                 :        5512 :         Oid                     namespaceId;
     774                 :        5512 :         Oid                     relationId;
     775                 :        5512 :         Oid                     tablespaceId;
     776                 :        5512 :         Relation        rel;
     777                 :        5512 :         TupleDesc       descriptor;
     778                 :        5512 :         List       *inheritOids;
     779                 :        5512 :         List       *old_constraints;
     780                 :        5512 :         List       *old_notnulls;
     781                 :        5512 :         List       *rawDefaults;
     782                 :        5512 :         List       *cookedDefaults;
     783                 :        5512 :         List       *nncols;
     784                 :        5512 :         Datum           reloptions;
     785                 :        5512 :         ListCell   *listptr;
     786                 :        5512 :         AttrNumber      attnum;
     787                 :        5512 :         bool            partitioned;
     788                 :        5512 :         const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
     789                 :        5512 :         Oid                     ofTypeId;
     790                 :             :         ObjectAddress address;
     791                 :        5512 :         LOCKMODE        parentLockmode;
     792                 :        5512 :         Oid                     accessMethodId = InvalidOid;
     793                 :             : 
     794                 :             :         /*
     795                 :             :          * Truncate relname to appropriate length (probably a waste of time, as
     796                 :             :          * parser should have done this already).
     797                 :             :          */
     798                 :        5512 :         strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
     799                 :             : 
     800                 :             :         /*
     801                 :             :          * Check consistency of arguments
     802                 :             :          */
     803                 :        5512 :         if (stmt->oncommit != ONCOMMIT_NOOP
     804   [ +  +  +  + ]:        5512 :                 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
     805   [ +  -  +  - ]:           2 :                 ereport(ERROR,
     806                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     807                 :             :                                  errmsg("ON COMMIT can only be used on temporary tables")));
     808                 :             : 
     809         [ +  + ]:        5510 :         if (stmt->partspec != NULL)
     810                 :             :         {
     811         [ +  - ]:         721 :                 if (relkind != RELKIND_RELATION)
     812   [ #  #  #  # ]:           0 :                         elog(ERROR, "unexpected relkind: %d", (int) relkind);
     813                 :             : 
     814                 :         721 :                 relkind = RELKIND_PARTITIONED_TABLE;
     815                 :         721 :                 partitioned = true;
     816                 :         721 :         }
     817                 :             :         else
     818                 :        4789 :                 partitioned = false;
     819                 :             : 
     820   [ +  +  +  + ]:        5510 :         if (relkind == RELKIND_PARTITIONED_TABLE &&
     821                 :         727 :                 stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
     822   [ +  -  +  - ]:           1 :                 ereport(ERROR,
     823                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     824                 :             :                                  errmsg("partitioned tables cannot be unlogged")));
     825                 :             : 
     826                 :             :         /*
     827                 :             :          * Look up the namespace in which we are supposed to create the relation,
     828                 :             :          * check we have permission to create there, lock it against concurrent
     829                 :             :          * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
     830                 :             :          * namespace is selected.
     831                 :             :          */
     832                 :        5509 :         namespaceId =
     833                 :        5509 :                 RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
     834                 :             : 
     835                 :             :         /*
     836                 :             :          * Security check: disallow creating temp tables from security-restricted
     837                 :             :          * code.  This is needed because calling code might not expect untrusted
     838                 :             :          * tables to appear in pg_temp at the front of its search path.
     839                 :             :          */
     840                 :        5509 :         if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
     841   [ +  +  +  - ]:        5509 :                 && InSecurityRestrictedOperation())
     842   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     843                 :             :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     844                 :             :                                  errmsg("cannot create temporary table within security-restricted operation")));
     845                 :             : 
     846                 :             :         /*
     847                 :             :          * Determine the lockmode to use when scanning parents.  A self-exclusive
     848                 :             :          * lock is needed here.
     849                 :             :          *
     850                 :             :          * For regular inheritance, if two backends attempt to add children to the
     851                 :             :          * same parent simultaneously, and that parent has no pre-existing
     852                 :             :          * children, then both will attempt to update the parent's relhassubclass
     853                 :             :          * field, leading to a "tuple concurrently updated" error.  Also, this
     854                 :             :          * interlocks against a concurrent ANALYZE on the parent table, which
     855                 :             :          * might otherwise be attempting to clear the parent's relhassubclass
     856                 :             :          * field, if its previous children were recently dropped.
     857                 :             :          *
     858                 :             :          * If the child table is a partition, then we instead grab an exclusive
     859                 :             :          * lock on the parent because its partition descriptor will be changed by
     860                 :             :          * addition of the new partition.
     861                 :             :          */
     862                 :        5509 :         parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
     863                 :             :                                           ShareUpdateExclusiveLock);
     864                 :             : 
     865                 :             :         /* Determine the list of OIDs of the parents. */
     866                 :        5509 :         inheritOids = NIL;
     867   [ +  +  +  +  :        7088 :         foreach(listptr, stmt->inhRelations)
                   +  + ]
     868                 :             :         {
     869                 :        1579 :                 RangeVar   *rv = (RangeVar *) lfirst(listptr);
     870                 :        1579 :                 Oid                     parentOid;
     871                 :             : 
     872                 :        1579 :                 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
     873                 :             : 
     874                 :             :                 /*
     875                 :             :                  * Reject duplications in the list of parents.
     876                 :             :                  */
     877         [ +  - ]:        1579 :                 if (list_member_oid(inheritOids, parentOid))
     878   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     879                 :             :                                         (errcode(ERRCODE_DUPLICATE_TABLE),
     880                 :             :                                          errmsg("relation \"%s\" would be inherited from more than once",
     881                 :             :                                                         get_rel_name(parentOid))));
     882                 :             : 
     883                 :        1579 :                 inheritOids = lappend_oid(inheritOids, parentOid);
     884                 :        1579 :         }
     885                 :             : 
     886                 :             :         /*
     887                 :             :          * Select tablespace to use: an explicitly indicated one, or (in the case
     888                 :             :          * of a partitioned table) the parent's, if it has one.
     889                 :             :          */
     890         [ +  + ]:        5509 :         if (stmt->tablespacename)
     891                 :             :         {
     892                 :           9 :                 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
     893                 :             : 
     894   [ +  +  +  + ]:           9 :                 if (partitioned && tablespaceId == MyDatabaseTableSpace)
     895   [ +  -  +  - ]:           1 :                         ereport(ERROR,
     896                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     897                 :             :                                          errmsg("cannot specify default tablespace for partitioned relations")));
     898                 :           8 :         }
     899         [ +  + ]:        5500 :         else if (stmt->partbound)
     900                 :             :         {
     901         [ +  - ]:        1223 :                 Assert(list_length(inheritOids) == 1);
     902                 :        1223 :                 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
     903                 :        1223 :         }
     904                 :             :         else
     905                 :        4277 :                 tablespaceId = InvalidOid;
     906                 :             : 
     907                 :             :         /* still nothing? use the default */
     908         [ +  + ]:        5508 :         if (!OidIsValid(tablespaceId))
     909                 :       10988 :                 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
     910                 :        5494 :                                                                                         partitioned);
     911                 :             : 
     912                 :             :         /* Check permissions except when using database's default */
     913   [ +  +  +  + ]:        5508 :         if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
     914                 :             :         {
     915                 :          21 :                 AclResult       aclresult;
     916                 :             : 
     917                 :          21 :                 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
     918                 :             :                                                                         ACL_CREATE);
     919         [ +  + ]:          21 :                 if (aclresult != ACLCHECK_OK)
     920                 :           2 :                         aclcheck_error(aclresult, OBJECT_TABLESPACE,
     921                 :           1 :                                                    get_tablespace_name(tablespaceId));
     922                 :          21 :         }
     923                 :             : 
     924                 :             :         /* In all cases disallow placing user relations in pg_global */
     925         [ +  + ]:        5508 :         if (tablespaceId == GLOBALTABLESPACE_OID)
     926   [ +  -  +  - ]:           3 :                 ereport(ERROR,
     927                 :             :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     928                 :             :                                  errmsg("only shared relations can be placed in pg_global tablespace")));
     929                 :             : 
     930                 :             :         /* Identify user ID that will own the table */
     931         [ +  + ]:        5505 :         if (!OidIsValid(ownerId))
     932                 :        5476 :                 ownerId = GetUserId();
     933                 :             : 
     934                 :             :         /*
     935                 :             :          * Parse and validate reloptions, if any.
     936                 :             :          */
     937                 :        5505 :         reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
     938                 :             :                                                                          true, false);
     939                 :             : 
     940      [ +  +  + ]:        5505 :         switch (relkind)
     941                 :             :         {
     942                 :             :                 case RELKIND_VIEW:
     943                 :         547 :                         (void) view_reloptions(reloptions, true);
     944                 :         547 :                         break;
     945                 :             :                 case RELKIND_PARTITIONED_TABLE:
     946                 :         723 :                         (void) partitioned_table_reloptions(reloptions, true);
     947                 :         723 :                         break;
     948                 :             :                 default:
     949                 :        4235 :                         (void) heap_reloptions(relkind, reloptions, true);
     950                 :        4235 :         }
     951                 :             : 
     952         [ +  + ]:        5505 :         if (stmt->ofTypename)
     953                 :             :         {
     954                 :          34 :                 AclResult       aclresult;
     955                 :             : 
     956                 :          34 :                 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
     957                 :             : 
     958                 :          34 :                 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
     959         [ +  + ]:          34 :                 if (aclresult != ACLCHECK_OK)
     960                 :           1 :                         aclcheck_error_type(aclresult, ofTypeId);
     961                 :          34 :         }
     962                 :             :         else
     963                 :        5471 :                 ofTypeId = InvalidOid;
     964                 :             : 
     965                 :             :         /*
     966                 :             :          * Look up inheritance ancestors and generate relation schema, including
     967                 :             :          * inherited attributes.  (Note that stmt->tableElts is destructively
     968                 :             :          * modified by MergeAttributes.)
     969                 :             :          */
     970                 :        5505 :         stmt->tableElts =
     971                 :       11010 :                 MergeAttributes(stmt->tableElts, inheritOids,
     972                 :        5505 :                                                 stmt->relation->relpersistence,
     973                 :        5505 :                                                 stmt->partbound != NULL,
     974                 :             :                                                 &old_constraints, &old_notnulls);
     975                 :             : 
     976                 :             :         /*
     977                 :             :          * Create a tuple descriptor from the relation schema.  Note that this
     978                 :             :          * deals with column names, types, and in-descriptor NOT NULL flags, but
     979                 :             :          * not default values, NOT NULL or CHECK constraints; we handle those
     980                 :             :          * below.
     981                 :             :          */
     982                 :        5505 :         descriptor = BuildDescForRelation(stmt->tableElts);
     983                 :             : 
     984                 :             :         /*
     985                 :             :          * Find columns with default values and prepare for insertion of the
     986                 :             :          * defaults.  Pre-cooked (that is, inherited) defaults go into a list of
     987                 :             :          * CookedConstraint structs that we'll pass to heap_create_with_catalog,
     988                 :             :          * while raw defaults go into a list of RawColumnDefault structs that will
     989                 :             :          * be processed by AddRelationNewConstraints.  (We can't deal with raw
     990                 :             :          * expressions until we can do transformExpr.)
     991                 :             :          */
     992                 :        5505 :         rawDefaults = NIL;
     993                 :        5505 :         cookedDefaults = NIL;
     994                 :        5505 :         attnum = 0;
     995                 :             : 
     996   [ +  +  +  +  :       19787 :         foreach(listptr, stmt->tableElts)
                   +  + ]
     997                 :             :         {
     998                 :       14282 :                 ColumnDef  *colDef = lfirst(listptr);
     999                 :             : 
    1000                 :       14282 :                 attnum++;
    1001         [ +  + ]:       14282 :                 if (colDef->raw_default != NULL)
    1002                 :             :                 {
    1003                 :         401 :                         RawColumnDefault *rawEnt;
    1004                 :             : 
    1005         [ +  - ]:         401 :                         Assert(colDef->cooked_default == NULL);
    1006                 :             : 
    1007                 :         401 :                         rawEnt = palloc_object(RawColumnDefault);
    1008                 :         401 :                         rawEnt->attnum = attnum;
    1009                 :         401 :                         rawEnt->raw_default = colDef->raw_default;
    1010                 :         401 :                         rawEnt->generated = colDef->generated;
    1011                 :         401 :                         rawDefaults = lappend(rawDefaults, rawEnt);
    1012                 :         401 :                 }
    1013         [ +  + ]:       13881 :                 else if (colDef->cooked_default != NULL)
    1014                 :             :                 {
    1015                 :          67 :                         CookedConstraint *cooked;
    1016                 :             : 
    1017                 :          67 :                         cooked = palloc_object(CookedConstraint);
    1018                 :          67 :                         cooked->contype = CONSTR_DEFAULT;
    1019                 :          67 :                         cooked->conoid = InvalidOid; /* until created */
    1020                 :          67 :                         cooked->name = NULL;
    1021                 :          67 :                         cooked->attnum = attnum;
    1022                 :          67 :                         cooked->expr = colDef->cooked_default;
    1023                 :          67 :                         cooked->is_enforced = true;
    1024                 :          67 :                         cooked->skip_validation = false;
    1025                 :          67 :                         cooked->is_local = true;     /* not used for defaults */
    1026                 :          67 :                         cooked->inhcount = 0;        /* ditto */
    1027                 :          67 :                         cooked->is_no_inherit = false;
    1028                 :          67 :                         cookedDefaults = lappend(cookedDefaults, cooked);
    1029                 :          67 :                 }
    1030                 :       14282 :         }
    1031                 :             : 
    1032                 :             :         /*
    1033                 :             :          * For relations with table AM and partitioned tables, select access
    1034                 :             :          * method to use: an explicitly indicated one, or (in the case of a
    1035                 :             :          * partitioned table) the parent's, if it has one.
    1036                 :             :          */
    1037         [ +  + ]:        5505 :         if (stmt->accessMethod != NULL)
    1038                 :             :         {
    1039   [ +  +  +  -  :          89 :                 Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
             +  +  +  - ]
    1040                 :          89 :                 accessMethodId = get_table_am_oid(stmt->accessMethod, false);
    1041                 :          89 :         }
    1042   [ +  +  +  -  :        5416 :         else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
             +  +  +  + ]
    1043                 :             :         {
    1044         [ +  + ]:        4519 :                 if (stmt->partbound)
    1045                 :             :                 {
    1046         [ +  - ]:        1200 :                         Assert(list_length(inheritOids) == 1);
    1047                 :        1200 :                         accessMethodId = get_rel_relam(linitial_oid(inheritOids));
    1048                 :        1200 :                 }
    1049                 :             : 
    1050   [ +  +  +  -  :        4519 :                 if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
                   +  + ]
    1051                 :        3794 :                         accessMethodId = get_table_am_oid(default_table_access_method, false);
    1052                 :        4519 :         }
    1053                 :             : 
    1054                 :             :         /*
    1055                 :             :          * Create the relation.  Inherited defaults and CHECK constraints are
    1056                 :             :          * passed in for immediate handling --- since they don't need parsing,
    1057                 :             :          * they can be stored immediately.
    1058                 :             :          */
    1059                 :       11010 :         relationId = heap_create_with_catalog(relname,
    1060                 :        5505 :                                                                                   namespaceId,
    1061                 :        5505 :                                                                                   tablespaceId,
    1062                 :             :                                                                                   InvalidOid,
    1063                 :             :                                                                                   InvalidOid,
    1064                 :        5505 :                                                                                   ofTypeId,
    1065                 :        5505 :                                                                                   ownerId,
    1066                 :        5505 :                                                                                   accessMethodId,
    1067                 :        5505 :                                                                                   descriptor,
    1068                 :       11010 :                                                                                   list_concat(cookedDefaults,
    1069                 :        5505 :                                                                                                           old_constraints),
    1070                 :        5505 :                                                                                   relkind,
    1071                 :        5505 :                                                                                   stmt->relation->relpersistence,
    1072                 :             :                                                                                   false,
    1073                 :             :                                                                                   false,
    1074                 :        5505 :                                                                                   stmt->oncommit,
    1075                 :        5505 :                                                                                   reloptions,
    1076                 :             :                                                                                   true,
    1077                 :        5505 :                                                                                   allowSystemTableMods,
    1078                 :             :                                                                                   false,
    1079                 :             :                                                                                   InvalidOid,
    1080                 :        5505 :                                                                                   typaddress);
    1081                 :             : 
    1082                 :             :         /*
    1083                 :             :          * We must bump the command counter to make the newly-created relation
    1084                 :             :          * tuple visible for opening.
    1085                 :             :          */
    1086                 :        5505 :         CommandCounterIncrement();
    1087                 :             : 
    1088                 :             :         /*
    1089                 :             :          * Open the new relation and acquire exclusive lock on it.  This isn't
    1090                 :             :          * really necessary for locking out other backends (since they can't see
    1091                 :             :          * the new rel anyway until we commit), but it keeps the lock manager from
    1092                 :             :          * complaining about deadlock risks.
    1093                 :             :          */
    1094                 :        5505 :         rel = relation_open(relationId, AccessExclusiveLock);
    1095                 :             : 
    1096                 :             :         /*
    1097                 :             :          * Now add any newly specified column default and generation expressions
    1098                 :             :          * to the new relation.  These are passed to us in the form of raw
    1099                 :             :          * parsetrees; we need to transform them to executable expression trees
    1100                 :             :          * before they can be added. The most convenient way to do that is to
    1101                 :             :          * apply the parser's transformExpr routine, but transformExpr doesn't
    1102                 :             :          * work unless we have a pre-existing relation. So, the transformation has
    1103                 :             :          * to be postponed to this final step of CREATE TABLE.
    1104                 :             :          *
    1105                 :             :          * This needs to be before processing the partitioning clauses because
    1106                 :             :          * those could refer to generated columns.
    1107                 :             :          */
    1108         [ +  + ]:        5505 :         if (rawDefaults)
    1109                 :         672 :                 AddRelationNewConstraints(rel, rawDefaults, NIL,
    1110                 :         336 :                                                                   true, true, false, queryString);
    1111                 :             : 
    1112                 :             :         /*
    1113                 :             :          * Make column generation expressions visible for use by partitioning.
    1114                 :             :          */
    1115                 :        5505 :         CommandCounterIncrement();
    1116                 :             : 
    1117                 :             :         /* Process and store partition bound, if any. */
    1118         [ +  + ]:        5505 :         if (stmt->partbound)
    1119                 :             :         {
    1120                 :        1154 :                 PartitionBoundSpec *bound;
    1121                 :        1154 :                 ParseState *pstate;
    1122                 :        1154 :                 Oid                     parentId = linitial_oid(inheritOids),
    1123                 :             :                                         defaultPartOid;
    1124                 :        1154 :                 Relation        parent,
    1125                 :        1154 :                                         defaultRel = NULL;
    1126                 :        1154 :                 ParseNamespaceItem *nsitem;
    1127                 :             : 
    1128                 :             :                 /* Already have strong enough lock on the parent */
    1129                 :        1154 :                 parent = table_open(parentId, NoLock);
    1130                 :             : 
    1131                 :             :                 /*
    1132                 :             :                  * We are going to try to validate the partition bound specification
    1133                 :             :                  * against the partition key of parentRel, so it better have one.
    1134                 :             :                  */
    1135         [ +  + ]:        1154 :                 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    1136   [ +  -  +  - ]:           3 :                         ereport(ERROR,
    1137                 :             :                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1138                 :             :                                          errmsg("\"%s\" is not partitioned",
    1139                 :             :                                                         RelationGetRelationName(parent))));
    1140                 :             : 
    1141                 :             :                 /*
    1142                 :             :                  * The partition constraint of the default partition depends on the
    1143                 :             :                  * partition bounds of every other partition. It is possible that
    1144                 :             :                  * another backend might be about to execute a query on the default
    1145                 :             :                  * partition table, and that the query relies on previously cached
    1146                 :             :                  * default partition constraints. We must therefore take a table lock
    1147                 :             :                  * strong enough to prevent all queries on the default partition from
    1148                 :             :                  * proceeding until we commit and send out a shared-cache-inval notice
    1149                 :             :                  * that will make them update their index lists.
    1150                 :             :                  *
    1151                 :             :                  * Order of locking: The relation being added won't be visible to
    1152                 :             :                  * other backends until it is committed, hence here in
    1153                 :             :                  * DefineRelation() the order of locking the default partition and the
    1154                 :             :                  * relation being added does not matter. But at all other places we
    1155                 :             :                  * need to lock the default relation before we lock the relation being
    1156                 :             :                  * added or removed i.e. we should take the lock in same order at all
    1157                 :             :                  * the places such that lock parent, lock default partition and then
    1158                 :             :                  * lock the partition so as to avoid a deadlock.
    1159                 :             :                  */
    1160                 :        1151 :                 defaultPartOid =
    1161                 :        1151 :                         get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
    1162                 :             :                                                                                                                                    true));
    1163         [ +  + ]:        1151 :                 if (OidIsValid(defaultPartOid))
    1164                 :          62 :                         defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
    1165                 :             : 
    1166                 :             :                 /* Transform the bound values */
    1167                 :        1151 :                 pstate = make_parsestate(NULL);
    1168                 :        1151 :                 pstate->p_sourcetext = queryString;
    1169                 :             : 
    1170                 :             :                 /*
    1171                 :             :                  * Add an nsitem containing this relation, so that transformExpr
    1172                 :             :                  * called on partition bound expressions is able to report errors
    1173                 :             :                  * using a proper context.
    1174                 :             :                  */
    1175                 :        1151 :                 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
    1176                 :             :                                                                                            NULL, false, false);
    1177                 :        1151 :                 addNSItemToQuery(pstate, nsitem, false, true, true);
    1178                 :             : 
    1179                 :        1151 :                 bound = transformPartitionBound(pstate, parent, stmt->partbound);
    1180                 :             : 
    1181                 :             :                 /*
    1182                 :             :                  * Check first that the new partition's bound is valid and does not
    1183                 :             :                  * overlap with any of existing partitions of the parent.
    1184                 :             :                  */
    1185                 :        1151 :                 check_new_partition_bound(relname, parent, bound, pstate);
    1186                 :             : 
    1187                 :             :                 /*
    1188                 :             :                  * If the default partition exists, its partition constraints will
    1189                 :             :                  * change after the addition of this new partition such that it won't
    1190                 :             :                  * allow any row that qualifies for this new partition. So, check that
    1191                 :             :                  * the existing data in the default partition satisfies the constraint
    1192                 :             :                  * as it will exist after adding this partition.
    1193                 :             :                  */
    1194         [ +  + ]:        1151 :                 if (OidIsValid(defaultPartOid))
    1195                 :             :                 {
    1196                 :          57 :                         check_default_partition_contents(parent, defaultRel, bound);
    1197                 :             :                         /* Keep the lock until commit. */
    1198                 :          57 :                         table_close(defaultRel, NoLock);
    1199                 :          57 :                 }
    1200                 :             : 
    1201                 :             :                 /* Update the pg_class entry. */
    1202                 :        1151 :                 StorePartitionBound(rel, parent, bound);
    1203                 :             : 
    1204                 :        1151 :                 table_close(parent, NoLock);
    1205                 :        1151 :         }
    1206                 :             : 
    1207                 :             :         /* Store inheritance information for new rel. */
    1208                 :        5502 :         StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
    1209                 :             : 
    1210                 :             :         /*
    1211                 :             :          * Process the partitioning specification (if any) and store the partition
    1212                 :             :          * key information into the catalog.
    1213                 :             :          */
    1214         [ +  + ]:        5502 :         if (partitioned)
    1215                 :             :         {
    1216                 :         722 :                 ParseState *pstate;
    1217                 :         722 :                 int                     partnatts;
    1218                 :         722 :                 AttrNumber      partattrs[PARTITION_MAX_KEYS];
    1219                 :         722 :                 Oid                     partopclass[PARTITION_MAX_KEYS];
    1220                 :         722 :                 Oid                     partcollation[PARTITION_MAX_KEYS];
    1221                 :         722 :                 List       *partexprs = NIL;
    1222                 :             : 
    1223                 :         722 :                 pstate = make_parsestate(NULL);
    1224                 :         722 :                 pstate->p_sourcetext = queryString;
    1225                 :             : 
    1226                 :         722 :                 partnatts = list_length(stmt->partspec->partParams);
    1227                 :             : 
    1228                 :             :                 /* Protect fixed-size arrays here and in executor */
    1229         [ +  - ]:         722 :                 if (partnatts > PARTITION_MAX_KEYS)
    1230   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    1231                 :             :                                         (errcode(ERRCODE_TOO_MANY_COLUMNS),
    1232                 :             :                                          errmsg("cannot partition using more than %d columns",
    1233                 :             :                                                         PARTITION_MAX_KEYS)));
    1234                 :             : 
    1235                 :             :                 /*
    1236                 :             :                  * We need to transform the raw parsetrees corresponding to partition
    1237                 :             :                  * expressions into executable expression trees.  Like column defaults
    1238                 :             :                  * and CHECK constraints, we could not have done the transformation
    1239                 :             :                  * earlier.
    1240                 :             :                  */
    1241                 :         722 :                 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
    1242                 :             : 
    1243                 :        1444 :                 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
    1244                 :         722 :                                                           partattrs, &partexprs, partopclass,
    1245                 :         722 :                                                           partcollation, stmt->partspec->strategy);
    1246                 :             : 
    1247                 :        1444 :                 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
    1248                 :         722 :                                                   partexprs,
    1249                 :         722 :                                                   partopclass, partcollation);
    1250                 :             : 
    1251                 :             :                 /* make it all visible */
    1252                 :         722 :                 CommandCounterIncrement();
    1253                 :         722 :         }
    1254                 :             : 
    1255                 :             :         /*
    1256                 :             :          * If we're creating a partition, create now all the indexes, triggers,
    1257                 :             :          * FKs defined in the parent.
    1258                 :             :          *
    1259                 :             :          * We can't do it earlier, because DefineIndex wants to know the partition
    1260                 :             :          * key which we just stored.
    1261                 :             :          */
    1262         [ +  + ]:        5502 :         if (stmt->partbound)
    1263                 :             :         {
    1264                 :        1149 :                 Oid                     parentId = linitial_oid(inheritOids);
    1265                 :        1149 :                 Relation        parent;
    1266                 :        1149 :                 List       *idxlist;
    1267                 :        1149 :                 ListCell   *cell;
    1268                 :             : 
    1269                 :             :                 /* Already have strong enough lock on the parent */
    1270                 :        1149 :                 parent = table_open(parentId, NoLock);
    1271                 :        1149 :                 idxlist = RelationGetIndexList(parent);
    1272                 :             : 
    1273                 :             :                 /*
    1274                 :             :                  * For each index in the parent table, create one in the partition
    1275                 :             :                  */
    1276   [ +  +  +  +  :        1373 :                 foreach(cell, idxlist)
                   +  + ]
    1277                 :             :                 {
    1278                 :         226 :                         Relation        idxRel = index_open(lfirst_oid(cell), AccessShareLock);
    1279                 :         226 :                         AttrMap    *attmap;
    1280                 :         226 :                         IndexStmt  *idxstmt;
    1281                 :         226 :                         Oid                     constraintOid;
    1282                 :             : 
    1283         [ +  + ]:         226 :                         if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    1284                 :             :                         {
    1285         [ +  + ]:           6 :                                 if (idxRel->rd_index->indisunique)
    1286   [ +  -  +  - ]:           2 :                                         ereport(ERROR,
    1287                 :             :                                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1288                 :             :                                                          errmsg("cannot create foreign partition of partitioned table \"%s\"",
    1289                 :             :                                                                         RelationGetRelationName(parent)),
    1290                 :             :                                                          errdetail("Table \"%s\" contains indexes that are unique.",
    1291                 :             :                                                                            RelationGetRelationName(parent))));
    1292                 :             :                                 else
    1293                 :             :                                 {
    1294                 :           4 :                                         index_close(idxRel, AccessShareLock);
    1295                 :           4 :                                         continue;
    1296                 :             :                                 }
    1297                 :           0 :                         }
    1298                 :             : 
    1299                 :         440 :                         attmap = build_attrmap_by_name(RelationGetDescr(rel),
    1300                 :         220 :                                                                                    RelationGetDescr(parent),
    1301                 :             :                                                                                    false);
    1302                 :         220 :                         idxstmt =
    1303                 :         440 :                                 generateClonedIndexStmt(NULL, idxRel,
    1304                 :         220 :                                                                                 attmap, &constraintOid);
    1305                 :         220 :                         DefineIndex(NULL,
    1306                 :         220 :                                                 RelationGetRelid(rel),
    1307                 :         220 :                                                 idxstmt,
    1308                 :             :                                                 InvalidOid,
    1309                 :         220 :                                                 RelationGetRelid(idxRel),
    1310                 :         220 :                                                 constraintOid,
    1311                 :             :                                                 -1,
    1312                 :             :                                                 false, false, false, false, false);
    1313                 :             : 
    1314                 :         220 :                         index_close(idxRel, AccessShareLock);
    1315      [ -  +  + ]:         224 :                 }
    1316                 :             : 
    1317                 :        1147 :                 list_free(idxlist);
    1318                 :             : 
    1319                 :             :                 /*
    1320                 :             :                  * If there are any row-level triggers, clone them to the new
    1321                 :             :                  * partition.
    1322                 :             :                  */
    1323         [ +  + ]:        1147 :                 if (parent->trigdesc != NULL)
    1324                 :          71 :                         CloneRowTriggersToPartition(parent, rel);
    1325                 :             : 
    1326                 :             :                 /*
    1327                 :             :                  * And foreign keys too.  Note that because we're freshly creating the
    1328                 :             :                  * table, there is no need to verify these new constraints.
    1329                 :             :                  */
    1330                 :        1147 :                 CloneForeignKeyConstraints(NULL, parent, rel);
    1331                 :             : 
    1332                 :        1147 :                 table_close(parent, NoLock);
    1333                 :        1147 :         }
    1334                 :             : 
    1335                 :             :         /*
    1336                 :             :          * Now add any newly specified CHECK constraints to the new relation. Same
    1337                 :             :          * as for defaults above, but these need to come after partitioning is set
    1338                 :             :          * up.
    1339                 :             :          */
    1340         [ +  + ]:        5500 :         if (stmt->constraints)
    1341                 :         204 :                 AddRelationNewConstraints(rel, NIL, stmt->constraints,
    1342                 :         102 :                                                                   true, true, false, queryString);
    1343                 :             : 
    1344                 :             :         /*
    1345                 :             :          * Finally, merge the not-null constraints that are declared directly with
    1346                 :             :          * those that come from parent relations (making sure to count inheritance
    1347                 :             :          * appropriately for each), create them, and set the attnotnull flag on
    1348                 :             :          * columns that don't yet have it.
    1349                 :             :          */
    1350                 :       11000 :         nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
    1351                 :        5500 :                                                                                    old_notnulls);
    1352   [ +  +  +  +  :       12191 :         foreach_int(attrnum, nncols)
             +  +  +  + ]
    1353                 :        6691 :                 set_attnotnull(NULL, rel, attrnum, true, false);
    1354                 :             : 
    1355                 :        5500 :         ObjectAddressSet(address, RelationRelationId, relationId);
    1356                 :             : 
    1357                 :             :         /*
    1358                 :             :          * Clean up.  We keep lock on new relation (although it shouldn't be
    1359                 :             :          * visible to anyone else anyway, until commit).
    1360                 :             :          */
    1361                 :        5500 :         relation_close(rel, NoLock);
    1362                 :             : 
    1363                 :             :         return address;
    1364                 :        5500 : }
    1365                 :             : 
    1366                 :             : /*
    1367                 :             :  * BuildDescForRelation
    1368                 :             :  *
    1369                 :             :  * Given a list of ColumnDef nodes, build a TupleDesc.
    1370                 :             :  *
    1371                 :             :  * Note: This is only for the limited purpose of table and view creation.  Not
    1372                 :             :  * everything is filled in.  A real tuple descriptor should be obtained from
    1373                 :             :  * the relcache.
    1374                 :             :  */
    1375                 :             : TupleDesc
    1376                 :        5968 : BuildDescForRelation(const List *columns)
    1377                 :             : {
    1378                 :        5968 :         int                     natts;
    1379                 :        5968 :         AttrNumber      attnum;
    1380                 :        5968 :         ListCell   *l;
    1381                 :        5968 :         TupleDesc       desc;
    1382                 :        5968 :         char       *attname;
    1383                 :        5968 :         Oid                     atttypid;
    1384                 :        5968 :         int32           atttypmod;
    1385                 :        5968 :         Oid                     attcollation;
    1386                 :        5968 :         int                     attdim;
    1387                 :             : 
    1388                 :             :         /*
    1389                 :             :          * allocate a new tuple descriptor
    1390                 :             :          */
    1391                 :        5968 :         natts = list_length(columns);
    1392                 :        5968 :         desc = CreateTemplateTupleDesc(natts);
    1393                 :             : 
    1394                 :        5968 :         attnum = 0;
    1395                 :             : 
    1396   [ +  +  +  +  :       20995 :         foreach(l, columns)
                   +  + ]
    1397                 :             :         {
    1398                 :       15027 :                 ColumnDef  *entry = lfirst(l);
    1399                 :       15027 :                 AclResult       aclresult;
    1400                 :       15027 :                 Form_pg_attribute att;
    1401                 :             : 
    1402                 :             :                 /*
    1403                 :             :                  * for each entry in the list, get the name and type information from
    1404                 :             :                  * the list and have TupleDescInitEntry fill in the attribute
    1405                 :             :                  * information we need.
    1406                 :             :                  */
    1407                 :       15027 :                 attnum++;
    1408                 :             : 
    1409                 :       15027 :                 attname = entry->colname;
    1410                 :       15027 :                 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
    1411                 :             : 
    1412                 :       15027 :                 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
    1413         [ +  + ]:       15027 :                 if (aclresult != ACLCHECK_OK)
    1414                 :           7 :                         aclcheck_error_type(aclresult, atttypid);
    1415                 :             : 
    1416                 :       15027 :                 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
    1417                 :       15027 :                 attdim = list_length(entry->typeName->arrayBounds);
    1418         [ +  - ]:       15027 :                 if (attdim > PG_INT16_MAX)
    1419   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    1420                 :             :                                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1421                 :             :                                         errmsg("too many array dimensions"));
    1422                 :             : 
    1423         [ +  - ]:       15027 :                 if (entry->typeName->setof)
    1424   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    1425                 :             :                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    1426                 :             :                                          errmsg("column \"%s\" cannot be declared SETOF",
    1427                 :             :                                                         attname)));
    1428                 :             : 
    1429                 :       30054 :                 TupleDescInitEntry(desc, attnum, attname,
    1430                 :       15027 :                                                    atttypid, atttypmod, attdim);
    1431                 :       15027 :                 att = TupleDescAttr(desc, attnum - 1);
    1432                 :             : 
    1433                 :             :                 /* Override TupleDescInitEntry's settings as requested */
    1434                 :       15027 :                 TupleDescInitEntryCollation(desc, attnum, attcollation);
    1435                 :             : 
    1436                 :             :                 /* Fill in additional stuff not handled by TupleDescInitEntry */
    1437                 :       15027 :                 att->attnotnull = entry->is_not_null;
    1438                 :       15027 :                 att->attislocal = entry->is_local;
    1439                 :       15027 :                 att->attinhcount = entry->inhcount;
    1440                 :       15027 :                 att->attidentity = entry->identity;
    1441                 :       15027 :                 att->attgenerated = entry->generated;
    1442                 :       15027 :                 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
    1443         [ +  + ]:       15027 :                 if (entry->storage)
    1444                 :        3286 :                         att->attstorage = entry->storage;
    1445         [ +  + ]:       11741 :                 else if (entry->storage_name)
    1446                 :          10 :                         att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
    1447                 :             : 
    1448                 :       15027 :                 populate_compact_attribute(desc, attnum - 1);
    1449                 :       15027 :         }
    1450                 :             : 
    1451                 :       11936 :         return desc;
    1452                 :        5968 : }
    1453                 :             : 
    1454                 :             : /*
    1455                 :             :  * Emit the right error or warning message for a "DROP" command issued on a
    1456                 :             :  * non-existent relation
    1457                 :             :  */
    1458                 :             : static void
    1459                 :          57 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
    1460                 :             : {
    1461                 :          57 :         const struct dropmsgstrings *rentry;
    1462                 :             : 
    1463   [ +  +  +  + ]:          57 :         if (rel->schemaname != NULL &&
    1464                 :          20 :                 !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
    1465                 :             :         {
    1466         [ +  - ]:           7 :                 if (!missing_ok)
    1467                 :             :                 {
    1468   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    1469                 :             :                                         (errcode(ERRCODE_UNDEFINED_SCHEMA),
    1470                 :             :                                          errmsg("schema \"%s\" does not exist", rel->schemaname)));
    1471                 :           0 :                 }
    1472                 :             :                 else
    1473                 :             :                 {
    1474   [ -  +  +  - ]:           7 :                         ereport(NOTICE,
    1475                 :             :                                         (errmsg("schema \"%s\" does not exist, skipping",
    1476                 :             :                                                         rel->schemaname)));
    1477                 :             :                 }
    1478                 :           7 :                 return;
    1479                 :             :         }
    1480                 :             : 
    1481         [ -  + ]:         103 :         for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1482                 :             :         {
    1483         [ +  + ]:         103 :                 if (rentry->kind == rightkind)
    1484                 :             :                 {
    1485         [ +  + ]:          50 :                         if (!missing_ok)
    1486                 :             :                         {
    1487   [ +  -  +  - ]:          23 :                                 ereport(ERROR,
    1488                 :             :                                                 (errcode(rentry->nonexistent_code),
    1489                 :             :                                                  errmsg(rentry->nonexistent_msg, rel->relname)));
    1490                 :           0 :                         }
    1491                 :             :                         else
    1492                 :             :                         {
    1493   [ -  +  +  - ]:          27 :                                 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
    1494                 :          27 :                                 break;
    1495                 :             :                         }
    1496                 :           0 :                 }
    1497                 :          53 :         }
    1498                 :             : 
    1499         [ -  + ]:          27 :         Assert(rentry->kind != '\0');        /* Should be impossible */
    1500         [ -  + ]:          34 : }
    1501                 :             : 
    1502                 :             : /*
    1503                 :             :  * Emit the right error message for a "DROP" command issued on a
    1504                 :             :  * relation of the wrong type
    1505                 :             :  */
    1506                 :             : static void
    1507                 :           0 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
    1508                 :             : {
    1509                 :           0 :         const struct dropmsgstrings *rentry;
    1510                 :           0 :         const struct dropmsgstrings *wentry;
    1511                 :             : 
    1512         [ #  # ]:           0 :         for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1513         [ #  # ]:           0 :                 if (rentry->kind == rightkind)
    1514                 :           0 :                         break;
    1515         [ #  # ]:           0 :         Assert(rentry->kind != '\0');
    1516                 :             : 
    1517         [ #  # ]:           0 :         for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
    1518         [ #  # ]:           0 :                 if (wentry->kind == wrongkind)
    1519                 :           0 :                         break;
    1520                 :             :         /* wrongkind could be something we don't have in our table... */
    1521                 :             : 
    1522   [ #  #  #  #  :           0 :         ereport(ERROR,
                   #  # ]
    1523                 :             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1524                 :             :                          errmsg(rentry->nota_msg, relname),
    1525                 :             :                          (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
    1526                 :           0 : }
    1527                 :             : 
    1528                 :             : /*
    1529                 :             :  * RemoveRelations
    1530                 :             :  *              Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
    1531                 :             :  *              DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
    1532                 :             :  */
    1533                 :             : void
    1534                 :        1932 : RemoveRelations(DropStmt *drop)
    1535                 :             : {
    1536                 :        1932 :         ObjectAddresses *objects;
    1537                 :        1932 :         char            relkind;
    1538                 :        1932 :         ListCell   *cell;
    1539                 :        1932 :         int                     flags = 0;
    1540                 :        1932 :         LOCKMODE        lockmode = AccessExclusiveLock;
    1541                 :             : 
    1542                 :             :         /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
    1543         [ +  + ]:        1932 :         if (drop->concurrent)
    1544                 :             :         {
    1545                 :             :                 /*
    1546                 :             :                  * Note that for temporary relations this lock may get upgraded later
    1547                 :             :                  * on, but as no other session can access a temporary relation, this
    1548                 :             :                  * is actually fine.
    1549                 :             :                  */
    1550                 :          13 :                 lockmode = ShareUpdateExclusiveLock;
    1551         [ +  - ]:          13 :                 Assert(drop->removeType == OBJECT_INDEX);
    1552         [ +  + ]:          13 :                 if (list_length(drop->objects) != 1)
    1553   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    1554                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1555                 :             :                                          errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
    1556         [ +  - ]:          12 :                 if (drop->behavior == DROP_CASCADE)
    1557   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    1558                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1559                 :             :                                          errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
    1560                 :          12 :         }
    1561                 :             : 
    1562                 :             :         /*
    1563                 :             :          * First we identify all the relations, then we delete them in a single
    1564                 :             :          * performMultipleDeletions() call.  This is to avoid unwanted DROP
    1565                 :             :          * RESTRICT errors if one of the relations depends on another.
    1566                 :             :          */
    1567                 :             : 
    1568                 :             :         /* Determine required relkind */
    1569   [ +  +  +  +  :        1931 :         switch (drop->removeType)
                +  +  - ]
    1570                 :             :         {
    1571                 :             :                 case OBJECT_TABLE:
    1572                 :        1612 :                         relkind = RELKIND_RELATION;
    1573                 :        1612 :                         break;
    1574                 :             : 
    1575                 :             :                 case OBJECT_INDEX:
    1576                 :         114 :                         relkind = RELKIND_INDEX;
    1577                 :         114 :                         break;
    1578                 :             : 
    1579                 :             :                 case OBJECT_SEQUENCE:
    1580                 :          26 :                         relkind = RELKIND_SEQUENCE;
    1581                 :          26 :                         break;
    1582                 :             : 
    1583                 :             :                 case OBJECT_VIEW:
    1584                 :         145 :                         relkind = RELKIND_VIEW;
    1585                 :         145 :                         break;
    1586                 :             : 
    1587                 :             :                 case OBJECT_MATVIEW:
    1588                 :          16 :                         relkind = RELKIND_MATVIEW;
    1589                 :          16 :                         break;
    1590                 :             : 
    1591                 :             :                 case OBJECT_FOREIGN_TABLE:
    1592                 :          18 :                         relkind = RELKIND_FOREIGN_TABLE;
    1593                 :          18 :                         break;
    1594                 :             : 
    1595                 :             :                 default:
    1596   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized drop object type: %d",
    1597                 :             :                                  (int) drop->removeType);
    1598                 :           0 :                         relkind = 0;            /* keep compiler quiet */
    1599                 :           0 :                         break;
    1600                 :             :         }
    1601                 :             : 
    1602                 :             :         /* Lock and validate each relation; build a list of object addresses */
    1603                 :        1931 :         objects = new_object_addresses();
    1604                 :             : 
    1605   [ +  +  +  +  :        4173 :         foreach(cell, drop->objects)
                   +  + ]
    1606                 :             :         {
    1607                 :        2243 :                 RangeVar   *rel = makeRangeVarFromNameList((List *) lfirst(cell));
    1608                 :        2243 :                 Oid                     relOid;
    1609                 :        2243 :                 ObjectAddress obj;
    1610                 :        2243 :                 struct DropRelationCallbackState state;
    1611                 :             : 
    1612                 :             :                 /*
    1613                 :             :                  * These next few steps are a great deal like relation_openrv, but we
    1614                 :             :                  * don't bother building a relcache entry since we don't need it.
    1615                 :             :                  *
    1616                 :             :                  * Check for shared-cache-inval messages before trying to access the
    1617                 :             :                  * relation.  This is needed to cover the case where the name
    1618                 :             :                  * identifies a rel that has been dropped and recreated since the
    1619                 :             :                  * start of our transaction: if we don't flush the old syscache entry,
    1620                 :             :                  * then we'll latch onto that entry and suffer an error later.
    1621                 :             :                  */
    1622                 :        2243 :                 AcceptInvalidationMessages();
    1623                 :             : 
    1624                 :             :                 /* Look up the appropriate relation using namespace search. */
    1625                 :        2243 :                 state.expected_relkind = relkind;
    1626                 :        2243 :                 state.heap_lockmode = drop->concurrent ?
    1627                 :             :                         ShareUpdateExclusiveLock : AccessExclusiveLock;
    1628                 :             :                 /* We must initialize these fields to show that no locks are held: */
    1629                 :        2243 :                 state.heapOid = InvalidOid;
    1630                 :        2243 :                 state.partParentOid = InvalidOid;
    1631                 :             : 
    1632                 :        2243 :                 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
    1633                 :             :                                                                                   RangeVarCallbackForDropRelation,
    1634                 :             :                                                                                   &state);
    1635                 :             : 
    1636                 :             :                 /* Not there? */
    1637         [ +  + ]:        2243 :                 if (!OidIsValid(relOid))
    1638                 :             :                 {
    1639                 :          57 :                         DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
    1640                 :          57 :                         continue;
    1641                 :             :                 }
    1642                 :             : 
    1643                 :             :                 /*
    1644                 :             :                  * Decide if concurrent mode needs to be used here or not.  The
    1645                 :             :                  * callback retrieved the rel's persistence for us.
    1646                 :             :                  */
    1647   [ +  +  +  + ]:        2186 :                 if (drop->concurrent &&
    1648                 :          11 :                         state.actual_relpersistence != RELPERSISTENCE_TEMP)
    1649                 :             :                 {
    1650         [ +  - ]:           8 :                         Assert(list_length(drop->objects) == 1 &&
    1651                 :             :                                    drop->removeType == OBJECT_INDEX);
    1652                 :           8 :                         flags |= PERFORM_DELETION_CONCURRENTLY;
    1653                 :           8 :                 }
    1654                 :             : 
    1655                 :             :                 /*
    1656                 :             :                  * Concurrent index drop cannot be used with partitioned indexes,
    1657                 :             :                  * either.
    1658                 :             :                  */
    1659   [ +  +  +  + ]:        2186 :                 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
    1660                 :           8 :                         state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1661   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    1662                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1663                 :             :                                          errmsg("cannot drop partitioned index \"%s\" concurrently",
    1664                 :             :                                                         rel->relname)));
    1665                 :             : 
    1666                 :             :                 /*
    1667                 :             :                  * If we're told to drop a partitioned index, we must acquire lock on
    1668                 :             :                  * all the children of its parent partitioned table before proceeding.
    1669                 :             :                  * Otherwise we'd try to lock the child index partitions before their
    1670                 :             :                  * tables, leading to potential deadlock against other sessions that
    1671                 :             :                  * will lock those objects in the other order.
    1672                 :             :                  */
    1673         [ +  + ]:        2185 :                 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1674                 :          24 :                         (void) find_all_inheritors(state.heapOid,
    1675                 :          12 :                                                                            state.heap_lockmode,
    1676                 :             :                                                                            NULL);
    1677                 :             : 
    1678                 :             :                 /* OK, we're ready to delete this one */
    1679                 :        2185 :                 obj.classId = RelationRelationId;
    1680                 :        2185 :                 obj.objectId = relOid;
    1681                 :        2185 :                 obj.objectSubId = 0;
    1682                 :             : 
    1683                 :        2185 :                 add_exact_object_address(&obj, objects);
    1684      [ -  +  + ]:        2242 :         }
    1685                 :             : 
    1686                 :        1930 :         performMultipleDeletions(objects, drop->behavior, flags);
    1687                 :             : 
    1688                 :        1930 :         free_object_addresses(objects);
    1689                 :        1930 : }
    1690                 :             : 
    1691                 :             : /*
    1692                 :             :  * Before acquiring a table lock, check whether we have sufficient rights.
    1693                 :             :  * In the case of DROP INDEX, also try to lock the table before the index.
    1694                 :             :  * Also, if the table to be dropped is a partition, we try to lock the parent
    1695                 :             :  * first.
    1696                 :             :  */
    1697                 :             : static void
    1698                 :        2302 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
    1699                 :             :                                                                 void *arg)
    1700                 :             : {
    1701                 :        2302 :         HeapTuple       tuple;
    1702                 :        2302 :         struct DropRelationCallbackState *state;
    1703                 :        2302 :         char            expected_relkind;
    1704                 :        2302 :         bool            is_partition;
    1705                 :        2302 :         Form_pg_class classform;
    1706                 :        2302 :         LOCKMODE        heap_lockmode;
    1707                 :        2302 :         bool            invalid_system_index = false;
    1708                 :             : 
    1709                 :        2302 :         state = (struct DropRelationCallbackState *) arg;
    1710                 :        2302 :         heap_lockmode = state->heap_lockmode;
    1711                 :             : 
    1712                 :             :         /*
    1713                 :             :          * If we previously locked some other index's heap, and the name we're
    1714                 :             :          * looking up no longer refers to that relation, release the now-useless
    1715                 :             :          * lock.
    1716                 :             :          */
    1717   [ +  +  +  - ]:        2302 :         if (relOid != oldRelOid && OidIsValid(state->heapOid))
    1718                 :             :         {
    1719                 :           0 :                 UnlockRelationOid(state->heapOid, heap_lockmode);
    1720                 :           0 :                 state->heapOid = InvalidOid;
    1721                 :           0 :         }
    1722                 :             : 
    1723                 :             :         /*
    1724                 :             :          * Similarly, if we previously locked some other partition's heap, and the
    1725                 :             :          * name we're looking up no longer refers to that relation, release the
    1726                 :             :          * now-useless lock.
    1727                 :             :          */
    1728   [ +  +  +  - ]:        2302 :         if (relOid != oldRelOid && OidIsValid(state->partParentOid))
    1729                 :             :         {
    1730                 :           0 :                 UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
    1731                 :           0 :                 state->partParentOid = InvalidOid;
    1732                 :           0 :         }
    1733                 :             : 
    1734                 :             :         /* Didn't find a relation, so no need for locking or permission checks. */
    1735         [ +  + ]:        2302 :         if (!OidIsValid(relOid))
    1736                 :          58 :                 return;
    1737                 :             : 
    1738                 :        2244 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
    1739         [ +  - ]:        2244 :         if (!HeapTupleIsValid(tuple))
    1740                 :           0 :                 return;                                 /* concurrently dropped, so nothing to do */
    1741                 :        2244 :         classform = (Form_pg_class) GETSTRUCT(tuple);
    1742                 :        2244 :         is_partition = classform->relispartition;
    1743                 :             : 
    1744                 :             :         /* Pass back some data to save lookups in RemoveRelations */
    1745                 :        2244 :         state->actual_relkind = classform->relkind;
    1746                 :        2244 :         state->actual_relpersistence = classform->relpersistence;
    1747                 :             : 
    1748                 :             :         /*
    1749                 :             :          * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
    1750                 :             :          * but RemoveRelations() can only pass one relkind for a given relation.
    1751                 :             :          * It chooses RELKIND_RELATION for both regular and partitioned tables.
    1752                 :             :          * That means we must be careful before giving the wrong type error when
    1753                 :             :          * the relation is RELKIND_PARTITIONED_TABLE.  An equivalent problem
    1754                 :             :          * exists with indexes.
    1755                 :             :          */
    1756         [ +  + ]:        2244 :         if (classform->relkind == RELKIND_PARTITIONED_TABLE)
    1757                 :         424 :                 expected_relkind = RELKIND_RELATION;
    1758         [ +  + ]:        1820 :         else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
    1759                 :          13 :                 expected_relkind = RELKIND_INDEX;
    1760                 :             :         else
    1761                 :        1807 :                 expected_relkind = classform->relkind;
    1762                 :             : 
    1763         [ +  - ]:        2244 :         if (state->expected_relkind != expected_relkind)
    1764                 :           0 :                 DropErrorMsgWrongType(rel->relname, classform->relkind,
    1765                 :           0 :                                                           state->expected_relkind);
    1766                 :             : 
    1767                 :             :         /* Allow DROP to either table owner or schema owner */
    1768   [ +  +  -  + ]:        2244 :         if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
    1769                 :           3 :                 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
    1770                 :           3 :                 aclcheck_error(ACLCHECK_NOT_OWNER,
    1771                 :           3 :                                            get_relkind_objtype(classform->relkind),
    1772                 :           3 :                                            rel->relname);
    1773                 :             : 
    1774                 :             :         /*
    1775                 :             :          * Check the case of a system index that might have been invalidated by a
    1776                 :             :          * failed concurrent process and allow its drop. For the time being, this
    1777                 :             :          * only concerns indexes of toast relations that became invalid during a
    1778                 :             :          * REINDEX CONCURRENTLY process.
    1779                 :             :          */
    1780   [ -  +  #  # ]:        2244 :         if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
    1781                 :             :         {
    1782                 :           0 :                 HeapTuple       locTuple;
    1783                 :           0 :                 Form_pg_index indexform;
    1784                 :           0 :                 bool            indisvalid;
    1785                 :             : 
    1786                 :           0 :                 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
    1787         [ #  # ]:           0 :                 if (!HeapTupleIsValid(locTuple))
    1788                 :             :                 {
    1789                 :           0 :                         ReleaseSysCache(tuple);
    1790                 :           0 :                         return;
    1791                 :             :                 }
    1792                 :             : 
    1793                 :           0 :                 indexform = (Form_pg_index) GETSTRUCT(locTuple);
    1794                 :           0 :                 indisvalid = indexform->indisvalid;
    1795                 :           0 :                 ReleaseSysCache(locTuple);
    1796                 :             : 
    1797                 :             :                 /* Mark object as being an invalid index of system catalogs */
    1798         [ #  # ]:           0 :                 if (!indisvalid)
    1799                 :           0 :                         invalid_system_index = true;
    1800         [ #  # ]:           0 :         }
    1801                 :             : 
    1802                 :             :         /* In the case of an invalid index, it is fine to bypass this check */
    1803   [ +  +  +  -  :        2244 :         if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
                   +  - ]
    1804   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    1805                 :             :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1806                 :             :                                  errmsg("permission denied: \"%s\" is a system catalog",
    1807                 :             :                                                 rel->relname)));
    1808                 :             : 
    1809                 :        2244 :         ReleaseSysCache(tuple);
    1810                 :             : 
    1811                 :             :         /*
    1812                 :             :          * In DROP INDEX, attempt to acquire lock on the parent table before
    1813                 :             :          * locking the index.  index_drop() will need this anyway, and since
    1814                 :             :          * regular queries lock tables before their indexes, we risk deadlock if
    1815                 :             :          * we do it the other way around.  No error if we don't find a pg_index
    1816                 :             :          * entry, though --- the relation may have been dropped.  Note that this
    1817                 :             :          * code will execute for either plain or partitioned indexes.
    1818                 :             :          */
    1819   [ +  +  +  + ]:        2244 :         if (expected_relkind == RELKIND_INDEX &&
    1820                 :         117 :                 relOid != oldRelOid)
    1821                 :             :         {
    1822                 :         112 :                 state->heapOid = IndexGetRelation(relOid, true);
    1823         [ -  + ]:         112 :                 if (OidIsValid(state->heapOid))
    1824                 :         112 :                         LockRelationOid(state->heapOid, heap_lockmode);
    1825                 :         112 :         }
    1826                 :             : 
    1827                 :             :         /*
    1828                 :             :          * Similarly, if the relation is a partition, we must acquire lock on its
    1829                 :             :          * parent before locking the partition.  That's because queries lock the
    1830                 :             :          * parent before its partitions, so we risk deadlock if we do it the other
    1831                 :             :          * way around.
    1832                 :             :          */
    1833   [ +  +  +  + ]:        2244 :         if (is_partition && relOid != oldRelOid)
    1834                 :             :         {
    1835                 :          79 :                 state->partParentOid = get_partition_parent(relOid, true);
    1836         [ -  + ]:          79 :                 if (OidIsValid(state->partParentOid))
    1837                 :          79 :                         LockRelationOid(state->partParentOid, AccessExclusiveLock);
    1838                 :          79 :         }
    1839         [ -  + ]:        2302 : }
    1840                 :             : 
    1841                 :             : /*
    1842                 :             :  * ExecuteTruncate
    1843                 :             :  *              Executes a TRUNCATE command.
    1844                 :             :  *
    1845                 :             :  * This is a multi-relation truncate.  We first open and grab exclusive
    1846                 :             :  * lock on all relations involved, checking permissions and otherwise
    1847                 :             :  * verifying that the relation is OK for truncation.  Note that if relations
    1848                 :             :  * are foreign tables, at this stage, we have not yet checked that their
    1849                 :             :  * foreign data in external data sources are OK for truncation.  These are
    1850                 :             :  * checked when foreign data are actually truncated later.  In CASCADE mode,
    1851                 :             :  * relations having FK references to the targeted relations are automatically
    1852                 :             :  * added to the group; in RESTRICT mode, we check that all FK references are
    1853                 :             :  * internal to the group that's being truncated.  Finally all the relations
    1854                 :             :  * are truncated and reindexed.
    1855                 :             :  */
    1856                 :             : void
    1857                 :         208 : ExecuteTruncate(TruncateStmt *stmt)
    1858                 :             : {
    1859                 :         208 :         List       *rels = NIL;
    1860                 :         208 :         List       *relids = NIL;
    1861                 :         208 :         List       *relids_logged = NIL;
    1862                 :         208 :         ListCell   *cell;
    1863                 :             : 
    1864                 :             :         /*
    1865                 :             :          * Open, exclusive-lock, and check all the explicitly-specified relations
    1866                 :             :          */
    1867   [ +  +  +  +  :         442 :         foreach(cell, stmt->relations)
                   +  + ]
    1868                 :             :         {
    1869                 :         236 :                 RangeVar   *rv = lfirst(cell);
    1870                 :         236 :                 Relation        rel;
    1871                 :         236 :                 bool            recurse = rv->inh;
    1872                 :         236 :                 Oid                     myrelid;
    1873                 :         236 :                 LOCKMODE        lockmode = AccessExclusiveLock;
    1874                 :             : 
    1875                 :         236 :                 myrelid = RangeVarGetRelidExtended(rv, lockmode,
    1876                 :             :                                                                                    0, RangeVarCallbackForTruncate,
    1877                 :             :                                                                                    NULL);
    1878                 :             : 
    1879                 :             :                 /* don't throw error for "TRUNCATE foo, foo" */
    1880         [ -  + ]:         236 :                 if (list_member_oid(relids, myrelid))
    1881                 :           0 :                         continue;
    1882                 :             : 
    1883                 :             :                 /* open the relation, we already hold a lock on it */
    1884                 :         236 :                 rel = table_open(myrelid, NoLock);
    1885                 :             : 
    1886                 :             :                 /*
    1887                 :             :                  * RangeVarGetRelidExtended() has done most checks with its callback,
    1888                 :             :                  * but other checks with the now-opened Relation remain.
    1889                 :             :                  */
    1890                 :         236 :                 truncate_check_activity(rel);
    1891                 :             : 
    1892                 :         236 :                 rels = lappend(rels, rel);
    1893                 :         236 :                 relids = lappend_oid(relids, myrelid);
    1894                 :             : 
    1895                 :             :                 /* Log this relation only if needed for logical decoding */
    1896   [ +  -  -  +  :         236 :                 if (RelationIsLogicallyLogged(rel))
          #  #  #  #  #  
                #  #  # ]
    1897                 :           0 :                         relids_logged = lappend_oid(relids_logged, myrelid);
    1898                 :             : 
    1899         [ +  + ]:         236 :                 if (recurse)
    1900                 :             :                 {
    1901                 :         230 :                         ListCell   *child;
    1902                 :         230 :                         List       *children;
    1903                 :             : 
    1904                 :         230 :                         children = find_all_inheritors(myrelid, lockmode, NULL);
    1905                 :             : 
    1906   [ +  -  +  +  :         749 :                         foreach(child, children)
                   +  + ]
    1907                 :             :                         {
    1908                 :         519 :                                 Oid                     childrelid = lfirst_oid(child);
    1909                 :             : 
    1910         [ +  + ]:         519 :                                 if (list_member_oid(relids, childrelid))
    1911                 :         230 :                                         continue;
    1912                 :             : 
    1913                 :             :                                 /* find_all_inheritors already got lock */
    1914                 :         289 :                                 rel = table_open(childrelid, NoLock);
    1915                 :             : 
    1916                 :             :                                 /*
    1917                 :             :                                  * It is possible that the parent table has children that are
    1918                 :             :                                  * temp tables of other backends.  We cannot safely access
    1919                 :             :                                  * such tables (because of buffering issues), and the best
    1920                 :             :                                  * thing to do is to silently ignore them.  Note that this
    1921                 :             :                                  * check is the same as one of the checks done in
    1922                 :             :                                  * truncate_check_activity() called below, still it is kept
    1923                 :             :                                  * here for simplicity.
    1924                 :             :                                  */
    1925   [ -  +  #  # ]:         289 :                                 if (RELATION_IS_OTHER_TEMP(rel))
    1926                 :             :                                 {
    1927                 :           0 :                                         table_close(rel, lockmode);
    1928                 :           0 :                                         continue;
    1929                 :             :                                 }
    1930                 :             : 
    1931                 :             :                                 /*
    1932                 :             :                                  * Inherited TRUNCATE commands perform access permission
    1933                 :             :                                  * checks on the parent table only. So we skip checking the
    1934                 :             :                                  * children's permissions and don't call
    1935                 :             :                                  * truncate_check_perms() here.
    1936                 :             :                                  */
    1937                 :         289 :                                 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
    1938                 :         289 :                                 truncate_check_activity(rel);
    1939                 :             : 
    1940                 :         289 :                                 rels = lappend(rels, rel);
    1941                 :         289 :                                 relids = lappend_oid(relids, childrelid);
    1942                 :             : 
    1943                 :             :                                 /* Log this relation only if needed for logical decoding */
    1944   [ +  -  -  +  :         289 :                                 if (RelationIsLogicallyLogged(rel))
          #  #  #  #  #  
                #  #  # ]
    1945                 :           0 :                                         relids_logged = lappend_oid(relids_logged, childrelid);
    1946         [ +  + ]:         519 :                         }
    1947                 :         230 :                 }
    1948         [ +  + ]:           6 :                 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    1949   [ +  -  +  - ]:           2 :                         ereport(ERROR,
    1950                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1951                 :             :                                          errmsg("cannot truncate only a partitioned table"),
    1952                 :             :                                          errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
    1953         [ -  + ]:         234 :         }
    1954                 :             : 
    1955                 :         392 :         ExecuteTruncateGuts(rels, relids, relids_logged,
    1956                 :         196 :                                                 stmt->behavior, stmt->restart_seqs, false);
    1957                 :             : 
    1958                 :             :         /* And close the rels */
    1959   [ +  -  +  +  :         683 :         foreach(cell, rels)
                   +  + ]
    1960                 :             :         {
    1961                 :         487 :                 Relation        rel = (Relation) lfirst(cell);
    1962                 :             : 
    1963                 :         487 :                 table_close(rel, NoLock);
    1964                 :         487 :         }
    1965                 :         196 : }
    1966                 :             : 
    1967                 :             : /*
    1968                 :             :  * ExecuteTruncateGuts
    1969                 :             :  *
    1970                 :             :  * Internal implementation of TRUNCATE.  This is called by the actual TRUNCATE
    1971                 :             :  * command (see above) as well as replication subscribers that execute a
    1972                 :             :  * replicated TRUNCATE action.
    1973                 :             :  *
    1974                 :             :  * explicit_rels is the list of Relations to truncate that the command
    1975                 :             :  * specified.  relids is the list of Oids corresponding to explicit_rels.
    1976                 :             :  * relids_logged is the list of Oids (a subset of relids) that require
    1977                 :             :  * WAL-logging.  This is all a bit redundant, but the existing callers have
    1978                 :             :  * this information handy in this form.
    1979                 :             :  */
    1980                 :             : void
    1981                 :         184 : ExecuteTruncateGuts(List *explicit_rels,
    1982                 :             :                                         List *relids,
    1983                 :             :                                         List *relids_logged,
    1984                 :             :                                         DropBehavior behavior, bool restart_seqs,
    1985                 :             :                                         bool run_as_table_owner)
    1986                 :             : {
    1987                 :         184 :         List       *rels;
    1988                 :         184 :         List       *seq_relids = NIL;
    1989                 :         184 :         HTAB       *ft_htab = NULL;
    1990                 :         184 :         EState     *estate;
    1991                 :         184 :         ResultRelInfo *resultRelInfos;
    1992                 :         184 :         ResultRelInfo *resultRelInfo;
    1993                 :         184 :         SubTransactionId mySubid;
    1994                 :         184 :         ListCell   *cell;
    1995                 :         184 :         Oid                *logrelids;
    1996                 :             : 
    1997                 :             :         /*
    1998                 :             :          * Check the explicitly-specified relations.
    1999                 :             :          *
    2000                 :             :          * In CASCADE mode, suck in all referencing relations as well.  This
    2001                 :             :          * requires multiple iterations to find indirectly-dependent relations. At
    2002                 :             :          * each phase, we need to exclusive-lock new rels before looking for their
    2003                 :             :          * dependencies, else we might miss something.  Also, we check each rel as
    2004                 :             :          * soon as we open it, to avoid a faux pas such as holding lock for a long
    2005                 :             :          * time on a rel we have no permissions for.
    2006                 :             :          */
    2007                 :         184 :         rels = list_copy(explicit_rels);
    2008         [ +  + ]:         184 :         if (behavior == DROP_CASCADE)
    2009                 :             :         {
    2010                 :          11 :                 for (;;)
    2011                 :             :                 {
    2012                 :          11 :                         List       *newrelids;
    2013                 :             : 
    2014                 :          11 :                         newrelids = heap_truncate_find_FKs(relids);
    2015         [ +  + ]:          11 :                         if (newrelids == NIL)
    2016                 :           5 :                                 break;                  /* nothing else to add */
    2017                 :             : 
    2018   [ +  -  +  +  :          21 :                         foreach(cell, newrelids)
                   +  + ]
    2019                 :             :                         {
    2020                 :          15 :                                 Oid                     relid = lfirst_oid(cell);
    2021                 :          15 :                                 Relation        rel;
    2022                 :             : 
    2023                 :          15 :                                 rel = table_open(relid, AccessExclusiveLock);
    2024   [ -  +  +  - ]:          15 :                                 ereport(NOTICE,
    2025                 :             :                                                 (errmsg("truncate cascades to table \"%s\"",
    2026                 :             :                                                                 RelationGetRelationName(rel))));
    2027                 :          15 :                                 truncate_check_rel(relid, rel->rd_rel);
    2028                 :          15 :                                 truncate_check_perms(relid, rel->rd_rel);
    2029                 :          15 :                                 truncate_check_activity(rel);
    2030                 :          15 :                                 rels = lappend(rels, rel);
    2031                 :          15 :                                 relids = lappend_oid(relids, relid);
    2032                 :             : 
    2033                 :             :                                 /* Log this relation only if needed for logical decoding */
    2034   [ +  -  -  +  :          15 :                                 if (RelationIsLogicallyLogged(rel))
          #  #  #  #  #  
                #  #  # ]
    2035                 :           0 :                                         relids_logged = lappend_oid(relids_logged, relid);
    2036                 :          15 :                         }
    2037         [ +  + ]:          11 :                 }
    2038                 :           5 :         }
    2039                 :             : 
    2040                 :             :         /*
    2041                 :             :          * Check foreign key references.  In CASCADE mode, this should be
    2042                 :             :          * unnecessary since we just pulled in all the references; but as a
    2043                 :             :          * cross-check, do it anyway if in an Assert-enabled build.
    2044                 :             :          */
    2045                 :             : #ifdef USE_ASSERT_CHECKING
    2046                 :         184 :         heap_truncate_check_FKs(rels, false);
    2047                 :             : #else
    2048                 :             :         if (behavior == DROP_RESTRICT)
    2049                 :             :                 heap_truncate_check_FKs(rels, false);
    2050                 :             : #endif
    2051                 :             : 
    2052                 :             :         /*
    2053                 :             :          * If we are asked to restart sequences, find all the sequences, lock them
    2054                 :             :          * (we need AccessExclusiveLock for ResetSequence), and check permissions.
    2055                 :             :          * We want to do this early since it's pointless to do all the truncation
    2056                 :             :          * work only to fail on sequence permissions.
    2057                 :             :          */
    2058         [ +  + ]:         184 :         if (restart_seqs)
    2059                 :             :         {
    2060   [ +  -  +  +  :           6 :                 foreach(cell, rels)
                   +  + ]
    2061                 :             :                 {
    2062                 :           3 :                         Relation        rel = (Relation) lfirst(cell);
    2063                 :           3 :                         List       *seqlist = getOwnedSequences(RelationGetRelid(rel));
    2064                 :           3 :                         ListCell   *seqcell;
    2065                 :             : 
    2066   [ +  -  +  +  :           8 :                         foreach(seqcell, seqlist)
                   +  + ]
    2067                 :             :                         {
    2068                 :           5 :                                 Oid                     seq_relid = lfirst_oid(seqcell);
    2069                 :           5 :                                 Relation        seq_rel;
    2070                 :             : 
    2071                 :           5 :                                 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
    2072                 :             : 
    2073                 :             :                                 /* This check must match AlterSequence! */
    2074         [ +  - ]:           5 :                                 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
    2075                 :           0 :                                         aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
    2076                 :           0 :                                                                    RelationGetRelationName(seq_rel));
    2077                 :             : 
    2078                 :           5 :                                 seq_relids = lappend_oid(seq_relids, seq_relid);
    2079                 :             : 
    2080                 :           5 :                                 relation_close(seq_rel, NoLock);
    2081                 :           5 :                         }
    2082                 :           3 :                 }
    2083                 :           3 :         }
    2084                 :             : 
    2085                 :             :         /* Prepare to catch AFTER triggers. */
    2086                 :         184 :         AfterTriggerBeginQuery();
    2087                 :             : 
    2088                 :             :         /*
    2089                 :             :          * To fire triggers, we'll need an EState as well as a ResultRelInfo for
    2090                 :             :          * each relation.  We don't need to call ExecOpenIndices, though.
    2091                 :             :          *
    2092                 :             :          * We put the ResultRelInfos in the es_opened_result_relations list, even
    2093                 :             :          * though we don't have a range table and don't populate the
    2094                 :             :          * es_result_relations array.  That's a bit bogus, but it's enough to make
    2095                 :             :          * ExecGetTriggerResultRel() find them.
    2096                 :             :          */
    2097                 :         184 :         estate = CreateExecutorState();
    2098                 :         184 :         resultRelInfos = (ResultRelInfo *)
    2099                 :         184 :                 palloc(list_length(rels) * sizeof(ResultRelInfo));
    2100                 :         184 :         resultRelInfo = resultRelInfos;
    2101   [ +  -  +  +  :         698 :         foreach(cell, rels)
                   +  + ]
    2102                 :             :         {
    2103                 :         514 :                 Relation        rel = (Relation) lfirst(cell);
    2104                 :             : 
    2105                 :        1028 :                 InitResultRelInfo(resultRelInfo,
    2106                 :         514 :                                                   rel,
    2107                 :             :                                                   0,    /* dummy rangetable index */
    2108                 :             :                                                   NULL,
    2109                 :             :                                                   0);
    2110                 :         514 :                 estate->es_opened_result_relations =
    2111                 :         514 :                         lappend(estate->es_opened_result_relations, resultRelInfo);
    2112                 :         514 :                 resultRelInfo++;
    2113                 :         514 :         }
    2114                 :             : 
    2115                 :             :         /*
    2116                 :             :          * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
    2117                 :             :          * truncating (this is because one of them might throw an error). Also, if
    2118                 :             :          * we were to allow them to prevent statement execution, that would need
    2119                 :             :          * to be handled here.
    2120                 :             :          */
    2121                 :         184 :         resultRelInfo = resultRelInfos;
    2122   [ +  -  +  +  :         698 :         foreach(cell, rels)
                   +  + ]
    2123                 :             :         {
    2124                 :         514 :                 UserContext ucxt;
    2125                 :             : 
    2126         [ -  + ]:         514 :                 if (run_as_table_owner)
    2127                 :           0 :                         SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2128                 :             :                                                                   &ucxt);
    2129                 :         514 :                 ExecBSTruncateTriggers(estate, resultRelInfo);
    2130         [ -  + ]:         514 :                 if (run_as_table_owner)
    2131                 :           0 :                         RestoreUserContext(&ucxt);
    2132                 :         514 :                 resultRelInfo++;
    2133                 :         514 :         }
    2134                 :             : 
    2135                 :             :         /*
    2136                 :             :          * OK, truncate each table.
    2137                 :             :          */
    2138                 :         184 :         mySubid = GetCurrentSubTransactionId();
    2139                 :             : 
    2140   [ +  -  +  +  :         698 :         foreach(cell, rels)
                   +  + ]
    2141                 :             :         {
    2142                 :         514 :                 Relation        rel = (Relation) lfirst(cell);
    2143                 :             : 
    2144                 :             :                 /* Skip partitioned tables as there is nothing to do */
    2145         [ +  + ]:         514 :                 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    2146                 :         112 :                         continue;
    2147                 :             : 
    2148                 :             :                 /*
    2149                 :             :                  * Build the lists of foreign tables belonging to each foreign server
    2150                 :             :                  * and pass each list to the foreign data wrapper's callback function,
    2151                 :             :                  * so that each server can truncate its all foreign tables in bulk.
    2152                 :             :                  * Each list is saved as a single entry in a hash table that uses the
    2153                 :             :                  * server OID as lookup key.
    2154                 :             :                  */
    2155         [ -  + ]:         402 :                 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    2156                 :             :                 {
    2157                 :           0 :                         Oid                     serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
    2158                 :           0 :                         bool            found;
    2159                 :           0 :                         ForeignTruncateInfo *ft_info;
    2160                 :             : 
    2161                 :             :                         /* First time through, initialize hashtable for foreign tables */
    2162         [ #  # ]:           0 :                         if (!ft_htab)
    2163                 :             :                         {
    2164                 :           0 :                                 HASHCTL         hctl;
    2165                 :             : 
    2166                 :           0 :                                 memset(&hctl, 0, sizeof(HASHCTL));
    2167                 :           0 :                                 hctl.keysize = sizeof(Oid);
    2168                 :           0 :                                 hctl.entrysize = sizeof(ForeignTruncateInfo);
    2169                 :           0 :                                 hctl.hcxt = CurrentMemoryContext;
    2170                 :             : 
    2171                 :           0 :                                 ft_htab = hash_create("TRUNCATE for Foreign Tables",
    2172                 :             :                                                                           32,   /* start small and extend */
    2173                 :             :                                                                           &hctl,
    2174                 :             :                                                                           HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
    2175                 :           0 :                         }
    2176                 :             : 
    2177                 :             :                         /* Find or create cached entry for the foreign table */
    2178                 :           0 :                         ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
    2179         [ #  # ]:           0 :                         if (!found)
    2180                 :           0 :                                 ft_info->rels = NIL;
    2181                 :             : 
    2182                 :             :                         /*
    2183                 :             :                          * Save the foreign table in the entry of the server that the
    2184                 :             :                          * foreign table belongs to.
    2185                 :             :                          */
    2186                 :           0 :                         ft_info->rels = lappend(ft_info->rels, rel);
    2187                 :             :                         continue;
    2188                 :           0 :                 }
    2189                 :             : 
    2190                 :             :                 /*
    2191                 :             :                  * Normally, we need a transaction-safe truncation here.  However, if
    2192                 :             :                  * the table was either created in the current (sub)transaction or has
    2193                 :             :                  * a new relfilenumber in the current (sub)transaction, then we can
    2194                 :             :                  * just truncate it in-place, because a rollback would cause the whole
    2195                 :             :                  * table or the current physical file to be thrown away anyway.
    2196                 :             :                  */
    2197   [ +  +  -  + ]:         402 :                 if (rel->rd_createSubid == mySubid ||
    2198                 :         401 :                         rel->rd_newRelfilelocatorSubid == mySubid)
    2199                 :             :                 {
    2200                 :             :                         /* Immediate, non-rollbackable truncation is OK */
    2201                 :           1 :                         heap_truncate_one_rel(rel);
    2202                 :           1 :                 }
    2203                 :             :                 else
    2204                 :             :                 {
    2205                 :         401 :                         Oid                     heap_relid;
    2206                 :         401 :                         Oid                     toast_relid;
    2207                 :         401 :                         ReindexParams reindex_params = {0};
    2208                 :             : 
    2209                 :             :                         /*
    2210                 :             :                          * This effectively deletes all rows in the table, and may be done
    2211                 :             :                          * in a serializable transaction.  In that case we must record a
    2212                 :             :                          * rw-conflict in to this transaction from each transaction
    2213                 :             :                          * holding a predicate lock on the table.
    2214                 :             :                          */
    2215                 :         401 :                         CheckTableForSerializableConflictIn(rel);
    2216                 :             : 
    2217                 :             :                         /*
    2218                 :             :                          * Need the full transaction-safe pushups.
    2219                 :             :                          *
    2220                 :             :                          * Create a new empty storage file for the relation, and assign it
    2221                 :             :                          * as the relfilenumber value. The old storage file is scheduled
    2222                 :             :                          * for deletion at commit.
    2223                 :             :                          */
    2224                 :         401 :                         RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
    2225                 :             : 
    2226                 :         401 :                         heap_relid = RelationGetRelid(rel);
    2227                 :             : 
    2228                 :             :                         /*
    2229                 :             :                          * The same for the toast table, if any.
    2230                 :             :                          */
    2231                 :         401 :                         toast_relid = rel->rd_rel->reltoastrelid;
    2232         [ +  + ]:         401 :                         if (OidIsValid(toast_relid))
    2233                 :             :                         {
    2234                 :         271 :                                 Relation        toastrel = relation_open(toast_relid,
    2235                 :             :                                                                                                          AccessExclusiveLock);
    2236                 :             : 
    2237                 :         542 :                                 RelationSetNewRelfilenumber(toastrel,
    2238                 :         271 :                                                                                         toastrel->rd_rel->relpersistence);
    2239                 :         271 :                                 table_close(toastrel, NoLock);
    2240                 :         271 :                         }
    2241                 :             : 
    2242                 :             :                         /*
    2243                 :             :                          * Reconstruct the indexes to match, and we're done.
    2244                 :             :                          */
    2245                 :         401 :                         reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
    2246                 :             :                                                          &reindex_params);
    2247                 :         401 :                 }
    2248                 :             : 
    2249                 :         402 :                 pgstat_count_truncate(rel);
    2250         [ +  + ]:         514 :         }
    2251                 :             : 
    2252                 :             :         /* Now go through the hash table, and truncate foreign tables */
    2253         [ +  - ]:         184 :         if (ft_htab)
    2254                 :             :         {
    2255                 :           0 :                 ForeignTruncateInfo *ft_info;
    2256                 :           0 :                 HASH_SEQ_STATUS seq;
    2257                 :             : 
    2258                 :           0 :                 hash_seq_init(&seq, ft_htab);
    2259                 :             : 
    2260         [ #  # ]:           0 :                 PG_TRY();
    2261                 :             :                 {
    2262         [ #  # ]:           0 :                         while ((ft_info = hash_seq_search(&seq)) != NULL)
    2263                 :             :                         {
    2264                 :           0 :                                 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
    2265                 :             : 
    2266                 :             :                                 /* truncate_check_rel() has checked that already */
    2267         [ #  # ]:           0 :                                 Assert(routine->ExecForeignTruncate != NULL);
    2268                 :             : 
    2269                 :           0 :                                 routine->ExecForeignTruncate(ft_info->rels,
    2270                 :           0 :                                                                                          behavior,
    2271                 :           0 :                                                                                          restart_seqs);
    2272                 :           0 :                         }
    2273                 :             :                 }
    2274                 :           0 :                 PG_FINALLY();
    2275                 :             :                 {
    2276                 :           0 :                         hash_destroy(ft_htab);
    2277                 :             :                 }
    2278         [ #  # ]:           0 :                 PG_END_TRY();
    2279                 :           0 :         }
    2280                 :             : 
    2281                 :             :         /*
    2282                 :             :          * Restart owned sequences if we were asked to.
    2283                 :             :          */
    2284   [ +  +  +  +  :         189 :         foreach(cell, seq_relids)
                   +  + ]
    2285                 :             :         {
    2286                 :           5 :                 Oid                     seq_relid = lfirst_oid(cell);
    2287                 :             : 
    2288                 :           5 :                 ResetSequence(seq_relid);
    2289                 :           5 :         }
    2290                 :             : 
    2291                 :             :         /*
    2292                 :             :          * Write a WAL record to allow this set of actions to be logically
    2293                 :             :          * decoded.
    2294                 :             :          *
    2295                 :             :          * Assemble an array of relids so we can write a single WAL record for the
    2296                 :             :          * whole action.
    2297                 :             :          */
    2298         [ +  - ]:         184 :         if (relids_logged != NIL)
    2299                 :             :         {
    2300                 :           0 :                 xl_heap_truncate xlrec;
    2301                 :           0 :                 int                     i = 0;
    2302                 :             : 
    2303                 :             :                 /* should only get here if effective_wal_level is 'logical' */
    2304   [ #  #  #  # ]:           0 :                 Assert(XLogLogicalInfoActive());
    2305                 :             : 
    2306                 :           0 :                 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
    2307   [ #  #  #  #  :           0 :                 foreach(cell, relids_logged)
                   #  # ]
    2308                 :           0 :                         logrelids[i++] = lfirst_oid(cell);
    2309                 :             : 
    2310                 :           0 :                 xlrec.dbId = MyDatabaseId;
    2311                 :           0 :                 xlrec.nrelids = list_length(relids_logged);
    2312                 :           0 :                 xlrec.flags = 0;
    2313         [ #  # ]:           0 :                 if (behavior == DROP_CASCADE)
    2314                 :           0 :                         xlrec.flags |= XLH_TRUNCATE_CASCADE;
    2315         [ #  # ]:           0 :                 if (restart_seqs)
    2316                 :           0 :                         xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
    2317                 :             : 
    2318                 :           0 :                 XLogBeginInsert();
    2319                 :           0 :                 XLogRegisterData(&xlrec, SizeOfHeapTruncate);
    2320                 :           0 :                 XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
    2321                 :             : 
    2322                 :           0 :                 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
    2323                 :             : 
    2324                 :           0 :                 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
    2325                 :           0 :         }
    2326                 :             : 
    2327                 :             :         /*
    2328                 :             :          * Process all AFTER STATEMENT TRUNCATE triggers.
    2329                 :             :          */
    2330                 :         184 :         resultRelInfo = resultRelInfos;
    2331   [ +  -  +  +  :         698 :         foreach(cell, rels)
                   +  + ]
    2332                 :             :         {
    2333                 :         514 :                 UserContext ucxt;
    2334                 :             : 
    2335         [ +  - ]:         514 :                 if (run_as_table_owner)
    2336                 :           0 :                         SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2337                 :             :                                                                   &ucxt);
    2338                 :         514 :                 ExecASTruncateTriggers(estate, resultRelInfo);
    2339         [ +  - ]:         514 :                 if (run_as_table_owner)
    2340                 :           0 :                         RestoreUserContext(&ucxt);
    2341                 :         514 :                 resultRelInfo++;
    2342                 :         514 :         }
    2343                 :             : 
    2344                 :             :         /* Handle queued AFTER triggers */
    2345                 :         184 :         AfterTriggerEndQuery(estate);
    2346                 :             : 
    2347                 :             :         /* We can clean up the EState now */
    2348                 :         184 :         FreeExecutorState(estate);
    2349                 :             : 
    2350                 :             :         /*
    2351                 :             :          * Close any rels opened by CASCADE (can't do this while EState still
    2352                 :             :          * holds refs)
    2353                 :             :          */
    2354                 :         184 :         rels = list_difference_ptr(rels, explicit_rels);
    2355   [ +  +  +  +  :         199 :         foreach(cell, rels)
                   +  + ]
    2356                 :             :         {
    2357                 :          15 :                 Relation        rel = (Relation) lfirst(cell);
    2358                 :             : 
    2359                 :          15 :                 table_close(rel, NoLock);
    2360                 :          15 :         }
    2361                 :         184 : }
    2362                 :             : 
    2363                 :             : /*
    2364                 :             :  * Check that a given relation is safe to truncate.  Subroutine for
    2365                 :             :  * ExecuteTruncate() and RangeVarCallbackForTruncate().
    2366                 :             :  */
    2367                 :             : static void
    2368                 :         580 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
    2369                 :             : {
    2370                 :         580 :         char       *relname = NameStr(reltuple->relname);
    2371                 :             : 
    2372                 :             :         /*
    2373                 :             :          * Only allow truncate on regular tables, foreign tables using foreign
    2374                 :             :          * data wrappers supporting TRUNCATE and partitioned tables (although, the
    2375                 :             :          * latter are only being included here for the following checks; no
    2376                 :             :          * physical truncation will occur in their case.).
    2377                 :             :          */
    2378         [ -  + ]:         580 :         if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
    2379                 :             :         {
    2380                 :           0 :                 Oid                     serverid = GetForeignServerIdByRelId(relid);
    2381                 :           0 :                 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
    2382                 :             : 
    2383         [ #  # ]:           0 :                 if (!fdwroutine->ExecForeignTruncate)
    2384   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    2385                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2386                 :             :                                          errmsg("cannot truncate foreign table \"%s\"",
    2387                 :             :                                                         relname)));
    2388                 :           0 :         }
    2389   [ +  +  +  - ]:         580 :         else if (reltuple->relkind != RELKIND_RELATION &&
    2390                 :         118 :                          reltuple->relkind != RELKIND_PARTITIONED_TABLE)
    2391   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    2392                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2393                 :             :                                  errmsg("\"%s\" is not a table", relname)));
    2394                 :             : 
    2395                 :             :         /*
    2396                 :             :          * Most system catalogs can't be truncated at all, or at least not unless
    2397                 :             :          * allow_system_table_mods=on. As an exception, however, we allow
    2398                 :             :          * pg_largeobject and pg_largeobject_metadata to be truncated as part of
    2399                 :             :          * pg_upgrade, because we need to change its relfilenode to match the old
    2400                 :             :          * cluster, and allowing a TRUNCATE command to be executed is the easiest
    2401                 :             :          * way of doing that.
    2402                 :             :          */
    2403         [ +  - ]:         580 :         if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
    2404   [ +  -  #  # ]:         580 :                 && (!IsBinaryUpgrade ||
    2405         [ #  # ]:           0 :                         (relid != LargeObjectRelationId &&
    2406                 :           0 :                          relid != LargeObjectMetadataRelationId)))
    2407   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    2408                 :             :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2409                 :             :                                  errmsg("permission denied: \"%s\" is a system catalog",
    2410                 :             :                                                 relname)));
    2411                 :             : 
    2412         [ +  - ]:         580 :         InvokeObjectTruncateHook(relid);
    2413                 :         580 : }
    2414                 :             : 
    2415                 :             : /*
    2416                 :             :  * Check that current user has the permission to truncate given relation.
    2417                 :             :  */
    2418                 :             : static void
    2419                 :         291 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
    2420                 :             : {
    2421                 :         291 :         char       *relname = NameStr(reltuple->relname);
    2422                 :         291 :         AclResult       aclresult;
    2423                 :             : 
    2424                 :             :         /* Permissions checks */
    2425                 :         291 :         aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
    2426         [ +  + ]:         291 :         if (aclresult != ACLCHECK_OK)
    2427                 :           8 :                 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
    2428                 :           4 :                                            relname);
    2429                 :         291 : }
    2430                 :             : 
    2431                 :             : /*
    2432                 :             :  * Set of extra sanity checks to check if a given relation is safe to
    2433                 :             :  * truncate.  This is split with truncate_check_rel() as
    2434                 :             :  * RangeVarCallbackForTruncate() cannot open a Relation yet.
    2435                 :             :  */
    2436                 :             : static void
    2437                 :         541 : truncate_check_activity(Relation rel)
    2438                 :             : {
    2439                 :             :         /*
    2440                 :             :          * Don't allow truncate on temp tables of other backends ... their local
    2441                 :             :          * buffer manager is not going to cope.
    2442                 :             :          */
    2443   [ +  +  +  - ]:         541 :         if (RELATION_IS_OTHER_TEMP(rel))
    2444   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    2445                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2446                 :             :                                  errmsg("cannot truncate temporary tables of other sessions")));
    2447                 :             : 
    2448                 :             :         /*
    2449                 :             :          * Also check for active uses of the relation in the current transaction,
    2450                 :             :          * including open scans and pending AFTER trigger events.
    2451                 :             :          */
    2452                 :         541 :         CheckTableNotInUse(rel, "TRUNCATE");
    2453                 :         541 : }
    2454                 :             : 
    2455                 :             : /*
    2456                 :             :  * storage_name
    2457                 :             :  *        returns the name corresponding to a typstorage/attstorage enum value
    2458                 :             :  */
    2459                 :             : static const char *
    2460                 :           4 : storage_name(char c)
    2461                 :             : {
    2462   [ +  -  -  -  :           4 :         switch (c)
                      + ]
    2463                 :             :         {
    2464                 :             :                 case TYPSTORAGE_PLAIN:
    2465                 :           0 :                         return "PLAIN";
    2466                 :             :                 case TYPSTORAGE_EXTERNAL:
    2467                 :           0 :                         return "EXTERNAL";
    2468                 :             :                 case TYPSTORAGE_EXTENDED:
    2469                 :           2 :                         return "EXTENDED";
    2470                 :             :                 case TYPSTORAGE_MAIN:
    2471                 :           2 :                         return "MAIN";
    2472                 :             :                 default:
    2473                 :           0 :                         return "???";
    2474                 :             :         }
    2475                 :           4 : }
    2476                 :             : 
    2477                 :             : /*----------
    2478                 :             :  * MergeAttributes
    2479                 :             :  *              Returns new schema given initial schema and superclasses.
    2480                 :             :  *
    2481                 :             :  * Input arguments:
    2482                 :             :  * 'columns' is the column/attribute definition for the table. (It's a list
    2483                 :             :  *              of ColumnDef's.) It is destructively changed.
    2484                 :             :  * 'supers' is a list of OIDs of parent relations, already locked by caller.
    2485                 :             :  * 'relpersistence' is the persistence type of the table.
    2486                 :             :  * 'is_partition' tells if the table is a partition.
    2487                 :             :  *
    2488                 :             :  * Output arguments:
    2489                 :             :  * 'supconstr' receives a list of CookedConstraint representing
    2490                 :             :  *              CHECK constraints belonging to parent relations, updated as
    2491                 :             :  *              necessary to be valid for the child.
    2492                 :             :  * 'supnotnulls' receives a list of CookedConstraint representing
    2493                 :             :  *              not-null constraints based on those from parent relations.
    2494                 :             :  *
    2495                 :             :  * Return value:
    2496                 :             :  * Completed schema list.
    2497                 :             :  *
    2498                 :             :  * Notes:
    2499                 :             :  *        The order in which the attributes are inherited is very important.
    2500                 :             :  *        Intuitively, the inherited attributes should come first. If a table
    2501                 :             :  *        inherits from multiple parents, the order of those attributes are
    2502                 :             :  *        according to the order of the parents specified in CREATE TABLE.
    2503                 :             :  *
    2504                 :             :  *        Here's an example:
    2505                 :             :  *
    2506                 :             :  *              create table person (name text, age int4, location point);
    2507                 :             :  *              create table emp (salary int4, manager text) inherits(person);
    2508                 :             :  *              create table student (gpa float8) inherits (person);
    2509                 :             :  *              create table stud_emp (percent int4) inherits (emp, student);
    2510                 :             :  *
    2511                 :             :  *        The order of the attributes of stud_emp is:
    2512                 :             :  *
    2513                 :             :  *                                                      person {1:name, 2:age, 3:location}
    2514                 :             :  *                                                      /        \
    2515                 :             :  *                         {6:gpa}      student   emp {4:salary, 5:manager}
    2516                 :             :  *                                                      \        /
    2517                 :             :  *                                                 stud_emp {7:percent}
    2518                 :             :  *
    2519                 :             :  *         If the same attribute name appears multiple times, then it appears
    2520                 :             :  *         in the result table in the proper location for its first appearance.
    2521                 :             :  *
    2522                 :             :  *         Constraints (including not-null constraints) for the child table
    2523                 :             :  *         are the union of all relevant constraints, from both the child schema
    2524                 :             :  *         and parent tables.  In addition, in legacy inheritance, each column that
    2525                 :             :  *         appears in a primary key in any of the parents also gets a NOT NULL
    2526                 :             :  *         constraint (partitioning doesn't need this, because the PK itself gets
    2527                 :             :  *         inherited.)
    2528                 :             :  *
    2529                 :             :  *         The default value for a child column is defined as:
    2530                 :             :  *              (1) If the child schema specifies a default, that value is used.
    2531                 :             :  *              (2) If neither the child nor any parent specifies a default, then
    2532                 :             :  *                      the column will not have a default.
    2533                 :             :  *              (3) If conflicting defaults are inherited from different parents
    2534                 :             :  *                      (and not overridden by the child), an error is raised.
    2535                 :             :  *              (4) Otherwise the inherited default is used.
    2536                 :             :  *
    2537                 :             :  *              Note that the default-value infrastructure is used for generated
    2538                 :             :  *              columns' expressions too, so most of the preceding paragraph applies
    2539                 :             :  *              to generation expressions too.  We insist that a child column be
    2540                 :             :  *              generated if and only if its parent(s) are, but it need not have
    2541                 :             :  *              the same generation expression.
    2542                 :             :  *----------
    2543                 :             :  */
    2544                 :             : static List *
    2545                 :        5484 : MergeAttributes(List *columns, const List *supers, char relpersistence,
    2546                 :             :                                 bool is_partition, List **supconstr, List **supnotnulls)
    2547                 :             : {
    2548                 :        5484 :         List       *inh_columns = NIL;
    2549                 :        5484 :         List       *constraints = NIL;
    2550                 :        5484 :         List       *nnconstraints = NIL;
    2551                 :        5484 :         bool            have_bogus_defaults = false;
    2552                 :        5484 :         int                     child_attno;
    2553                 :             :         static Node bogus_marker = {0}; /* marks conflicting defaults */
    2554                 :        5484 :         List       *saved_columns = NIL;
    2555                 :        5484 :         ListCell   *lc;
    2556                 :             : 
    2557                 :             :         /*
    2558                 :             :          * Check for and reject tables with too many columns. We perform this
    2559                 :             :          * check relatively early for two reasons: (a) we don't run the risk of
    2560                 :             :          * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
    2561                 :             :          * okay if we're processing <= 1600 columns, but could take minutes to
    2562                 :             :          * execute if the user attempts to create a table with hundreds of
    2563                 :             :          * thousands of columns.
    2564                 :             :          *
    2565                 :             :          * Note that we also need to check that we do not exceed this figure after
    2566                 :             :          * including columns from inherited relations.
    2567                 :             :          */
    2568         [ +  - ]:        5484 :         if (list_length(columns) > MaxHeapAttributeNumber)
    2569   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    2570                 :             :                                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    2571                 :             :                                  errmsg("tables can have at most %d columns",
    2572                 :             :                                                 MaxHeapAttributeNumber)));
    2573                 :             : 
    2574                 :             :         /*
    2575                 :             :          * Check for duplicate names in the explicit list of attributes.
    2576                 :             :          *
    2577                 :             :          * Although we might consider merging such entries in the same way that we
    2578                 :             :          * handle name conflicts for inherited attributes, it seems to make more
    2579                 :             :          * sense to assume such conflicts are errors.
    2580                 :             :          *
    2581                 :             :          * We don't use foreach() here because we have two nested loops over the
    2582                 :             :          * columns list, with possible element deletions in the inner one.  If we
    2583                 :             :          * used foreach_delete_current() it could only fix up the state of one of
    2584                 :             :          * the loops, so it seems cleaner to use looping over list indexes for
    2585                 :             :          * both loops.  Note that any deletion will happen beyond where the outer
    2586                 :             :          * loop is, so its index never needs adjustment.
    2587                 :             :          */
    2588         [ +  + ]:       16877 :         for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
    2589                 :             :         {
    2590                 :       11397 :                 ColumnDef  *coldef = list_nth_node(ColumnDef, columns, coldefpos);
    2591                 :             : 
    2592   [ +  +  +  + ]:       11397 :                 if (!is_partition && coldef->typeName == NULL)
    2593                 :             :                 {
    2594                 :             :                         /*
    2595                 :             :                          * Typed table column option that does not belong to a column from
    2596                 :             :                          * the type.  This works because the columns from the type come
    2597                 :             :                          * first in the list.  (We omit this check for partition column
    2598                 :             :                          * lists; those are processed separately below.)
    2599                 :             :                          */
    2600   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    2601                 :             :                                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    2602                 :             :                                          errmsg("column \"%s\" does not exist",
    2603                 :             :                                                         coldef->colname)));
    2604                 :           0 :                 }
    2605                 :             : 
    2606                 :             :                 /* restpos scans all entries beyond coldef; incr is in loop body */
    2607         [ +  + ]:      646934 :                 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
    2608                 :             :                 {
    2609                 :      635541 :                         ColumnDef  *restdef = list_nth_node(ColumnDef, columns, restpos);
    2610                 :             : 
    2611         [ +  + ]:      635541 :                         if (strcmp(coldef->colname, restdef->colname) == 0)
    2612                 :             :                         {
    2613         [ +  + ]:           8 :                                 if (coldef->is_from_type)
    2614                 :             :                                 {
    2615                 :             :                                         /*
    2616                 :             :                                          * merge the column options into the column from the type
    2617                 :             :                                          */
    2618                 :           5 :                                         coldef->is_not_null = restdef->is_not_null;
    2619                 :           5 :                                         coldef->raw_default = restdef->raw_default;
    2620                 :           5 :                                         coldef->cooked_default = restdef->cooked_default;
    2621                 :           5 :                                         coldef->constraints = restdef->constraints;
    2622                 :           5 :                                         coldef->is_from_type = false;
    2623                 :           5 :                                         columns = list_delete_nth_cell(columns, restpos);
    2624                 :           5 :                                 }
    2625                 :             :                                 else
    2626   [ +  -  +  - ]:           3 :                                         ereport(ERROR,
    2627                 :             :                                                         (errcode(ERRCODE_DUPLICATE_COLUMN),
    2628                 :             :                                                          errmsg("column \"%s\" specified more than once",
    2629                 :             :                                                                         coldef->colname)));
    2630                 :           5 :                         }
    2631                 :             :                         else
    2632                 :      635533 :                                 restpos++;
    2633                 :      635538 :                 }
    2634                 :       11393 :         }
    2635                 :             : 
    2636                 :             :         /*
    2637                 :             :          * In case of a partition, there are no new column definitions, only dummy
    2638                 :             :          * ColumnDefs created for column constraints.  Set them aside for now and
    2639                 :             :          * process them at the end.
    2640                 :             :          */
    2641         [ +  + ]:        5480 :         if (is_partition)
    2642                 :             :         {
    2643                 :        1221 :                 saved_columns = columns;
    2644                 :        1221 :                 columns = NIL;
    2645                 :        1221 :         }
    2646                 :             : 
    2647                 :             :         /*
    2648                 :             :          * Scan the parents left-to-right, and merge their attributes to form a
    2649                 :             :          * list of inherited columns (inh_columns).
    2650                 :             :          */
    2651                 :        5480 :         child_attno = 0;
    2652   [ +  +  +  +  :        7035 :         foreach(lc, supers)
                   +  + ]
    2653                 :             :         {
    2654                 :        1562 :                 Oid                     parent = lfirst_oid(lc);
    2655                 :        1562 :                 Relation        relation;
    2656                 :        1562 :                 TupleDesc       tupleDesc;
    2657                 :        1562 :                 TupleConstr *constr;
    2658                 :        1562 :                 AttrMap    *newattmap;
    2659                 :        1562 :                 List       *inherited_defaults;
    2660                 :        1562 :                 List       *cols_with_defaults;
    2661                 :        1562 :                 List       *nnconstrs;
    2662                 :        1562 :                 ListCell   *lc1;
    2663                 :        1562 :                 ListCell   *lc2;
    2664                 :        1562 :                 Bitmapset  *nncols = NULL;
    2665                 :             : 
    2666                 :             :                 /* caller already got lock */
    2667                 :        1562 :                 relation = table_open(parent, NoLock);
    2668                 :             : 
    2669                 :             :                 /*
    2670                 :             :                  * Check for active uses of the parent partitioned table in the
    2671                 :             :                  * current transaction, such as being used in some manner by an
    2672                 :             :                  * enclosing command.
    2673                 :             :                  */
    2674         [ +  + ]:        1562 :                 if (is_partition)
    2675                 :        1221 :                         CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
    2676                 :             : 
    2677                 :             :                 /*
    2678                 :             :                  * We do not allow partitioned tables and partitions to participate in
    2679                 :             :                  * regular inheritance.
    2680                 :             :                  */
    2681   [ +  +  +  + ]:        1562 :                 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
    2682   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    2683                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2684                 :             :                                          errmsg("cannot inherit from partitioned table \"%s\"",
    2685                 :             :                                                         RelationGetRelationName(relation))));
    2686   [ +  +  +  + ]:        1561 :                 if (relation->rd_rel->relispartition && !is_partition)
    2687   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    2688                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2689                 :             :                                          errmsg("cannot inherit from partition \"%s\"",
    2690                 :             :                                                         RelationGetRelationName(relation))));
    2691                 :             : 
    2692         [ +  + ]:        1560 :                 if (relation->rd_rel->relkind != RELKIND_RELATION &&
    2693   [ +  +  +  - ]:        1219 :                         relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
    2694                 :        1217 :                         relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    2695   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    2696                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2697                 :             :                                          errmsg("inherited relation \"%s\" is not a table or foreign table",
    2698                 :             :                                                         RelationGetRelationName(relation))));
    2699                 :             : 
    2700                 :             :                 /*
    2701                 :             :                  * If the parent is permanent, so must be all of its partitions.  Note
    2702                 :             :                  * that inheritance allows that case.
    2703                 :             :                  */
    2704         [ +  + ]:        1560 :                 if (is_partition &&
    2705   [ +  +  +  + ]:        1220 :                         relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
    2706                 :        1178 :                         relpersistence == RELPERSISTENCE_TEMP)
    2707   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    2708                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2709                 :             :                                          errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
    2710                 :             :                                                         RelationGetRelationName(relation))));
    2711                 :             : 
    2712                 :             :                 /* Permanent rels cannot inherit from temporary ones */
    2713   [ +  +  +  + ]:        1559 :                 if (relpersistence != RELPERSISTENCE_TEMP &&
    2714                 :        1502 :                         relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
    2715   [ +  -  +  - ]:           4 :                         ereport(ERROR,
    2716                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2717                 :             :                                          errmsg(!is_partition
    2718                 :             :                                                         ? "cannot inherit from temporary relation \"%s\""
    2719                 :             :                                                         : "cannot create a permanent relation as partition of temporary relation \"%s\"",
    2720                 :             :                                                         RelationGetRelationName(relation))));
    2721                 :             : 
    2722                 :             :                 /* If existing rel is temp, it must belong to this session */
    2723   [ +  +  +  - ]:        1555 :                 if (RELATION_IS_OTHER_TEMP(relation))
    2724   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    2725                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2726                 :             :                                          errmsg(!is_partition
    2727                 :             :                                                         ? "cannot inherit from temporary relation of another session"
    2728                 :             :                                                         : "cannot create as partition of temporary relation of another session")));
    2729                 :             : 
    2730                 :             :                 /*
    2731                 :             :                  * We should have an UNDER permission flag for this, but for now,
    2732                 :             :                  * demand that creator of a child table own the parent.
    2733                 :             :                  */
    2734         [ +  - ]:        1555 :                 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
    2735                 :           0 :                         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
    2736                 :           0 :                                                    RelationGetRelationName(relation));
    2737                 :             : 
    2738                 :        1555 :                 tupleDesc = RelationGetDescr(relation);
    2739                 :        1555 :                 constr = tupleDesc->constr;
    2740                 :             : 
    2741                 :             :                 /*
    2742                 :             :                  * newattmap->attnums[] will contain the child-table attribute numbers
    2743                 :             :                  * for the attributes of this parent table.  (They are not the same
    2744                 :             :                  * for parents after the first one, nor if we have dropped columns.)
    2745                 :             :                  */
    2746                 :        1555 :                 newattmap = make_attrmap(tupleDesc->natts);
    2747                 :             : 
    2748                 :             :                 /* We can't process inherited defaults until newattmap is complete. */
    2749                 :        1555 :                 inherited_defaults = cols_with_defaults = NIL;
    2750                 :             : 
    2751                 :             :                 /*
    2752                 :             :                  * Request attnotnull on columns that have a not-null constraint
    2753                 :             :                  * that's not marked NO INHERIT (even if not valid).
    2754                 :             :                  */
    2755                 :        1555 :                 nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
    2756                 :             :                                                                                                   true, false);
    2757   [ +  +  +  +  :        3499 :                 foreach_ptr(CookedConstraint, cc, nnconstrs)
             +  +  +  + ]
    2758                 :        1944 :                         nncols = bms_add_member(nncols, cc->attnum);
    2759                 :             : 
    2760         [ +  + ]:        4705 :                 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
    2761                 :        3150 :                          parent_attno++)
    2762                 :             :                 {
    2763                 :        6300 :                         Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
    2764                 :        3150 :                                                                                                                 parent_attno - 1);
    2765                 :        3150 :                         char       *attributeName = NameStr(attribute->attname);
    2766                 :        3150 :                         int                     exist_attno;
    2767                 :        3150 :                         ColumnDef  *newdef;
    2768                 :        3150 :                         ColumnDef  *mergeddef;
    2769                 :             : 
    2770                 :             :                         /*
    2771                 :             :                          * Ignore dropped columns in the parent.
    2772                 :             :                          */
    2773         [ +  + ]:        3150 :                         if (attribute->attisdropped)
    2774                 :          33 :                                 continue;               /* leave newattmap->attnums entry as zero */
    2775                 :             : 
    2776                 :             :                         /*
    2777                 :             :                          * Create new column definition
    2778                 :             :                          */
    2779                 :        6234 :                         newdef = makeColumnDef(attributeName, attribute->atttypid,
    2780                 :        3117 :                                                                    attribute->atttypmod, attribute->attcollation);
    2781                 :        3117 :                         newdef->storage = attribute->attstorage;
    2782                 :        3117 :                         newdef->generated = attribute->attgenerated;
    2783         [ +  + ]:        3117 :                         if (CompressionMethodIsValid(attribute->attcompression))
    2784                 :           6 :                                 newdef->compression =
    2785                 :           6 :                                         pstrdup(GetCompressionMethodName(attribute->attcompression));
    2786                 :             : 
    2787                 :             :                         /*
    2788                 :             :                          * Regular inheritance children are independent enough not to
    2789                 :             :                          * inherit identity columns.  But partitions are integral part of
    2790                 :             :                          * a partitioned table and inherit identity column.
    2791                 :             :                          */
    2792         [ +  + ]:        3117 :                         if (is_partition)
    2793                 :        2545 :                                 newdef->identity = attribute->attidentity;
    2794                 :             : 
    2795                 :             :                         /*
    2796                 :             :                          * Does it match some previously considered column from another
    2797                 :             :                          * parent?
    2798                 :             :                          */
    2799                 :        3117 :                         exist_attno = findAttrByName(attributeName, inh_columns);
    2800         [ +  + ]:        3117 :                         if (exist_attno > 0)
    2801                 :             :                         {
    2802                 :             :                                 /*
    2803                 :             :                                  * Yes, try to merge the two column definitions.
    2804                 :             :                                  */
    2805                 :          54 :                                 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
    2806                 :             : 
    2807                 :          54 :                                 newattmap->attnums[parent_attno - 1] = exist_attno;
    2808                 :             : 
    2809                 :             :                                 /*
    2810                 :             :                                  * Partitions have only one parent, so conflict should never
    2811                 :             :                                  * occur.
    2812                 :             :                                  */
    2813         [ +  - ]:          54 :                                 Assert(!is_partition);
    2814                 :          54 :                         }
    2815                 :             :                         else
    2816                 :             :                         {
    2817                 :             :                                 /*
    2818                 :             :                                  * No, create a new inherited column
    2819                 :             :                                  */
    2820                 :        3063 :                                 newdef->inhcount = 1;
    2821                 :        3063 :                                 newdef->is_local = false;
    2822                 :        3063 :                                 inh_columns = lappend(inh_columns, newdef);
    2823                 :             : 
    2824                 :        3063 :                                 newattmap->attnums[parent_attno - 1] = ++child_attno;
    2825                 :        3063 :                                 mergeddef = newdef;
    2826                 :             :                         }
    2827                 :             : 
    2828                 :             :                         /*
    2829                 :             :                          * mark attnotnull if parent has it
    2830                 :             :                          */
    2831         [ +  + ]:        3117 :                         if (bms_is_member(parent_attno, nncols))
    2832                 :         383 :                                 mergeddef->is_not_null = true;
    2833                 :             : 
    2834                 :             :                         /*
    2835                 :             :                          * Locate default/generation expression if any
    2836                 :             :                          */
    2837         [ +  + ]:        3117 :                         if (attribute->atthasdef)
    2838                 :             :                         {
    2839                 :         101 :                                 Node       *this_default;
    2840                 :             : 
    2841                 :         101 :                                 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
    2842         [ +  - ]:         101 :                                 if (this_default == NULL)
    2843   [ #  #  #  # ]:           0 :                                         elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
    2844                 :             :                                                  parent_attno, RelationGetRelationName(relation));
    2845                 :             : 
    2846                 :             :                                 /*
    2847                 :             :                                  * If it's a GENERATED default, it might contain Vars that
    2848                 :             :                                  * need to be mapped to the inherited column(s)' new numbers.
    2849                 :             :                                  * We can't do that till newattmap is ready, so just remember
    2850                 :             :                                  * all the inherited default expressions for the moment.
    2851                 :             :                                  */
    2852                 :         101 :                                 inherited_defaults = lappend(inherited_defaults, this_default);
    2853                 :         101 :                                 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
    2854                 :         101 :                         }
    2855         [ +  + ]:        3150 :                 }
    2856                 :             : 
    2857                 :             :                 /*
    2858                 :             :                  * Now process any inherited default expressions, adjusting attnos
    2859                 :             :                  * using the completed newattmap map.
    2860                 :             :                  */
    2861   [ +  +  +  +  :        1656 :                 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
          +  +  +  +  +  
                +  +  + ]
    2862                 :             :                 {
    2863                 :         101 :                         Node       *this_default = (Node *) lfirst(lc1);
    2864                 :         101 :                         ColumnDef  *def = (ColumnDef *) lfirst(lc2);
    2865                 :         101 :                         bool            found_whole_row;
    2866                 :             : 
    2867                 :             :                         /* Adjust Vars to match new table's column numbering */
    2868                 :         202 :                         this_default = map_variable_attnos(this_default,
    2869                 :             :                                                                                            1, 0,
    2870                 :         101 :                                                                                            newattmap,
    2871                 :             :                                                                                            InvalidOid, &found_whole_row);
    2872                 :             : 
    2873                 :             :                         /*
    2874                 :             :                          * For the moment we have to reject whole-row variables.  We could
    2875                 :             :                          * convert them, if we knew the new table's rowtype OID, but that
    2876                 :             :                          * hasn't been assigned yet.  (A variable could only appear in a
    2877                 :             :                          * generation expression, so the error message is correct.)
    2878                 :             :                          */
    2879         [ +  - ]:         101 :                         if (found_whole_row)
    2880   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    2881                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2882                 :             :                                                  errmsg("cannot convert whole-row table reference"),
    2883                 :             :                                                  errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
    2884                 :             :                                                                    def->colname,
    2885                 :             :                                                                    RelationGetRelationName(relation))));
    2886                 :             : 
    2887                 :             :                         /*
    2888                 :             :                          * If we already had a default from some prior parent, check to
    2889                 :             :                          * see if they are the same.  If so, no problem; if not, mark the
    2890                 :             :                          * column as having a bogus default.  Below, we will complain if
    2891                 :             :                          * the bogus default isn't overridden by the child columns.
    2892                 :             :                          */
    2893         [ +  - ]:         101 :                         Assert(def->raw_default == NULL);
    2894         [ +  + ]:         101 :                         if (def->cooked_default == NULL)
    2895                 :          94 :                                 def->cooked_default = this_default;
    2896         [ +  + ]:           7 :                         else if (!equal(def->cooked_default, this_default))
    2897                 :             :                         {
    2898                 :           6 :                                 def->cooked_default = &bogus_marker;
    2899                 :           6 :                                 have_bogus_defaults = true;
    2900                 :           6 :                         }
    2901                 :         101 :                 }
    2902                 :             : 
    2903                 :             :                 /*
    2904                 :             :                  * Now copy the CHECK constraints of this parent, adjusting attnos
    2905                 :             :                  * using the completed newattmap map.  Identically named constraints
    2906                 :             :                  * are merged if possible, else we throw error.
    2907                 :             :                  */
    2908   [ +  +  +  + ]:        1555 :                 if (constr && constr->num_check > 0)
    2909                 :             :                 {
    2910                 :          57 :                         ConstrCheck *check = constr->check;
    2911                 :             : 
    2912         [ +  + ]:         181 :                         for (int i = 0; i < constr->num_check; i++)
    2913                 :             :                         {
    2914                 :         124 :                                 char       *name = check[i].ccname;
    2915                 :         124 :                                 Node       *expr;
    2916                 :         124 :                                 bool            found_whole_row;
    2917                 :             : 
    2918                 :             :                                 /* ignore if the constraint is non-inheritable */
    2919         [ +  + ]:         124 :                                 if (check[i].ccnoinherit)
    2920                 :           8 :                                         continue;
    2921                 :             : 
    2922                 :             :                                 /* Adjust Vars to match new table's column numbering */
    2923                 :         232 :                                 expr = map_variable_attnos(stringToNode(check[i].ccbin),
    2924                 :             :                                                                                    1, 0,
    2925                 :         116 :                                                                                    newattmap,
    2926                 :             :                                                                                    InvalidOid, &found_whole_row);
    2927                 :             : 
    2928                 :             :                                 /*
    2929                 :             :                                  * For the moment we have to reject whole-row variables. We
    2930                 :             :                                  * could convert them, if we knew the new table's rowtype OID,
    2931                 :             :                                  * but that hasn't been assigned yet.
    2932                 :             :                                  */
    2933         [ +  - ]:         116 :                                 if (found_whole_row)
    2934   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
    2935                 :             :                                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2936                 :             :                                                          errmsg("cannot convert whole-row table reference"),
    2937                 :             :                                                          errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
    2938                 :             :                                                                            name,
    2939                 :             :                                                                            RelationGetRelationName(relation))));
    2940                 :             : 
    2941                 :         232 :                                 constraints = MergeCheckConstraint(constraints, name, expr,
    2942                 :         116 :                                                                                                    check[i].ccenforced);
    2943         [ +  + ]:         124 :                         }
    2944                 :          57 :                 }
    2945                 :             : 
    2946                 :             :                 /*
    2947                 :             :                  * Also copy the not-null constraints from this parent.  The
    2948                 :             :                  * attnotnull markings were already installed above.
    2949                 :             :                  */
    2950   [ +  +  +  +  :        3493 :                 foreach_ptr(CookedConstraint, nn, nnconstrs)
             +  +  +  + ]
    2951                 :             :                 {
    2952         [ +  - ]:         383 :                         Assert(nn->contype == CONSTR_NOTNULL);
    2953                 :             : 
    2954                 :         383 :                         nn->attnum = newattmap->attnums[nn->attnum - 1];
    2955                 :             : 
    2956                 :         383 :                         nnconstraints = lappend(nnconstraints, nn);
    2957                 :        1938 :                 }
    2958                 :             : 
    2959                 :        1555 :                 free_attrmap(newattmap);
    2960                 :             : 
    2961                 :             :                 /*
    2962                 :             :                  * Close the parent rel, but keep our lock on it until xact commit.
    2963                 :             :                  * That will prevent someone else from deleting or ALTERing the parent
    2964                 :             :                  * before the child is committed.
    2965                 :             :                  */
    2966                 :        1555 :                 table_close(relation, NoLock);
    2967                 :        1555 :         }
    2968                 :             : 
    2969                 :             :         /*
    2970                 :             :          * If we had no inherited attributes, the result columns are just the
    2971                 :             :          * explicitly declared columns.  Otherwise, we need to merge the declared
    2972                 :             :          * columns into the inherited column list.  Although, we never have any
    2973                 :             :          * explicitly declared columns if the table is a partition.
    2974                 :             :          */
    2975         [ +  + ]:        5473 :         if (inh_columns != NIL)
    2976                 :             :         {
    2977                 :        1470 :                 int                     newcol_attno = 0;
    2978                 :             : 
    2979   [ +  +  +  +  :        1640 :                 foreach(lc, columns)
                   +  + ]
    2980                 :             :                 {
    2981                 :         170 :                         ColumnDef  *newdef = lfirst_node(ColumnDef, lc);
    2982                 :         170 :                         char       *attributeName = newdef->colname;
    2983                 :         170 :                         int                     exist_attno;
    2984                 :             : 
    2985                 :             :                         /*
    2986                 :             :                          * Partitions have only one parent and have no column definitions
    2987                 :             :                          * of their own, so conflict should never occur.
    2988                 :             :                          */
    2989         [ +  - ]:         170 :                         Assert(!is_partition);
    2990                 :             : 
    2991                 :         170 :                         newcol_attno++;
    2992                 :             : 
    2993                 :             :                         /*
    2994                 :             :                          * Does it match some inherited column?
    2995                 :             :                          */
    2996                 :         170 :                         exist_attno = findAttrByName(attributeName, inh_columns);
    2997         [ +  + ]:         170 :                         if (exist_attno > 0)
    2998                 :             :                         {
    2999                 :             :                                 /*
    3000                 :             :                                  * Yes, try to merge the two column definitions.
    3001                 :             :                                  */
    3002                 :          61 :                                 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
    3003                 :          61 :                         }
    3004                 :             :                         else
    3005                 :             :                         {
    3006                 :             :                                 /*
    3007                 :             :                                  * No, attach new column unchanged to result columns.
    3008                 :             :                                  */
    3009                 :         109 :                                 inh_columns = lappend(inh_columns, newdef);
    3010                 :             :                         }
    3011                 :         170 :                 }
    3012                 :             : 
    3013                 :        1470 :                 columns = inh_columns;
    3014                 :             : 
    3015                 :             :                 /*
    3016                 :             :                  * Check that we haven't exceeded the legal # of columns after merging
    3017                 :             :                  * in inherited columns.
    3018                 :             :                  */
    3019         [ +  - ]:        1470 :                 if (list_length(columns) > MaxHeapAttributeNumber)
    3020   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    3021                 :             :                                         (errcode(ERRCODE_TOO_MANY_COLUMNS),
    3022                 :             :                                          errmsg("tables can have at most %d columns",
    3023                 :             :                                                         MaxHeapAttributeNumber)));
    3024                 :        1470 :         }
    3025                 :             : 
    3026                 :             :         /*
    3027                 :             :          * Now that we have the column definition list for a partition, we can
    3028                 :             :          * check whether the columns referenced in the column constraint specs
    3029                 :             :          * actually exist.  Also, merge column defaults.
    3030                 :             :          */
    3031         [ +  + ]:        5473 :         if (is_partition)
    3032                 :             :         {
    3033   [ +  +  +  +  :        1233 :                 foreach(lc, saved_columns)
                   +  + ]
    3034                 :             :                 {
    3035                 :          23 :                         ColumnDef  *restdef = lfirst(lc);
    3036                 :          23 :                         bool            found = false;
    3037                 :          23 :                         ListCell   *l;
    3038                 :             : 
    3039   [ +  -  +  +  :          64 :                         foreach(l, columns)
                   +  + ]
    3040                 :             :                         {
    3041                 :          47 :                                 ColumnDef  *coldef = lfirst(l);
    3042                 :             : 
    3043         [ +  + ]:          47 :                                 if (strcmp(coldef->colname, restdef->colname) == 0)
    3044                 :             :                                 {
    3045                 :          23 :                                         found = true;
    3046                 :             : 
    3047                 :             :                                         /*
    3048                 :             :                                          * Check for conflicts related to generated columns.
    3049                 :             :                                          *
    3050                 :             :                                          * Same rules as above: generated-ness has to match the
    3051                 :             :                                          * parent, but the contents of the generation expression
    3052                 :             :                                          * can be different.
    3053                 :             :                                          */
    3054         [ +  + ]:          23 :                                         if (coldef->generated)
    3055                 :             :                                         {
    3056   [ +  -  +  + ]:           6 :                                                 if (restdef->raw_default && !restdef->generated)
    3057   [ +  -  +  - ]:           2 :                                                         ereport(ERROR,
    3058                 :             :                                                                         (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3059                 :             :                                                                          errmsg("column \"%s\" inherits from generated column but specifies default",
    3060                 :             :                                                                                         restdef->colname)));
    3061         [ +  - ]:           4 :                                                 if (restdef->identity)
    3062   [ #  #  #  # ]:           0 :                                                         ereport(ERROR,
    3063                 :             :                                                                         (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3064                 :             :                                                                          errmsg("column \"%s\" inherits from generated column but specifies identity",
    3065                 :             :                                                                                         restdef->colname)));
    3066                 :           4 :                                         }
    3067                 :             :                                         else
    3068                 :             :                                         {
    3069         [ +  + ]:          17 :                                                 if (restdef->generated)
    3070   [ +  -  +  - ]:           2 :                                                         ereport(ERROR,
    3071                 :             :                                                                         (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3072                 :             :                                                                          errmsg("child column \"%s\" specifies generation expression",
    3073                 :             :                                                                                         restdef->colname),
    3074                 :             :                                                                          errhint("A child table column cannot be generated unless its parent column is.")));
    3075                 :             :                                         }
    3076                 :             : 
    3077   [ +  +  +  -  :          19 :                                         if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
                   +  + ]
    3078   [ +  -  +  - ]:           2 :                                                 ereport(ERROR,
    3079                 :             :                                                                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3080                 :             :                                                                  errmsg("column \"%s\" inherits from generated column of different kind",
    3081                 :             :                                                                                 restdef->colname),
    3082                 :             :                                                                  errdetail("Parent column is %s, child column is %s.",
    3083                 :             :                                                                                    coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
    3084                 :             :                                                                                    restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
    3085                 :             : 
    3086                 :             :                                         /*
    3087                 :             :                                          * Override the parent's default value for this column
    3088                 :             :                                          * (coldef->cooked_default) with the partition's local
    3089                 :             :                                          * definition (restdef->raw_default), if there's one. It
    3090                 :             :                                          * should be physically impossible to get a cooked default
    3091                 :             :                                          * in the local definition or a raw default in the
    3092                 :             :                                          * inherited definition, but make sure they're nulls, for
    3093                 :             :                                          * future-proofing.
    3094                 :             :                                          */
    3095         [ -  + ]:          17 :                                         Assert(restdef->cooked_default == NULL);
    3096         [ -  + ]:          17 :                                         Assert(coldef->raw_default == NULL);
    3097         [ +  + ]:          17 :                                         if (restdef->raw_default)
    3098                 :             :                                         {
    3099                 :           5 :                                                 coldef->raw_default = restdef->raw_default;
    3100                 :           5 :                                                 coldef->cooked_default = NULL;
    3101                 :           5 :                                         }
    3102                 :          17 :                                 }
    3103                 :          41 :                         }
    3104                 :             : 
    3105                 :             :                         /* complain for constraints on columns not in parent */
    3106         [ +  - ]:          17 :                         if (!found)
    3107   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    3108                 :             :                                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    3109                 :             :                                                  errmsg("column \"%s\" does not exist",
    3110                 :             :                                                                 restdef->colname)));
    3111                 :          17 :                 }
    3112                 :        1210 :         }
    3113                 :             : 
    3114                 :             :         /*
    3115                 :             :          * If we found any conflicting parent default values, check to make sure
    3116                 :             :          * they were overridden by the child.
    3117                 :             :          */
    3118         [ +  + ]:        5467 :         if (have_bogus_defaults)
    3119                 :             :         {
    3120   [ +  -  +  +  :          15 :                 foreach(lc, columns)
                   +  + ]
    3121                 :             :                 {
    3122                 :          12 :                         ColumnDef  *def = lfirst(lc);
    3123                 :             : 
    3124         [ +  + ]:          12 :                         if (def->cooked_default == &bogus_marker)
    3125                 :             :                         {
    3126         [ +  + ]:           3 :                                 if (def->generated)
    3127   [ +  -  +  - ]:           2 :                                         ereport(ERROR,
    3128                 :             :                                                         (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3129                 :             :                                                          errmsg("column \"%s\" inherits conflicting generation expressions",
    3130                 :             :                                                                         def->colname),
    3131                 :             :                                                          errhint("To resolve the conflict, specify a generation expression explicitly.")));
    3132                 :             :                                 else
    3133   [ +  -  +  - ]:           1 :                                         ereport(ERROR,
    3134                 :             :                                                         (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3135                 :             :                                                          errmsg("column \"%s\" inherits conflicting default values",
    3136                 :             :                                                                         def->colname),
    3137                 :             :                                                          errhint("To resolve the conflict, specify a default explicitly.")));
    3138                 :           0 :                         }
    3139                 :           9 :                 }
    3140                 :           3 :         }
    3141                 :             : 
    3142                 :        5464 :         *supconstr = constraints;
    3143                 :        5464 :         *supnotnulls = nnconstraints;
    3144                 :             : 
    3145                 :       10928 :         return columns;
    3146                 :        5464 : }
    3147                 :             : 
    3148                 :             : 
    3149                 :             : /*
    3150                 :             :  * MergeCheckConstraint
    3151                 :             :  *              Try to merge an inherited CHECK constraint with previous ones
    3152                 :             :  *
    3153                 :             :  * If we inherit identically-named constraints from multiple parents, we must
    3154                 :             :  * merge them, or throw an error if they don't have identical definitions.
    3155                 :             :  *
    3156                 :             :  * constraints is a list of CookedConstraint structs for previous constraints.
    3157                 :             :  *
    3158                 :             :  * If the new constraint matches an existing one, then the existing
    3159                 :             :  * constraint's inheritance count is updated.  If there is a conflict (same
    3160                 :             :  * name but different expression), throw an error.  If the constraint neither
    3161                 :             :  * matches nor conflicts with an existing one, a new constraint is appended to
    3162                 :             :  * the list.
    3163                 :             :  */
    3164                 :             : static List *
    3165                 :         116 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
    3166                 :             : {
    3167                 :         116 :         ListCell   *lc;
    3168                 :         116 :         CookedConstraint *newcon;
    3169                 :             : 
    3170   [ +  +  +  +  :         395 :         foreach(lc, constraints)
             +  +  +  + ]
    3171                 :             :         {
    3172                 :         279 :                 CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
    3173                 :             : 
    3174         [ -  + ]:         279 :                 Assert(ccon->contype == CONSTR_CHECK);
    3175                 :             : 
    3176                 :             :                 /* Non-matching names never conflict */
    3177         [ +  + ]:         279 :                 if (strcmp(ccon->name, name) != 0)
    3178                 :         254 :                         continue;
    3179                 :             : 
    3180         [ +  - ]:          25 :                 if (equal(expr, ccon->expr))
    3181                 :             :                 {
    3182                 :             :                         /* OK to merge constraint with existing */
    3183   [ +  -  +  - ]:          50 :                         if (pg_add_s16_overflow(ccon->inhcount, 1,
    3184                 :          25 :                                                                         &ccon->inhcount))
    3185   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    3186                 :             :                                                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3187                 :             :                                                 errmsg("too many inheritance parents"));
    3188                 :             : 
    3189                 :             :                         /*
    3190                 :             :                          * When enforceability differs, the merged constraint should be
    3191                 :             :                          * marked as ENFORCED because one of the parents is ENFORCED.
    3192                 :             :                          */
    3193   [ +  +  +  + ]:          25 :                         if (!ccon->is_enforced && is_enforced)
    3194                 :             :                         {
    3195                 :           8 :                                 ccon->is_enforced = true;
    3196                 :           8 :                                 ccon->skip_validation = false;
    3197                 :           8 :                         }
    3198                 :             : 
    3199                 :          25 :                         return constraints;
    3200                 :             :                 }
    3201                 :             : 
    3202   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    3203                 :             :                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    3204                 :             :                                  errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
    3205                 :             :                                                 name)));
    3206      [ +  +  - ]:         279 :         }
    3207                 :             : 
    3208                 :             :         /*
    3209                 :             :          * Constraint couldn't be merged with an existing one and also didn't
    3210                 :             :          * conflict with an existing one, so add it as a new one to the list.
    3211                 :             :          */
    3212                 :          91 :         newcon = palloc0_object(CookedConstraint);
    3213                 :          91 :         newcon->contype = CONSTR_CHECK;
    3214                 :          91 :         newcon->name = pstrdup(name);
    3215                 :          91 :         newcon->expr = expr;
    3216                 :          91 :         newcon->inhcount = 1;
    3217                 :          91 :         newcon->is_enforced = is_enforced;
    3218                 :          91 :         newcon->skip_validation = !is_enforced;
    3219                 :          91 :         return lappend(constraints, newcon);
    3220                 :         116 : }
    3221                 :             : 
    3222                 :             : /*
    3223                 :             :  * MergeChildAttribute
    3224                 :             :  *              Merge given child attribute definition into given inherited attribute.
    3225                 :             :  *
    3226                 :             :  * Input arguments:
    3227                 :             :  * 'inh_columns' is the list of inherited ColumnDefs.
    3228                 :             :  * 'exist_attno' is the number of the inherited attribute in inh_columns
    3229                 :             :  * 'newcol_attno' is the attribute number in child table's schema definition
    3230                 :             :  * 'newdef' is the column/attribute definition from the child table.
    3231                 :             :  *
    3232                 :             :  * The ColumnDef in 'inh_columns' list is modified.  The child attribute's
    3233                 :             :  * ColumnDef remains unchanged.
    3234                 :             :  *
    3235                 :             :  * Notes:
    3236                 :             :  * - The attribute is merged according to the rules laid out in the prologue
    3237                 :             :  *   of MergeAttributes().
    3238                 :             :  * - If matching inherited attribute exists but the child attribute can not be
    3239                 :             :  *   merged into it, the function throws respective errors.
    3240                 :             :  * - A partition can not have its own column definitions. Hence this function
    3241                 :             :  *   is applicable only to a regular inheritance child.
    3242                 :             :  */
    3243                 :             : static void
    3244                 :          61 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
    3245                 :             : {
    3246                 :          61 :         char       *attributeName = newdef->colname;
    3247                 :          61 :         ColumnDef  *inhdef;
    3248                 :          61 :         Oid                     inhtypeid,
    3249                 :             :                                 newtypeid;
    3250                 :          61 :         int32           inhtypmod,
    3251                 :             :                                 newtypmod;
    3252                 :          61 :         Oid                     inhcollid,
    3253                 :             :                                 newcollid;
    3254                 :             : 
    3255         [ +  + ]:          61 :         if (exist_attno == newcol_attno)
    3256   [ -  +  +  - ]:          56 :                 ereport(NOTICE,
    3257                 :             :                                 (errmsg("merging column \"%s\" with inherited definition",
    3258                 :             :                                                 attributeName)));
    3259                 :             :         else
    3260   [ -  +  +  - ]:           5 :                 ereport(NOTICE,
    3261                 :             :                                 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
    3262                 :             :                                  errdetail("User-specified column moved to the position of the inherited column.")));
    3263                 :             : 
    3264                 :          61 :         inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
    3265                 :             : 
    3266                 :             :         /*
    3267                 :             :          * Must have the same type and typmod
    3268                 :             :          */
    3269                 :          61 :         typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
    3270                 :          61 :         typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
    3271         [ +  + ]:          61 :         if (inhtypeid != newtypeid || inhtypmod != newtypmod)
    3272   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    3273                 :             :                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3274                 :             :                                  errmsg("column \"%s\" has a type conflict",
    3275                 :             :                                                 attributeName),
    3276                 :             :                                  errdetail("%s versus %s",
    3277                 :             :                                                    format_type_with_typemod(inhtypeid, inhtypmod),
    3278                 :             :                                                    format_type_with_typemod(newtypeid, newtypmod))));
    3279                 :             : 
    3280                 :             :         /*
    3281                 :             :          * Must have the same collation
    3282                 :             :          */
    3283                 :          59 :         inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
    3284                 :          59 :         newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
    3285         [ +  + ]:          59 :         if (inhcollid != newcollid)
    3286   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    3287                 :             :                                 (errcode(ERRCODE_COLLATION_MISMATCH),
    3288                 :             :                                  errmsg("column \"%s\" has a collation conflict",
    3289                 :             :                                                 attributeName),
    3290                 :             :                                  errdetail("\"%s\" versus \"%s\"",
    3291                 :             :                                                    get_collation_name(inhcollid),
    3292                 :             :                                                    get_collation_name(newcollid))));
    3293                 :             : 
    3294                 :             :         /*
    3295                 :             :          * Identity is never inherited by a regular inheritance child. Pick
    3296                 :             :          * child's identity definition if there's one.
    3297                 :             :          */
    3298                 :          58 :         inhdef->identity = newdef->identity;
    3299                 :             : 
    3300                 :             :         /*
    3301                 :             :          * Copy storage parameter
    3302                 :             :          */
    3303         [ +  - ]:          58 :         if (inhdef->storage == 0)
    3304                 :           0 :                 inhdef->storage = newdef->storage;
    3305   [ +  +  +  + ]:          58 :         else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
    3306   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    3307                 :             :                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3308                 :             :                                  errmsg("column \"%s\" has a storage parameter conflict",
    3309                 :             :                                                 attributeName),
    3310                 :             :                                  errdetail("%s versus %s",
    3311                 :             :                                                    storage_name(inhdef->storage),
    3312                 :             :                                                    storage_name(newdef->storage))));
    3313                 :             : 
    3314                 :             :         /*
    3315                 :             :          * Copy compression parameter
    3316                 :             :          */
    3317         [ +  + ]:          57 :         if (inhdef->compression == NULL)
    3318                 :          56 :                 inhdef->compression = newdef->compression;
    3319         [ -  + ]:           1 :         else if (newdef->compression != NULL)
    3320                 :             :         {
    3321         [ -  + ]:           1 :                 if (strcmp(inhdef->compression, newdef->compression) != 0)
    3322   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    3323                 :             :                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    3324                 :             :                                          errmsg("column \"%s\" has a compression method conflict",
    3325                 :             :                                                         attributeName),
    3326                 :             :                                          errdetail("%s versus %s", inhdef->compression, newdef->compression)));
    3327                 :           0 :         }
    3328                 :             : 
    3329                 :             :         /*
    3330                 :             :          * Merge of not-null constraints = OR 'em together
    3331                 :             :          */
    3332                 :          56 :         inhdef->is_not_null |= newdef->is_not_null;
    3333                 :             : 
    3334                 :             :         /*
    3335                 :             :          * Check for conflicts related to generated columns.
    3336                 :             :          *
    3337                 :             :          * If the parent column is generated, the child column will be made a
    3338                 :             :          * generated column if it isn't already.  If it is a generated column,
    3339                 :             :          * we'll take its generation expression in preference to the parent's.  We
    3340                 :             :          * must check that the child column doesn't specify a default value or
    3341                 :             :          * identity, which matches the rules for a single column in
    3342                 :             :          * parse_utilcmd.c.
    3343                 :             :          *
    3344                 :             :          * Conversely, if the parent column is not generated, the child column
    3345                 :             :          * can't be either.  (We used to allow that, but it results in being able
    3346                 :             :          * to override the generation expression via UPDATEs through the parent.)
    3347                 :             :          */
    3348         [ +  + ]:          56 :         if (inhdef->generated)
    3349                 :             :         {
    3350   [ +  +  +  + ]:          10 :                 if (newdef->raw_default && !newdef->generated)
    3351   [ +  -  +  - ]:           2 :                         ereport(ERROR,
    3352                 :             :                                         (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3353                 :             :                                          errmsg("column \"%s\" inherits from generated column but specifies default",
    3354                 :             :                                                         inhdef->colname)));
    3355         [ +  + ]:           8 :                 if (newdef->identity)
    3356   [ +  -  +  - ]:           2 :                         ereport(ERROR,
    3357                 :             :                                         (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3358                 :             :                                          errmsg("column \"%s\" inherits from generated column but specifies identity",
    3359                 :             :                                                         inhdef->colname)));
    3360                 :           6 :         }
    3361                 :             :         else
    3362                 :             :         {
    3363         [ +  + ]:          46 :                 if (newdef->generated)
    3364   [ +  -  +  - ]:           2 :                         ereport(ERROR,
    3365                 :             :                                         (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3366                 :             :                                          errmsg("child column \"%s\" specifies generation expression",
    3367                 :             :                                                         inhdef->colname),
    3368                 :             :                                          errhint("A child table column cannot be generated unless its parent column is.")));
    3369                 :             :         }
    3370                 :             : 
    3371   [ +  +  +  -  :          50 :         if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
                   +  + ]
    3372   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    3373                 :             :                                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3374                 :             :                                  errmsg("column \"%s\" inherits from generated column of different kind",
    3375                 :             :                                                 inhdef->colname),
    3376                 :             :                                  errdetail("Parent column is %s, child column is %s.",
    3377                 :             :                                                    inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
    3378                 :             :                                                    newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
    3379                 :             : 
    3380                 :             :         /*
    3381                 :             :          * If new def has a default, override previous default
    3382                 :             :          */
    3383         [ +  + ]:          48 :         if (newdef->raw_default != NULL)
    3384                 :             :         {
    3385                 :           5 :                 inhdef->raw_default = newdef->raw_default;
    3386                 :           5 :                 inhdef->cooked_default = newdef->cooked_default;
    3387                 :           5 :         }
    3388                 :             : 
    3389                 :             :         /* Mark the column as locally defined */
    3390                 :          48 :         inhdef->is_local = true;
    3391                 :          48 : }
    3392                 :             : 
    3393                 :             : /*
    3394                 :             :  * MergeInheritedAttribute
    3395                 :             :  *              Merge given parent attribute definition into specified attribute
    3396                 :             :  *              inherited from the previous parents.
    3397                 :             :  *
    3398                 :             :  * Input arguments:
    3399                 :             :  * 'inh_columns' is the list of previously inherited ColumnDefs.
    3400                 :             :  * 'exist_attno' is the number the existing matching attribute in inh_columns.
    3401                 :             :  * 'newdef' is the new parent column/attribute definition to be merged.
    3402                 :             :  *
    3403                 :             :  * The matching ColumnDef in 'inh_columns' list is modified and returned.
    3404                 :             :  *
    3405                 :             :  * Notes:
    3406                 :             :  * - The attribute is merged according to the rules laid out in the prologue
    3407                 :             :  *   of MergeAttributes().
    3408                 :             :  * - If matching inherited attribute exists but the new attribute can not be
    3409                 :             :  *   merged into it, the function throws respective errors.
    3410                 :             :  * - A partition inherits from only a single parent. Hence this function is
    3411                 :             :  *   applicable only to a regular inheritance.
    3412                 :             :  */
    3413                 :             : static ColumnDef *
    3414                 :          60 : MergeInheritedAttribute(List *inh_columns,
    3415                 :             :                                                 int exist_attno,
    3416                 :             :                                                 const ColumnDef *newdef)
    3417                 :             : {
    3418                 :          60 :         char       *attributeName = newdef->colname;
    3419                 :          60 :         ColumnDef  *prevdef;
    3420                 :          60 :         Oid                     prevtypeid,
    3421                 :             :                                 newtypeid;
    3422                 :          60 :         int32           prevtypmod,
    3423                 :             :                                 newtypmod;
    3424                 :          60 :         Oid                     prevcollid,
    3425                 :             :                                 newcollid;
    3426                 :             : 
    3427   [ -  +  +  - ]:          60 :         ereport(NOTICE,
    3428                 :             :                         (errmsg("merging multiple inherited definitions of column \"%s\"",
    3429                 :             :                                         attributeName)));
    3430                 :          60 :         prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
    3431                 :             : 
    3432                 :             :         /*
    3433                 :             :          * Must have the same type and typmod
    3434                 :             :          */
    3435                 :          60 :         typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
    3436                 :          60 :         typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
    3437         [ +  - ]:          60 :         if (prevtypeid != newtypeid || prevtypmod != newtypmod)
    3438   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    3439                 :             :                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3440                 :             :                                  errmsg("inherited column \"%s\" has a type conflict",
    3441                 :             :                                                 attributeName),
    3442                 :             :                                  errdetail("%s versus %s",
    3443                 :             :                                                    format_type_with_typemod(prevtypeid, prevtypmod),
    3444                 :             :                                                    format_type_with_typemod(newtypeid, newtypmod))));
    3445                 :             : 
    3446                 :             :         /*
    3447                 :             :          * Must have the same collation
    3448                 :             :          */
    3449                 :          60 :         prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
    3450                 :          60 :         newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
    3451         [ +  - ]:          60 :         if (prevcollid != newcollid)
    3452   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    3453                 :             :                                 (errcode(ERRCODE_COLLATION_MISMATCH),
    3454                 :             :                                  errmsg("inherited column \"%s\" has a collation conflict",
    3455                 :             :                                                 attributeName),
    3456                 :             :                                  errdetail("\"%s\" versus \"%s\"",
    3457                 :             :                                                    get_collation_name(prevcollid),
    3458                 :             :                                                    get_collation_name(newcollid))));
    3459                 :             : 
    3460                 :             :         /*
    3461                 :             :          * Copy/check storage parameter
    3462                 :             :          */
    3463         [ +  - ]:          60 :         if (prevdef->storage == 0)
    3464                 :           0 :                 prevdef->storage = newdef->storage;
    3465         [ +  + ]:          60 :         else if (prevdef->storage != newdef->storage)
    3466   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    3467                 :             :                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3468                 :             :                                  errmsg("inherited column \"%s\" has a storage parameter conflict",
    3469                 :             :                                                 attributeName),
    3470                 :             :                                  errdetail("%s versus %s",
    3471                 :             :                                                    storage_name(prevdef->storage),
    3472                 :             :                                                    storage_name(newdef->storage))));
    3473                 :             : 
    3474                 :             :         /*
    3475                 :             :          * Copy/check compression parameter
    3476                 :             :          */
    3477         [ +  + ]:          59 :         if (prevdef->compression == NULL)
    3478                 :          56 :                 prevdef->compression = newdef->compression;
    3479         [ +  + ]:           3 :         else if (newdef->compression != NULL)
    3480                 :             :         {
    3481         [ -  + ]:           1 :                 if (strcmp(prevdef->compression, newdef->compression) != 0)
    3482   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    3483                 :             :                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    3484                 :             :                                          errmsg("column \"%s\" has a compression method conflict",
    3485                 :             :                                                         attributeName),
    3486                 :             :                                          errdetail("%s versus %s",
    3487                 :             :                                                            prevdef->compression, newdef->compression)));
    3488                 :           0 :         }
    3489                 :             : 
    3490                 :             :         /*
    3491                 :             :          * Check for GENERATED conflicts
    3492                 :             :          */
    3493         [ +  + ]:          58 :         if (prevdef->generated != newdef->generated)
    3494   [ +  -  +  - ]:           4 :                 ereport(ERROR,
    3495                 :             :                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3496                 :             :                                  errmsg("inherited column \"%s\" has a generation conflict",
    3497                 :             :                                                 attributeName)));
    3498                 :             : 
    3499                 :             :         /*
    3500                 :             :          * Default and other constraints are handled by the caller.
    3501                 :             :          */
    3502                 :             : 
    3503   [ +  -  +  - ]:         108 :         if (pg_add_s16_overflow(prevdef->inhcount, 1,
    3504                 :          54 :                                                         &prevdef->inhcount))
    3505   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    3506                 :             :                                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3507                 :             :                                 errmsg("too many inheritance parents"));
    3508                 :             : 
    3509                 :         108 :         return prevdef;
    3510                 :          54 : }
    3511                 :             : 
    3512                 :             : /*
    3513                 :             :  * StoreCatalogInheritance
    3514                 :             :  *              Updates the system catalogs with proper inheritance information.
    3515                 :             :  *
    3516                 :             :  * supers is a list of the OIDs of the new relation's direct ancestors.
    3517                 :             :  */
    3518                 :             : static void
    3519                 :        5335 : StoreCatalogInheritance(Oid relationId, List *supers,
    3520                 :             :                                                 bool child_is_partition)
    3521                 :             : {
    3522                 :        5335 :         Relation        relation;
    3523                 :        5335 :         int32           seqNumber;
    3524                 :        5335 :         ListCell   *entry;
    3525                 :             : 
    3526                 :             :         /*
    3527                 :             :          * sanity checks
    3528                 :             :          */
    3529         [ +  - ]:        5335 :         Assert(OidIsValid(relationId));
    3530                 :             : 
    3531         [ +  + ]:        5335 :         if (supers == NIL)
    3532                 :        3925 :                 return;
    3533                 :             : 
    3534                 :             :         /*
    3535                 :             :          * Store INHERITS information in pg_inherits using direct ancestors only.
    3536                 :             :          * Also enter dependencies on the direct ancestors, and make sure they are
    3537                 :             :          * marked with relhassubclass = true.
    3538                 :             :          *
    3539                 :             :          * (Once upon a time, both direct and indirect ancestors were found here
    3540                 :             :          * and then entered into pg_ipl.  Since that catalog doesn't exist
    3541                 :             :          * anymore, there's no need to look for indirect ancestors.)
    3542                 :             :          */
    3543                 :        1410 :         relation = table_open(InheritsRelationId, RowExclusiveLock);
    3544                 :             : 
    3545                 :        1410 :         seqNumber = 1;
    3546   [ +  -  +  +  :        2875 :         foreach(entry, supers)
                   +  + ]
    3547                 :             :         {
    3548                 :        1465 :                 Oid                     parentOid = lfirst_oid(entry);
    3549                 :             : 
    3550                 :        2930 :                 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
    3551                 :        1465 :                                                                  child_is_partition);
    3552                 :        1465 :                 seqNumber++;
    3553                 :        1465 :         }
    3554                 :             : 
    3555                 :        1410 :         table_close(relation, RowExclusiveLock);
    3556         [ -  + ]:        5335 : }
    3557                 :             : 
    3558                 :             : /*
    3559                 :             :  * Make catalog entries showing relationId as being an inheritance child
    3560                 :             :  * of parentOid.  inhRelation is the already-opened pg_inherits catalog.
    3561                 :             :  */
    3562                 :             : static void
    3563                 :        1854 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
    3564                 :             :                                                  int32 seqNumber, Relation inhRelation,
    3565                 :             :                                                  bool child_is_partition)
    3566                 :             : {
    3567                 :        1854 :         ObjectAddress childobject,
    3568                 :             :                                 parentobject;
    3569                 :             : 
    3570                 :             :         /* store the pg_inherits row */
    3571                 :        1854 :         StoreSingleInheritance(relationId, parentOid, seqNumber);
    3572                 :             : 
    3573                 :             :         /*
    3574                 :             :          * Store a dependency too
    3575                 :             :          */
    3576                 :        1854 :         parentobject.classId = RelationRelationId;
    3577                 :        1854 :         parentobject.objectId = parentOid;
    3578                 :        1854 :         parentobject.objectSubId = 0;
    3579                 :        1854 :         childobject.classId = RelationRelationId;
    3580                 :        1854 :         childobject.objectId = relationId;
    3581                 :        1854 :         childobject.objectSubId = 0;
    3582                 :             : 
    3583                 :        1854 :         recordDependencyOn(&childobject, &parentobject,
    3584                 :        1854 :                                            child_dependency_type(child_is_partition));
    3585                 :             : 
    3586                 :             :         /*
    3587                 :             :          * Post creation hook of this inheritance. Since object_access_hook
    3588                 :             :          * doesn't take multiple object identifiers, we relay oid of parent
    3589                 :             :          * relation using auxiliary_id argument.
    3590                 :             :          */
    3591         [ +  - ]:        1854 :         InvokeObjectPostAlterHookArg(InheritsRelationId,
    3592                 :             :                                                                  relationId, 0,
    3593                 :             :                                                                  parentOid, false);
    3594                 :             : 
    3595                 :             :         /*
    3596                 :             :          * Mark the parent as having subclasses.
    3597                 :             :          */
    3598                 :        1854 :         SetRelationHasSubclass(parentOid, true);
    3599                 :        1854 : }
    3600                 :             : 
    3601                 :             : /*
    3602                 :             :  * Look for an existing column entry with the given name.
    3603                 :             :  *
    3604                 :             :  * Returns the index (starting with 1) if attribute already exists in columns,
    3605                 :             :  * 0 if it doesn't.
    3606                 :             :  */
    3607                 :             : static int
    3608                 :        3293 : findAttrByName(const char *attributeName, const List *columns)
    3609                 :             : {
    3610                 :        3293 :         ListCell   *lc;
    3611                 :        3293 :         int                     i = 1;
    3612                 :             : 
    3613   [ +  +  +  +  :        6149 :         foreach(lc, columns)
             +  +  +  + ]
    3614                 :             :         {
    3615         [ +  + ]:        2856 :                 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
    3616                 :         121 :                         return i;
    3617                 :             : 
    3618                 :        2735 :                 i++;
    3619                 :        2735 :         }
    3620                 :        3172 :         return 0;
    3621                 :        3293 : }
    3622                 :             : 
    3623                 :             : 
    3624                 :             : /*
    3625                 :             :  * SetRelationHasSubclass
    3626                 :             :  *              Set the value of the relation's relhassubclass field in pg_class.
    3627                 :             :  *
    3628                 :             :  * It's always safe to set this field to true, because all SQL commands are
    3629                 :             :  * ready to see true and then find no children.  On the other hand, commands
    3630                 :             :  * generally assume zero children if this is false.
    3631                 :             :  *
    3632                 :             :  * Caller must hold any self-exclusive lock until end of transaction.  If the
    3633                 :             :  * new value is false, caller must have acquired that lock before reading the
    3634                 :             :  * evidence that justified the false value.  That way, it properly waits if
    3635                 :             :  * another backend is simultaneously concluding no need to change the tuple
    3636                 :             :  * (new and old values are true).
    3637                 :             :  *
    3638                 :             :  * NOTE: an important side-effect of this operation is that an SI invalidation
    3639                 :             :  * message is sent out to all backends --- including me --- causing plans
    3640                 :             :  * referencing the relation to be rebuilt with the new list of children.
    3641                 :             :  * This must happen even if we find that no change is needed in the pg_class
    3642                 :             :  * row.
    3643                 :             :  */
    3644                 :             : void
    3645                 :        2387 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
    3646                 :             : {
    3647                 :        2387 :         Relation        relationRelation;
    3648                 :        2387 :         HeapTuple       tuple;
    3649                 :        2387 :         Form_pg_class classtuple;
    3650                 :             : 
    3651   [ +  +  +  - ]:        2387 :         Assert(CheckRelationOidLockedByMe(relationId,
    3652                 :             :                                                                           ShareUpdateExclusiveLock, false) ||
    3653                 :             :                    CheckRelationOidLockedByMe(relationId,
    3654                 :             :                                                                           ShareRowExclusiveLock, true));
    3655                 :             : 
    3656                 :             :         /*
    3657                 :             :          * Fetch a modifiable copy of the tuple, modify it, update pg_class.
    3658                 :             :          */
    3659                 :        2387 :         relationRelation = table_open(RelationRelationId, RowExclusiveLock);
    3660                 :        2387 :         tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
    3661         [ +  - ]:        2387 :         if (!HeapTupleIsValid(tuple))
    3662   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", relationId);
    3663                 :        2387 :         classtuple = (Form_pg_class) GETSTRUCT(tuple);
    3664                 :             : 
    3665         [ +  + ]:        2387 :         if (classtuple->relhassubclass != relhassubclass)
    3666                 :             :         {
    3667                 :        1118 :                 classtuple->relhassubclass = relhassubclass;
    3668                 :        1118 :                 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
    3669                 :        1118 :         }
    3670                 :             :         else
    3671                 :             :         {
    3672                 :             :                 /* no need to change tuple, but force relcache rebuild anyway */
    3673                 :        1269 :                 CacheInvalidateRelcacheByTuple(tuple);
    3674                 :             :         }
    3675                 :             : 
    3676                 :        2387 :         heap_freetuple(tuple);
    3677                 :        2387 :         table_close(relationRelation, RowExclusiveLock);
    3678                 :        2387 : }
    3679                 :             : 
    3680                 :             : /*
    3681                 :             :  * CheckRelationTableSpaceMove
    3682                 :             :  *              Check if relation can be moved to new tablespace.
    3683                 :             :  *
    3684                 :             :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3685                 :             :  *
    3686                 :             :  * Returns true if the relation can be moved to the new tablespace; raises
    3687                 :             :  * an error if it is not possible to do the move; returns false if the move
    3688                 :             :  * would have no effect.
    3689                 :             :  */
    3690                 :             : bool
    3691                 :          66 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
    3692                 :             : {
    3693                 :          66 :         Oid                     oldTableSpaceId;
    3694                 :             : 
    3695                 :             :         /*
    3696                 :             :          * No work if no change in tablespace.  Note that MyDatabaseTableSpace is
    3697                 :             :          * stored as 0.
    3698                 :             :          */
    3699                 :          66 :         oldTableSpaceId = rel->rd_rel->reltablespace;
    3700   [ +  +  +  + ]:         101 :         if (newTableSpaceId == oldTableSpaceId ||
    3701         [ +  + ]:          65 :                 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
    3702                 :           2 :                 return false;
    3703                 :             : 
    3704                 :             :         /*
    3705                 :             :          * We cannot support moving mapped relations into different tablespaces.
    3706                 :             :          * (In particular this eliminates all shared catalogs.)
    3707                 :             :          */
    3708   [ +  +  +  +  :          64 :         if (RelationIsMapped(rel))
          +  -  +  +  +  
                      - ]
    3709   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    3710                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3711                 :             :                                  errmsg("cannot move system relation \"%s\"",
    3712                 :             :                                                 RelationGetRelationName(rel))));
    3713                 :             : 
    3714                 :             :         /* Cannot move a non-shared relation into pg_global */
    3715         [ +  + ]:          64 :         if (newTableSpaceId == GLOBALTABLESPACE_OID)
    3716   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    3717                 :             :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3718                 :             :                                  errmsg("only shared relations can be placed in pg_global tablespace")));
    3719                 :             : 
    3720                 :             :         /*
    3721                 :             :          * Do not allow moving temp tables of other backends ... their local
    3722                 :             :          * buffer manager is not going to cope.
    3723                 :             :          */
    3724   [ +  +  +  - ]:          62 :         if (RELATION_IS_OTHER_TEMP(rel))
    3725   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    3726                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3727                 :             :                                  errmsg("cannot move temporary tables of other sessions")));
    3728                 :             : 
    3729                 :          62 :         return true;
    3730                 :          64 : }
    3731                 :             : 
    3732                 :             : /*
    3733                 :             :  * SetRelationTableSpace
    3734                 :             :  *              Set new reltablespace and relfilenumber in pg_class entry.
    3735                 :             :  *
    3736                 :             :  * newTableSpaceId is the new tablespace for the relation, and
    3737                 :             :  * newRelFilenumber its new filenumber.  If newRelFilenumber is
    3738                 :             :  * InvalidRelFileNumber, this field is not updated.
    3739                 :             :  *
    3740                 :             :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3741                 :             :  *
    3742                 :             :  * The caller of this routine had better check if a relation can be
    3743                 :             :  * moved to this new tablespace by calling CheckRelationTableSpaceMove()
    3744                 :             :  * first, and is responsible for making the change visible with
    3745                 :             :  * CommandCounterIncrement().
    3746                 :             :  */
    3747                 :             : void
    3748                 :          31 : SetRelationTableSpace(Relation rel,
    3749                 :             :                                           Oid newTableSpaceId,
    3750                 :             :                                           RelFileNumber newRelFilenumber)
    3751                 :             : {
    3752                 :          31 :         Relation        pg_class;
    3753                 :          31 :         HeapTuple       tuple;
    3754                 :          31 :         ItemPointerData otid;
    3755                 :          31 :         Form_pg_class rd_rel;
    3756                 :          31 :         Oid                     reloid = RelationGetRelid(rel);
    3757                 :             : 
    3758         [ +  - ]:          31 :         Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
    3759                 :             : 
    3760                 :             :         /* Get a modifiable copy of the relation's pg_class row. */
    3761                 :          31 :         pg_class = table_open(RelationRelationId, RowExclusiveLock);
    3762                 :             : 
    3763                 :          31 :         tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
    3764         [ +  - ]:          31 :         if (!HeapTupleIsValid(tuple))
    3765   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", reloid);
    3766                 :          31 :         otid = tuple->t_self;
    3767                 :          31 :         rd_rel = (Form_pg_class) GETSTRUCT(tuple);
    3768                 :             : 
    3769                 :             :         /* Update the pg_class row. */
    3770         [ +  + ]:          31 :         rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
    3771                 :          14 :                 InvalidOid : newTableSpaceId;
    3772         [ +  + ]:          31 :         if (RelFileNumberIsValid(newRelFilenumber))
    3773                 :          24 :                 rd_rel->relfilenode = newRelFilenumber;
    3774                 :          31 :         CatalogTupleUpdate(pg_class, &otid, tuple);
    3775                 :          31 :         UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
    3776                 :             : 
    3777                 :             :         /*
    3778                 :             :          * Record dependency on tablespace.  This is only required for relations
    3779                 :             :          * that have no physical storage.
    3780                 :             :          */
    3781   [ +  +  +  +  :          31 :         if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
          +  -  +  +  +  
                      + ]
    3782                 :          10 :                 changeDependencyOnTablespace(RelationRelationId, reloid,
    3783                 :           5 :                                                                          rd_rel->reltablespace);
    3784                 :             : 
    3785                 :          31 :         heap_freetuple(tuple);
    3786                 :          31 :         table_close(pg_class, RowExclusiveLock);
    3787                 :          31 : }
    3788                 :             : 
    3789                 :             : /*
    3790                 :             :  *              renameatt_check                 - basic sanity checks before attribute rename
    3791                 :             :  */
    3792                 :             : static void
    3793                 :         164 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
    3794                 :             : {
    3795                 :         164 :         char            relkind = classform->relkind;
    3796                 :             : 
    3797   [ +  +  +  + ]:         164 :         if (classform->reloftype && !recursing)
    3798   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    3799                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3800                 :             :                                  errmsg("cannot rename column of typed table")));
    3801                 :             : 
    3802                 :             :         /*
    3803                 :             :          * Renaming the columns of sequences or toast tables doesn't actually
    3804                 :             :          * break anything from the system's point of view, since internal
    3805                 :             :          * references are by attnum.  But it doesn't seem right to allow users to
    3806                 :             :          * change names that are hardcoded into the system, hence the following
    3807                 :             :          * restriction.
    3808                 :             :          */
    3809         [ +  + ]:         163 :         if (relkind != RELKIND_RELATION &&
    3810         [ +  + ]:          22 :                 relkind != RELKIND_VIEW &&
    3811         [ +  - ]:          14 :                 relkind != RELKIND_MATVIEW &&
    3812         [ +  + ]:          14 :                 relkind != RELKIND_COMPOSITE_TYPE &&
    3813         [ +  - ]:           6 :                 relkind != RELKIND_INDEX &&
    3814         [ +  - ]:           6 :                 relkind != RELKIND_PARTITIONED_INDEX &&
    3815   [ -  +  #  # ]:           6 :                 relkind != RELKIND_FOREIGN_TABLE &&
    3816                 :           0 :                 relkind != RELKIND_PARTITIONED_TABLE)
    3817   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    3818                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3819                 :             :                                  errmsg("cannot rename columns of relation \"%s\"",
    3820                 :             :                                                 NameStr(classform->relname)),
    3821                 :             :                                  errdetail_relkind_not_supported(relkind)));
    3822                 :             : 
    3823                 :             :         /*
    3824                 :             :          * permissions checking.  only the owner of a class can change its schema.
    3825                 :             :          */
    3826         [ +  - ]:         163 :         if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
    3827                 :           0 :                 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
    3828                 :           0 :                                            NameStr(classform->relname));
    3829   [ +  -  +  - ]:         163 :         if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
    3830   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    3831                 :             :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    3832                 :             :                                  errmsg("permission denied: \"%s\" is a system catalog",
    3833                 :             :                                                 NameStr(classform->relname))));
    3834                 :         163 : }
    3835                 :             : 
    3836                 :             : /*
    3837                 :             :  *              renameatt_internal              - workhorse for renameatt
    3838                 :             :  *
    3839                 :             :  * Return value is the attribute number in the 'myrelid' relation.
    3840                 :             :  */
    3841                 :             : static AttrNumber
    3842                 :          85 : renameatt_internal(Oid myrelid,
    3843                 :             :                                    const char *oldattname,
    3844                 :             :                                    const char *newattname,
    3845                 :             :                                    bool recurse,
    3846                 :             :                                    bool recursing,
    3847                 :             :                                    int expected_parents,
    3848                 :             :                                    DropBehavior behavior)
    3849                 :             : {
    3850                 :          85 :         Relation        targetrelation;
    3851                 :          85 :         Relation        attrelation;
    3852                 :          85 :         HeapTuple       atttup;
    3853                 :          85 :         Form_pg_attribute attform;
    3854                 :          85 :         AttrNumber      attnum;
    3855                 :             : 
    3856                 :             :         /*
    3857                 :             :          * Grab an exclusive lock on the target table, which we will NOT release
    3858                 :             :          * until end of transaction.
    3859                 :             :          */
    3860                 :          85 :         targetrelation = relation_open(myrelid, AccessExclusiveLock);
    3861                 :          85 :         renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
    3862                 :             : 
    3863                 :             :         /*
    3864                 :             :          * if the 'recurse' flag is set then we are supposed to rename this
    3865                 :             :          * attribute in all classes that inherit from 'relname' (as well as in
    3866                 :             :          * 'relname').
    3867                 :             :          *
    3868                 :             :          * any permissions or problems with duplicate attributes will cause the
    3869                 :             :          * whole transaction to abort, which is what we want -- all or nothing.
    3870                 :             :          */
    3871         [ +  + ]:          85 :         if (recurse)
    3872                 :             :         {
    3873                 :          35 :                 List       *child_oids,
    3874                 :             :                                    *child_numparents;
    3875                 :          35 :                 ListCell   *lo,
    3876                 :             :                                    *li;
    3877                 :             : 
    3878                 :             :                 /*
    3879                 :             :                  * we need the number of parents for each child so that the recursive
    3880                 :             :                  * calls to renameatt() can determine whether there are any parents
    3881                 :             :                  * outside the inheritance hierarchy being processed.
    3882                 :             :                  */
    3883                 :          35 :                 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    3884                 :             :                                                                                  &child_numparents);
    3885                 :             : 
    3886                 :             :                 /*
    3887                 :             :                  * find_all_inheritors does the recursive search of the inheritance
    3888                 :             :                  * hierarchy, so all we have to do is process all of the relids in the
    3889                 :             :                  * list that it returns.
    3890                 :             :                  */
    3891   [ +  -  +  +  :         119 :                 forboth(lo, child_oids, li, child_numparents)
          +  -  +  +  +  
                +  +  + ]
    3892                 :             :                 {
    3893                 :          84 :                         Oid                     childrelid = lfirst_oid(lo);
    3894                 :          84 :                         int                     numparents = lfirst_int(li);
    3895                 :             : 
    3896         [ +  + ]:          84 :                         if (childrelid == myrelid)
    3897                 :          40 :                                 continue;
    3898                 :             :                         /* note we need not recurse again */
    3899                 :          44 :                         renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
    3900      [ -  +  + ]:          84 :                 }
    3901                 :          35 :         }
    3902                 :             :         else
    3903                 :             :         {
    3904                 :             :                 /*
    3905                 :             :                  * If we are told not to recurse, there had better not be any child
    3906                 :             :                  * tables; else the rename would put them out of step.
    3907                 :             :                  *
    3908                 :             :                  * expected_parents will only be 0 if we are not already recursing.
    3909                 :             :                  */
    3910   [ +  +  +  + ]:          50 :                 if (expected_parents == 0 &&
    3911                 :           6 :                         find_inheritance_children(myrelid, NoLock) != NIL)
    3912   [ +  -  +  - ]:           2 :                         ereport(ERROR,
    3913                 :             :                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3914                 :             :                                          errmsg("inherited column \"%s\" must be renamed in child tables too",
    3915                 :             :                                                         oldattname)));
    3916                 :             :         }
    3917                 :             : 
    3918                 :             :         /* rename attributes in typed tables of composite type */
    3919         [ +  + ]:          83 :         if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    3920                 :             :         {
    3921                 :           3 :                 List       *child_oids;
    3922                 :           3 :                 ListCell   *lo;
    3923                 :             : 
    3924                 :           6 :                 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
    3925                 :           3 :                                                                                                    RelationGetRelationName(targetrelation),
    3926                 :           3 :                                                                                                    behavior);
    3927                 :             : 
    3928   [ +  +  +  +  :           4 :                 foreach(lo, child_oids)
                   +  + ]
    3929                 :           1 :                         renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
    3930                 :           3 :         }
    3931                 :             : 
    3932                 :          83 :         attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    3933                 :             : 
    3934                 :          83 :         atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
    3935         [ +  + ]:          83 :         if (!HeapTupleIsValid(atttup))
    3936   [ +  -  +  - ]:           4 :                 ereport(ERROR,
    3937                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    3938                 :             :                                  errmsg("column \"%s\" does not exist",
    3939                 :             :                                                 oldattname)));
    3940                 :          79 :         attform = (Form_pg_attribute) GETSTRUCT(atttup);
    3941                 :             : 
    3942                 :          79 :         attnum = attform->attnum;
    3943         [ +  - ]:          79 :         if (attnum <= 0)
    3944   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    3945                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3946                 :             :                                  errmsg("cannot rename system column \"%s\"",
    3947                 :             :                                                 oldattname)));
    3948                 :             : 
    3949                 :             :         /*
    3950                 :             :          * if the attribute is inherited, forbid the renaming.  if this is a
    3951                 :             :          * top-level call to renameatt(), then expected_parents will be 0, so the
    3952                 :             :          * effect of this code will be to prohibit the renaming if the attribute
    3953                 :             :          * is inherited at all.  if this is a recursive call to renameatt(),
    3954                 :             :          * expected_parents will be the number of parents the current relation has
    3955                 :             :          * within the inheritance hierarchy being processed, so we'll prohibit the
    3956                 :             :          * renaming only if there are additional parents from elsewhere.
    3957                 :             :          */
    3958         [ +  + ]:          79 :         if (attform->attinhcount > expected_parents)
    3959   [ +  -  +  - ]:           5 :                 ereport(ERROR,
    3960                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    3961                 :             :                                  errmsg("cannot rename inherited column \"%s\"",
    3962                 :             :                                                 oldattname)));
    3963                 :             : 
    3964                 :             :         /* new name should not already exist */
    3965                 :          74 :         (void) check_for_column_name_collision(targetrelation, newattname, false);
    3966                 :             : 
    3967                 :             :         /* apply the update */
    3968                 :          74 :         namestrcpy(&(attform->attname), newattname);
    3969                 :             : 
    3970                 :          74 :         CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
    3971                 :             : 
    3972         [ -  + ]:          74 :         InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
    3973                 :             : 
    3974                 :          74 :         heap_freetuple(atttup);
    3975                 :             : 
    3976                 :          74 :         table_close(attrelation, RowExclusiveLock);
    3977                 :             : 
    3978                 :          74 :         relation_close(targetrelation, NoLock); /* close rel but keep lock */
    3979                 :             : 
    3980                 :         148 :         return attnum;
    3981                 :          74 : }
    3982                 :             : 
    3983                 :             : /*
    3984                 :             :  * Perform permissions and integrity checks before acquiring a relation lock.
    3985                 :             :  */
    3986                 :             : static void
    3987                 :          66 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
    3988                 :             :                                                                    void *arg)
    3989                 :             : {
    3990                 :          66 :         HeapTuple       tuple;
    3991                 :          66 :         Form_pg_class form;
    3992                 :             : 
    3993                 :          66 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
    3994         [ +  + ]:          66 :         if (!HeapTupleIsValid(tuple))
    3995                 :           6 :                 return;                                 /* concurrently dropped */
    3996                 :          60 :         form = (Form_pg_class) GETSTRUCT(tuple);
    3997                 :          60 :         renameatt_check(relid, form, false);
    3998                 :          60 :         ReleaseSysCache(tuple);
    3999         [ -  + ]:          66 : }
    4000                 :             : 
    4001                 :             : /*
    4002                 :             :  *              renameatt               - changes the name of an attribute in a relation
    4003                 :             :  *
    4004                 :             :  * The returned ObjectAddress is that of the renamed column.
    4005                 :             :  */
    4006                 :             : ObjectAddress
    4007                 :          35 : renameatt(RenameStmt *stmt)
    4008                 :             : {
    4009                 :          35 :         Oid                     relid;
    4010                 :          35 :         AttrNumber      attnum;
    4011                 :          35 :         ObjectAddress address;
    4012                 :             : 
    4013                 :             :         /* lock level taken here should match renameatt_internal */
    4014                 :          70 :         relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    4015                 :          35 :                                                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4016                 :             :                                                                          RangeVarCallbackForRenameAttribute,
    4017                 :             :                                                                          NULL);
    4018                 :             : 
    4019         [ +  + ]:          35 :         if (!OidIsValid(relid))
    4020                 :             :         {
    4021   [ -  +  +  - ]:           4 :                 ereport(NOTICE,
    4022                 :             :                                 (errmsg("relation \"%s\" does not exist, skipping",
    4023                 :             :                                                 stmt->relation->relname)));
    4024                 :           4 :                 return InvalidObjectAddress;
    4025                 :             :         }
    4026                 :             : 
    4027                 :          31 :         attnum =
    4028                 :          62 :                 renameatt_internal(relid,
    4029                 :          31 :                                                    stmt->subname,    /* old att name */
    4030                 :          31 :                                                    stmt->newname,    /* new att name */
    4031                 :          31 :                                                    stmt->relation->inh, /* recursive? */
    4032                 :             :                                                    false,       /* recursing? */
    4033                 :             :                                                    0,   /* expected inhcount */
    4034                 :          31 :                                                    stmt->behavior);
    4035                 :             : 
    4036                 :          31 :         ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
    4037                 :             : 
    4038                 :          31 :         return address;
    4039                 :          35 : }
    4040                 :             : 
    4041                 :             : /*
    4042                 :             :  * same logic as renameatt_internal
    4043                 :             :  */
    4044                 :             : static ObjectAddress
    4045                 :          15 : rename_constraint_internal(Oid myrelid,
    4046                 :             :                                                    Oid mytypid,
    4047                 :             :                                                    const char *oldconname,
    4048                 :             :                                                    const char *newconname,
    4049                 :             :                                                    bool recurse,
    4050                 :             :                                                    bool recursing,
    4051                 :             :                                                    int expected_parents)
    4052                 :             : {
    4053                 :          15 :         Relation        targetrelation = NULL;
    4054                 :          15 :         Oid                     constraintOid;
    4055                 :          15 :         HeapTuple       tuple;
    4056                 :          15 :         Form_pg_constraint con;
    4057                 :             :         ObjectAddress address;
    4058                 :             : 
    4059   [ +  +  +  - ]:          15 :         Assert(!myrelid || !mytypid);
    4060                 :             : 
    4061         [ +  + ]:          15 :         if (mytypid)
    4062                 :             :         {
    4063                 :           1 :                 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
    4064                 :           1 :         }
    4065                 :             :         else
    4066                 :             :         {
    4067                 :          14 :                 targetrelation = relation_open(myrelid, AccessExclusiveLock);
    4068                 :             : 
    4069                 :             :                 /*
    4070                 :             :                  * don't tell it whether we're recursing; we allow changing typed
    4071                 :             :                  * tables here
    4072                 :             :                  */
    4073                 :          14 :                 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
    4074                 :             : 
    4075                 :          14 :                 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
    4076                 :             :         }
    4077                 :             : 
    4078                 :          15 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
    4079         [ +  - ]:          15 :         if (!HeapTupleIsValid(tuple))
    4080   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for constraint %u",
    4081                 :             :                          constraintOid);
    4082                 :          15 :         con = (Form_pg_constraint) GETSTRUCT(tuple);
    4083                 :             : 
    4084         [ +  + ]:          15 :         if (myrelid &&
    4085         [ +  + ]:          14 :                 (con->contype == CONSTRAINT_CHECK ||
    4086         [ +  + ]:          14 :                  con->contype == CONSTRAINT_NOTNULL) &&
    4087                 :          14 :                 !con->connoinherit)
    4088                 :             :         {
    4089         [ +  + ]:           9 :                 if (recurse)
    4090                 :             :                 {
    4091                 :           6 :                         List       *child_oids,
    4092                 :             :                                            *child_numparents;
    4093                 :           6 :                         ListCell   *lo,
    4094                 :             :                                            *li;
    4095                 :             : 
    4096                 :           6 :                         child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    4097                 :             :                                                                                          &child_numparents);
    4098                 :             : 
    4099   [ +  -  +  +  :          14 :                         forboth(lo, child_oids, li, child_numparents)
          +  -  +  +  +  
                +  +  + ]
    4100                 :             :                         {
    4101                 :           8 :                                 Oid                     childrelid = lfirst_oid(lo);
    4102                 :           8 :                                 int                     numparents = lfirst_int(li);
    4103                 :             : 
    4104         [ +  + ]:           8 :                                 if (childrelid == myrelid)
    4105                 :           6 :                                         continue;
    4106                 :             : 
    4107                 :           2 :                                 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
    4108      [ -  +  + ]:           8 :                         }
    4109                 :           6 :                 }
    4110                 :             :                 else
    4111                 :             :                 {
    4112   [ +  +  -  + ]:           3 :                         if (expected_parents == 0 &&
    4113                 :           1 :                                 find_inheritance_children(myrelid, NoLock) != NIL)
    4114   [ +  -  +  - ]:           1 :                                 ereport(ERROR,
    4115                 :             :                                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4116                 :             :                                                  errmsg("inherited constraint \"%s\" must be renamed in child tables too",
    4117                 :             :                                                                 oldconname)));
    4118                 :             :                 }
    4119                 :             : 
    4120         [ +  + ]:           8 :                 if (con->coninhcount > expected_parents)
    4121   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    4122                 :             :                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4123                 :             :                                          errmsg("cannot rename inherited constraint \"%s\"",
    4124                 :             :                                                         oldconname)));
    4125                 :           7 :         }
    4126                 :             : 
    4127                 :          13 :         if (con->conindid
    4128   [ +  +  #  # ]:          13 :                 && (con->contype == CONSTRAINT_PRIMARY
    4129         [ +  + ]:           3 :                         || con->contype == CONSTRAINT_UNIQUE
    4130         [ -  + ]:           1 :                         || con->contype == CONSTRAINT_EXCLUSION))
    4131                 :             :                 /* rename the index; this renames the constraint as well */
    4132                 :           3 :                 RenameRelationInternal(con->conindid, newconname, false, true);
    4133                 :             :         else
    4134                 :          10 :                 RenameConstraintById(constraintOid, newconname);
    4135                 :             : 
    4136                 :          13 :         ObjectAddressSet(address, ConstraintRelationId, constraintOid);
    4137                 :             : 
    4138                 :          13 :         ReleaseSysCache(tuple);
    4139                 :             : 
    4140         [ +  + ]:          13 :         if (targetrelation)
    4141                 :             :         {
    4142                 :             :                 /*
    4143                 :             :                  * Invalidate relcache so as others can see the new constraint name.
    4144                 :             :                  */
    4145                 :          12 :                 CacheInvalidateRelcache(targetrelation);
    4146                 :             : 
    4147                 :          12 :                 relation_close(targetrelation, NoLock); /* close rel but keep lock */
    4148                 :          12 :         }
    4149                 :             : 
    4150                 :             :         return address;
    4151                 :          13 : }
    4152                 :             : 
    4153                 :             : ObjectAddress
    4154                 :          14 : RenameConstraint(RenameStmt *stmt)
    4155                 :             : {
    4156                 :          14 :         Oid                     relid = InvalidOid;
    4157                 :          14 :         Oid                     typid = InvalidOid;
    4158                 :             : 
    4159         [ +  + ]:          14 :         if (stmt->renameType == OBJECT_DOMCONSTRAINT)
    4160                 :             :         {
    4161                 :           1 :                 Relation        rel;
    4162                 :           1 :                 HeapTuple       tup;
    4163                 :             : 
    4164                 :           1 :                 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
    4165                 :           1 :                 rel = table_open(TypeRelationId, RowExclusiveLock);
    4166                 :           1 :                 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
    4167         [ +  - ]:           1 :                 if (!HeapTupleIsValid(tup))
    4168   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for type %u", typid);
    4169                 :           1 :                 checkDomainOwner(tup);
    4170                 :           1 :                 ReleaseSysCache(tup);
    4171                 :           1 :                 table_close(rel, NoLock);
    4172                 :           1 :         }
    4173                 :             :         else
    4174                 :             :         {
    4175                 :             :                 /* lock level taken here should match rename_constraint_internal */
    4176                 :          26 :                 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    4177                 :          13 :                                                                                  stmt->missing_ok ? RVR_MISSING_OK : 0,
    4178                 :             :                                                                                  RangeVarCallbackForRenameAttribute,
    4179                 :             :                                                                                  NULL);
    4180         [ +  + ]:          13 :                 if (!OidIsValid(relid))
    4181                 :             :                 {
    4182   [ -  +  +  - ]:           1 :                         ereport(NOTICE,
    4183                 :             :                                         (errmsg("relation \"%s\" does not exist, skipping",
    4184                 :             :                                                         stmt->relation->relname)));
    4185                 :           1 :                         return InvalidObjectAddress;
    4186                 :             :                 }
    4187                 :             :         }
    4188                 :             : 
    4189                 :          13 :         return
    4190                 :          25 :                 rename_constraint_internal(relid, typid,
    4191                 :          13 :                                                                    stmt->subname,
    4192                 :          13 :                                                                    stmt->newname,
    4193         [ +  + ]:          13 :                                                                    (stmt->relation &&
    4194                 :          12 :                                                                         stmt->relation->inh),     /* recursive? */
    4195                 :             :                                                                    false,       /* recursing? */
    4196                 :             :                                                                    0 /* expected inhcount */ );
    4197                 :          14 : }
    4198                 :             : 
    4199                 :             : /*
    4200                 :             :  * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
    4201                 :             :  * RENAME
    4202                 :             :  */
    4203                 :             : ObjectAddress
    4204                 :          43 : RenameRelation(RenameStmt *stmt)
    4205                 :             : {
    4206                 :          43 :         bool            is_index_stmt = stmt->renameType == OBJECT_INDEX;
    4207                 :          43 :         Oid                     relid;
    4208                 :          43 :         ObjectAddress address;
    4209                 :             : 
    4210                 :             :         /*
    4211                 :             :          * Grab an exclusive lock on the target table, index, sequence, view,
    4212                 :             :          * materialized view, or foreign table, which we will NOT release until
    4213                 :             :          * end of transaction.
    4214                 :             :          *
    4215                 :             :          * Lock level used here should match RenameRelationInternal, to avoid lock
    4216                 :             :          * escalation.  However, because ALTER INDEX can be used with any relation
    4217                 :             :          * type, we mustn't believe without verification.
    4218                 :             :          */
    4219                 :          49 :         for (;;)
    4220                 :             :         {
    4221                 :          49 :                 LOCKMODE        lockmode;
    4222                 :          49 :                 char            relkind;
    4223                 :          49 :                 bool            obj_is_index;
    4224                 :             : 
    4225                 :          49 :                 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
    4226                 :             : 
    4227                 :          98 :                 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
    4228                 :          49 :                                                                                  stmt->missing_ok ? RVR_MISSING_OK : 0,
    4229                 :             :                                                                                  RangeVarCallbackForAlterRelation,
    4230                 :          49 :                                                                                  stmt);
    4231                 :             : 
    4232         [ +  + ]:          49 :                 if (!OidIsValid(relid))
    4233                 :             :                 {
    4234   [ -  +  +  - ]:           3 :                         ereport(NOTICE,
    4235                 :             :                                         (errmsg("relation \"%s\" does not exist, skipping",
    4236                 :             :                                                         stmt->relation->relname)));
    4237                 :           3 :                         return InvalidObjectAddress;
    4238                 :             :                 }
    4239                 :             : 
    4240                 :             :                 /*
    4241                 :             :                  * We allow mismatched statement and object types (e.g., ALTER INDEX
    4242                 :             :                  * to rename a table), but we might've used the wrong lock level.  If
    4243                 :             :                  * that happens, retry with the correct lock level.  We don't bother
    4244                 :             :                  * if we already acquired AccessExclusiveLock with an index, however.
    4245                 :             :                  */
    4246                 :          34 :                 relkind = get_rel_relkind(relid);
    4247         [ +  + ]:          34 :                 obj_is_index = (relkind == RELKIND_INDEX ||
    4248                 :          28 :                                                 relkind == RELKIND_PARTITIONED_INDEX);
    4249   [ +  +  +  + ]:          34 :                 if (obj_is_index || is_index_stmt == obj_is_index)
    4250                 :          32 :                         break;
    4251                 :             : 
    4252                 :           2 :                 UnlockRelationOid(relid, lockmode);
    4253                 :           2 :                 is_index_stmt = obj_is_index;
    4254      [ +  +  + ]:          37 :         }
    4255                 :             : 
    4256                 :             :         /* Do the work */
    4257                 :          30 :         RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
    4258                 :             : 
    4259                 :          30 :         ObjectAddressSet(address, RelationRelationId, relid);
    4260                 :             : 
    4261                 :          30 :         return address;
    4262                 :          31 : }
    4263                 :             : 
    4264                 :             : /*
    4265                 :             :  *              RenameRelationInternal - change the name of a relation
    4266                 :             :  */
    4267                 :             : void
    4268                 :         206 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
    4269                 :             : {
    4270                 :         206 :         Relation        targetrelation;
    4271                 :         206 :         Relation        relrelation;    /* for RELATION relation */
    4272                 :         206 :         ItemPointerData otid;
    4273                 :         206 :         HeapTuple       reltup;
    4274                 :         206 :         Form_pg_class relform;
    4275                 :         206 :         Oid                     namespaceId;
    4276                 :             : 
    4277                 :             :         /*
    4278                 :             :          * Grab a lock on the target relation, which we will NOT release until end
    4279                 :             :          * of transaction.  We need at least a self-exclusive lock so that
    4280                 :             :          * concurrent DDL doesn't overwrite the rename if they start updating
    4281                 :             :          * while still seeing the old version.  The lock also guards against
    4282                 :             :          * triggering relcache reloads in concurrent sessions, which might not
    4283                 :             :          * handle this information changing under them.  For indexes, we can use a
    4284                 :             :          * reduced lock level because RelationReloadIndexInfo() handles indexes
    4285                 :             :          * specially.
    4286                 :             :          */
    4287                 :         206 :         targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
    4288                 :         206 :         namespaceId = RelationGetNamespace(targetrelation);
    4289                 :             : 
    4290                 :             :         /*
    4291                 :             :          * Find relation's pg_class tuple, and make sure newrelname isn't in use.
    4292                 :             :          */
    4293                 :         206 :         relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4294                 :             : 
    4295                 :         206 :         reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4296         [ +  - ]:         206 :         if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4297   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4298                 :         206 :         otid = reltup->t_self;
    4299                 :         206 :         relform = (Form_pg_class) GETSTRUCT(reltup);
    4300                 :             : 
    4301         [ +  + ]:         206 :         if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
    4302   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    4303                 :             :                                 (errcode(ERRCODE_DUPLICATE_TABLE),
    4304                 :             :                                  errmsg("relation \"%s\" already exists",
    4305                 :             :                                                 newrelname)));
    4306                 :             : 
    4307                 :             :         /*
    4308                 :             :          * RenameRelation is careful not to believe the caller's idea of the
    4309                 :             :          * relation kind being handled.  We don't have to worry about this, but
    4310                 :             :          * let's not be totally oblivious to it.  We can process an index as
    4311                 :             :          * not-an-index, but not the other way around.
    4312                 :             :          */
    4313   [ +  +  +  +  :         204 :         Assert(!is_index ||
                   +  - ]
    4314                 :             :                    is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4315                 :             :                                                 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
    4316                 :             : 
    4317                 :             :         /*
    4318                 :             :          * Update pg_class tuple with new relname.  (Scribbling on reltup is OK
    4319                 :             :          * because it's a copy...)
    4320                 :             :          */
    4321                 :         204 :         namestrcpy(&(relform->relname), newrelname);
    4322                 :             : 
    4323                 :         204 :         CatalogTupleUpdate(relrelation, &otid, reltup);
    4324                 :         204 :         UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
    4325                 :             : 
    4326         [ +  - ]:         204 :         InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
    4327                 :             :                                                                  InvalidOid, is_internal);
    4328                 :             : 
    4329                 :         204 :         heap_freetuple(reltup);
    4330                 :         204 :         table_close(relrelation, RowExclusiveLock);
    4331                 :             : 
    4332                 :             :         /*
    4333                 :             :          * Also rename the associated type, if any.
    4334                 :             :          */
    4335         [ +  + ]:         204 :         if (OidIsValid(targetrelation->rd_rel->reltype))
    4336                 :          58 :                 RenameTypeInternal(targetrelation->rd_rel->reltype,
    4337                 :          29 :                                                    newrelname, namespaceId);
    4338                 :             : 
    4339                 :             :         /*
    4340                 :             :          * Also rename the associated constraint, if any.
    4341                 :             :          */
    4342   [ +  +  +  + ]:         204 :         if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4343                 :         113 :                 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    4344                 :             :         {
    4345                 :          94 :                 Oid                     constraintId = get_index_constraint(myrelid);
    4346                 :             : 
    4347         [ +  + ]:          94 :                 if (OidIsValid(constraintId))
    4348                 :           6 :                         RenameConstraintById(constraintId, newrelname);
    4349                 :          94 :         }
    4350                 :             : 
    4351                 :             :         /*
    4352                 :             :          * Close rel, but keep lock!
    4353                 :             :          */
    4354                 :         204 :         relation_close(targetrelation, NoLock);
    4355                 :         204 : }
    4356                 :             : 
    4357                 :             : /*
    4358                 :             :  *              ResetRelRewrite - reset relrewrite
    4359                 :             :  */
    4360                 :             : void
    4361                 :          79 : ResetRelRewrite(Oid myrelid)
    4362                 :             : {
    4363                 :          79 :         Relation        relrelation;    /* for RELATION relation */
    4364                 :          79 :         HeapTuple       reltup;
    4365                 :          79 :         Form_pg_class relform;
    4366                 :             : 
    4367                 :             :         /*
    4368                 :             :          * Find relation's pg_class tuple.
    4369                 :             :          */
    4370                 :          79 :         relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4371                 :             : 
    4372                 :          79 :         reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4373         [ +  - ]:          79 :         if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4374   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4375                 :          79 :         relform = (Form_pg_class) GETSTRUCT(reltup);
    4376                 :             : 
    4377                 :             :         /*
    4378                 :             :          * Update pg_class tuple.
    4379                 :             :          */
    4380                 :          79 :         relform->relrewrite = InvalidOid;
    4381                 :             : 
    4382                 :          79 :         CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
    4383                 :             : 
    4384                 :          79 :         heap_freetuple(reltup);
    4385                 :          79 :         table_close(relrelation, RowExclusiveLock);
    4386                 :          79 : }
    4387                 :             : 
    4388                 :             : /*
    4389                 :             :  * Disallow ALTER TABLE (and similar commands) when the current backend has
    4390                 :             :  * any open reference to the target table besides the one just acquired by
    4391                 :             :  * the calling command; this implies there's an open cursor or active plan.
    4392                 :             :  * We need this check because our lock doesn't protect us against stomping
    4393                 :             :  * on our own foot, only other people's feet!
    4394                 :             :  *
    4395                 :             :  * For ALTER TABLE, the only case known to cause serious trouble is ALTER
    4396                 :             :  * COLUMN TYPE, and some changes are obviously pretty benign, so this could
    4397                 :             :  * possibly be relaxed to only error out for certain types of alterations.
    4398                 :             :  * But the use-case for allowing any of these things is not obvious, so we
    4399                 :             :  * won't work hard at it for now.
    4400                 :             :  *
    4401                 :             :  * We also reject these commands if there are any pending AFTER trigger events
    4402                 :             :  * for the rel.  This is certainly necessary for the rewriting variants of
    4403                 :             :  * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
    4404                 :             :  * events would try to fetch the wrong tuples.  It might be overly cautious
    4405                 :             :  * in other cases, but again it seems better to err on the side of paranoia.
    4406                 :             :  *
    4407                 :             :  * REINDEX calls this with "rel" referencing the index to be rebuilt; here
    4408                 :             :  * we are worried about active indexscans on the index.  The trigger-event
    4409                 :             :  * check can be skipped, since we are doing no damage to the parent table.
    4410                 :             :  *
    4411                 :             :  * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
    4412                 :             :  */
    4413                 :             : void
    4414                 :       17757 : CheckTableNotInUse(Relation rel, const char *stmt)
    4415                 :             : {
    4416                 :       17757 :         int                     expected_refcnt;
    4417                 :             : 
    4418                 :       17757 :         expected_refcnt = rel->rd_isnailed ? 2 : 1;
    4419         [ +  + ]:       17757 :         if (rel->rd_refcnt != expected_refcnt)
    4420   [ +  -  +  - ]:           7 :                 ereport(ERROR,
    4421                 :             :                                 (errcode(ERRCODE_OBJECT_IN_USE),
    4422                 :             :                 /* translator: first %s is a SQL command, eg ALTER TABLE */
    4423                 :             :                                  errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
    4424                 :             :                                                 stmt, RelationGetRelationName(rel))));
    4425                 :             : 
    4426         [ +  + ]:       17750 :         if (rel->rd_rel->relkind != RELKIND_INDEX &&
    4427   [ +  +  +  + ]:       14568 :                 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    4428                 :       14250 :                 AfterTriggerPendingOnRel(RelationGetRelid(rel)))
    4429   [ +  -  +  - ]:           3 :                 ereport(ERROR,
    4430                 :             :                                 (errcode(ERRCODE_OBJECT_IN_USE),
    4431                 :             :                 /* translator: first %s is a SQL command, eg ALTER TABLE */
    4432                 :             :                                  errmsg("cannot %s \"%s\" because it has pending trigger events",
    4433                 :             :                                                 stmt, RelationGetRelationName(rel))));
    4434                 :       17747 : }
    4435                 :             : 
    4436                 :             : /*
    4437                 :             :  * CheckAlterTableIsSafe
    4438                 :             :  *              Verify that it's safe to allow ALTER TABLE on this relation.
    4439                 :             :  *
    4440                 :             :  * This consists of CheckTableNotInUse() plus a check that the relation
    4441                 :             :  * isn't another session's temp table.  We must split out the temp-table
    4442                 :             :  * check because there are callers of CheckTableNotInUse() that don't want
    4443                 :             :  * that, notably DROP TABLE.  (We must allow DROP or we couldn't clean out
    4444                 :             :  * an orphaned temp schema.)  Compare truncate_check_activity().
    4445                 :             :  */
    4446                 :             : static void
    4447                 :        5287 : CheckAlterTableIsSafe(Relation rel)
    4448                 :             : {
    4449                 :             :         /*
    4450                 :             :          * Don't allow ALTER on temp tables of other backends.  Their local buffer
    4451                 :             :          * manager is not going to cope if we need to change the table's contents.
    4452                 :             :          * Even if we don't, there may be optimizations that assume temp tables
    4453                 :             :          * aren't subject to such interference.
    4454                 :             :          */
    4455   [ +  +  +  - ]:        5287 :         if (RELATION_IS_OTHER_TEMP(rel))
    4456   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    4457                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4458                 :             :                                  errmsg("cannot alter temporary tables of other sessions")));
    4459                 :             : 
    4460                 :             :         /*
    4461                 :             :          * Also check for active uses of the relation in the current transaction,
    4462                 :             :          * including open scans and pending AFTER trigger events.
    4463                 :             :          */
    4464                 :        5287 :         CheckTableNotInUse(rel, "ALTER TABLE");
    4465                 :        5287 : }
    4466                 :             : 
    4467                 :             : /*
    4468                 :             :  * AlterTableLookupRelation
    4469                 :             :  *              Look up, and lock, the OID for the relation named by an alter table
    4470                 :             :  *              statement.
    4471                 :             :  */
    4472                 :             : Oid
    4473                 :        2866 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
    4474                 :             : {
    4475                 :        5732 :         return RangeVarGetRelidExtended(stmt->relation, lockmode,
    4476                 :        2866 :                                                                         stmt->missing_ok ? RVR_MISSING_OK : 0,
    4477                 :             :                                                                         RangeVarCallbackForAlterRelation,
    4478                 :        2866 :                                                                         stmt);
    4479                 :             : }
    4480                 :             : 
    4481                 :             : /*
    4482                 :             :  * AlterTable
    4483                 :             :  *              Execute ALTER TABLE, which can be a list of subcommands
    4484                 :             :  *
    4485                 :             :  * ALTER TABLE is performed in three phases:
    4486                 :             :  *              1. Examine subcommands and perform pre-transformation checking.
    4487                 :             :  *              2. Validate and transform subcommands, and update system catalogs.
    4488                 :             :  *              3. Scan table(s) to check new constraints, and optionally recopy
    4489                 :             :  *                 the data into new table(s).
    4490                 :             :  * Phase 3 is not performed unless one or more of the subcommands requires
    4491                 :             :  * it.  The intention of this design is to allow multiple independent
    4492                 :             :  * updates of the table schema to be performed with only one pass over the
    4493                 :             :  * data.
    4494                 :             :  *
    4495                 :             :  * ATPrepCmd performs phase 1.  A "work queue" entry is created for
    4496                 :             :  * each table to be affected (there may be multiple affected tables if the
    4497                 :             :  * commands traverse a table inheritance hierarchy).  Also we do preliminary
    4498                 :             :  * validation of the subcommands.  Because earlier subcommands may change
    4499                 :             :  * the catalog state seen by later commands, there are limits to what can
    4500                 :             :  * be done in this phase.  Generally, this phase acquires table locks,
    4501                 :             :  * checks permissions and relkind, and recurses to find child tables.
    4502                 :             :  *
    4503                 :             :  * ATRewriteCatalogs performs phase 2 for each affected table.
    4504                 :             :  * Certain subcommands need to be performed before others to avoid
    4505                 :             :  * unnecessary conflicts; for example, DROP COLUMN should come before
    4506                 :             :  * ADD COLUMN.  Therefore phase 1 divides the subcommands into multiple
    4507                 :             :  * lists, one for each logical "pass" of phase 2.
    4508                 :             :  *
    4509                 :             :  * ATRewriteTables performs phase 3 for those tables that need it.
    4510                 :             :  *
    4511                 :             :  * For most subcommand types, phases 2 and 3 do no explicit recursion,
    4512                 :             :  * since phase 1 already does it.  However, for certain subcommand types
    4513                 :             :  * it is only possible to determine how to recurse at phase 2 time; for
    4514                 :             :  * those cases, phase 1 sets the cmd->recurse flag.
    4515                 :             :  *
    4516                 :             :  * Thanks to the magic of MVCC, an error anywhere along the way rolls back
    4517                 :             :  * the whole operation; we don't have to do anything special to clean up.
    4518                 :             :  *
    4519                 :             :  * The caller must lock the relation, with an appropriate lock level
    4520                 :             :  * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
    4521                 :             :  * or higher. We pass the lock level down
    4522                 :             :  * so that we can apply it recursively to inherited tables. Note that the
    4523                 :             :  * lock level we want as we recurse might well be higher than required for
    4524                 :             :  * that specific subcommand. So we pass down the overall lock requirement,
    4525                 :             :  * rather than reassess it at lower levels.
    4526                 :             :  *
    4527                 :             :  * The caller also provides a "context" which is to be passed back to
    4528                 :             :  * utility.c when we need to execute a subcommand such as CREATE INDEX.
    4529                 :             :  * Some of the fields therein, such as the relid, are used here as well.
    4530                 :             :  */
    4531                 :             : void
    4532                 :        2828 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
    4533                 :             :                    AlterTableUtilityContext *context)
    4534                 :             : {
    4535                 :        2828 :         Relation        rel;
    4536                 :             : 
    4537                 :             :         /* Caller is required to provide an adequate lock. */
    4538                 :        2828 :         rel = relation_open(context->relid, NoLock);
    4539                 :             : 
    4540                 :        2828 :         CheckAlterTableIsSafe(rel);
    4541                 :             : 
    4542                 :        2828 :         ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
    4543                 :        2828 : }
    4544                 :             : 
    4545                 :             : /*
    4546                 :             :  * AlterTableInternal
    4547                 :             :  *
    4548                 :             :  * ALTER TABLE with target specified by OID
    4549                 :             :  *
    4550                 :             :  * We do not reject if the relation is already open, because it's quite
    4551                 :             :  * likely that one or more layers of caller have it open.  That means it
    4552                 :             :  * is unsafe to use this entry point for alterations that could break
    4553                 :             :  * existing query plans.  On the assumption it's not used for such, we
    4554                 :             :  * don't have to reject pending AFTER triggers, either.
    4555                 :             :  *
    4556                 :             :  * Also, since we don't have an AlterTableUtilityContext, this cannot be
    4557                 :             :  * used for any subcommand types that require parse transformation or
    4558                 :             :  * could generate subcommands that have to be passed to ProcessUtility.
    4559                 :             :  */
    4560                 :             : void
    4561                 :          45 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
    4562                 :             : {
    4563                 :          45 :         Relation        rel;
    4564                 :          45 :         LOCKMODE        lockmode = AlterTableGetLockLevel(cmds);
    4565                 :             : 
    4566                 :          45 :         rel = relation_open(relid, lockmode);
    4567                 :             : 
    4568                 :          45 :         EventTriggerAlterTableRelid(relid);
    4569                 :             : 
    4570                 :          45 :         ATController(NULL, rel, cmds, recurse, lockmode, NULL);
    4571                 :          45 : }
    4572                 :             : 
    4573                 :             : /*
    4574                 :             :  * AlterTableGetLockLevel
    4575                 :             :  *
    4576                 :             :  * Sets the overall lock level required for the supplied list of subcommands.
    4577                 :             :  * Policy for doing this set according to needs of AlterTable(), see
    4578                 :             :  * comments there for overall explanation.
    4579                 :             :  *
    4580                 :             :  * Function is called before and after parsing, so it must give same
    4581                 :             :  * answer each time it is called. Some subcommands are transformed
    4582                 :             :  * into other subcommand types, so the transform must never be made to a
    4583                 :             :  * lower lock level than previously assigned. All transforms are noted below.
    4584                 :             :  *
    4585                 :             :  * Since this is called before we lock the table we cannot use table metadata
    4586                 :             :  * to influence the type of lock we acquire.
    4587                 :             :  *
    4588                 :             :  * There should be no lockmodes hardcoded into the subcommand functions. All
    4589                 :             :  * lockmode decisions for ALTER TABLE are made here only. The one exception is
    4590                 :             :  * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
    4591                 :             :  * and does not travel through this section of code and cannot be combined with
    4592                 :             :  * any of the subcommands given here.
    4593                 :             :  *
    4594                 :             :  * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
    4595                 :             :  * so any changes that might affect SELECTs running on standbys need to use
    4596                 :             :  * AccessExclusiveLocks even if you think a lesser lock would do, unless you
    4597                 :             :  * have a solution for that also.
    4598                 :             :  *
    4599                 :             :  * Also note that pg_dump uses only an AccessShareLock, meaning that anything
    4600                 :             :  * that takes a lock less than AccessExclusiveLock can change object definitions
    4601                 :             :  * while pg_dump is running. Be careful to check that the appropriate data is
    4602                 :             :  * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
    4603                 :             :  * otherwise we might end up with an inconsistent dump that can't restore.
    4604                 :             :  */
    4605                 :             : LOCKMODE
    4606                 :        2911 : AlterTableGetLockLevel(List *cmds)
    4607                 :             : {
    4608                 :             :         /*
    4609                 :             :          * This only works if we read catalog tables using MVCC snapshots.
    4610                 :             :          */
    4611                 :        2911 :         ListCell   *lcmd;
    4612                 :        2911 :         LOCKMODE        lockmode = ShareUpdateExclusiveLock;
    4613                 :             : 
    4614   [ +  -  +  +  :        6018 :         foreach(lcmd, cmds)
                   +  + ]
    4615                 :             :         {
    4616                 :        3107 :                 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4617                 :        3107 :                 LOCKMODE        cmd_lockmode = AccessExclusiveLock; /* default for compiler */
    4618                 :             : 
    4619   [ +  +  +  +  :        3107 :                 switch (cmd->subtype)
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
                   +  - ]
    4620                 :             :                 {
    4621                 :             :                                 /*
    4622                 :             :                                  * These subcommands rewrite the heap, so require full locks.
    4623                 :             :                                  */
    4624                 :             :                         case AT_AddColumn:      /* may rewrite heap, in some cases and visible
    4625                 :             :                                                                  * to SELECT */
    4626                 :             :                         case AT_SetAccessMethod:        /* must rewrite heap */
    4627                 :             :                         case AT_SetTableSpace:  /* must rewrite heap */
    4628                 :             :                         case AT_AlterColumnType:        /* must rewrite heap */
    4629                 :         545 :                                 cmd_lockmode = AccessExclusiveLock;
    4630                 :         545 :                                 break;
    4631                 :             : 
    4632                 :             :                                 /*
    4633                 :             :                                  * These subcommands may require addition of toast tables. If
    4634                 :             :                                  * we add a toast table to a table currently being scanned, we
    4635                 :             :                                  * might miss data added to the new toast table by concurrent
    4636                 :             :                                  * insert transactions.
    4637                 :             :                                  */
    4638                 :             :                         case AT_SetStorage: /* may add toast tables, see
    4639                 :             :                                                                  * ATRewriteCatalogs() */
    4640                 :          28 :                                 cmd_lockmode = AccessExclusiveLock;
    4641                 :          28 :                                 break;
    4642                 :             : 
    4643                 :             :                                 /*
    4644                 :             :                                  * Removing constraints can affect SELECTs that have been
    4645                 :             :                                  * optimized assuming the constraint holds true. See also
    4646                 :             :                                  * CloneFkReferenced.
    4647                 :             :                                  */
    4648                 :             :                         case AT_DropConstraint: /* as DROP INDEX */
    4649                 :             :                         case AT_DropNotNull:    /* may change some SQL plans */
    4650                 :         182 :                                 cmd_lockmode = AccessExclusiveLock;
    4651                 :         182 :                                 break;
    4652                 :             : 
    4653                 :             :                                 /*
    4654                 :             :                                  * Subcommands that may be visible to concurrent SELECTs
    4655                 :             :                                  */
    4656                 :             :                         case AT_DropColumn: /* change visible to SELECT */
    4657                 :             :                         case AT_AddColumnToView:        /* CREATE VIEW */
    4658                 :             :                         case AT_DropOids:       /* used to equiv to DropColumn */
    4659                 :             :                         case AT_EnableAlwaysRule:       /* may change SELECT rules */
    4660                 :             :                         case AT_EnableReplicaRule:      /* may change SELECT rules */
    4661                 :             :                         case AT_EnableRule: /* may change SELECT rules */
    4662                 :             :                         case AT_DisableRule:    /* may change SELECT rules */
    4663                 :         252 :                                 cmd_lockmode = AccessExclusiveLock;
    4664                 :         252 :                                 break;
    4665                 :             : 
    4666                 :             :                                 /*
    4667                 :             :                                  * Changing owner may remove implicit SELECT privileges
    4668                 :             :                                  */
    4669                 :             :                         case AT_ChangeOwner:    /* change visible to SELECT */
    4670                 :          46 :                                 cmd_lockmode = AccessExclusiveLock;
    4671                 :          46 :                                 break;
    4672                 :             : 
    4673                 :             :                                 /*
    4674                 :             :                                  * Changing foreign table options may affect optimization.
    4675                 :             :                                  */
    4676                 :             :                         case AT_GenericOptions:
    4677                 :             :                         case AT_AlterColumnGenericOptions:
    4678                 :          12 :                                 cmd_lockmode = AccessExclusiveLock;
    4679                 :          12 :                                 break;
    4680                 :             : 
    4681                 :             :                                 /*
    4682                 :             :                                  * These subcommands affect write operations only.
    4683                 :             :                                  */
    4684                 :             :                         case AT_EnableTrig:
    4685                 :             :                         case AT_EnableAlwaysTrig:
    4686                 :             :                         case AT_EnableReplicaTrig:
    4687                 :             :                         case AT_EnableTrigAll:
    4688                 :             :                         case AT_EnableTrigUser:
    4689                 :             :                         case AT_DisableTrig:
    4690                 :             :                         case AT_DisableTrigAll:
    4691                 :             :                         case AT_DisableTrigUser:
    4692                 :          20 :                                 cmd_lockmode = ShareRowExclusiveLock;
    4693                 :          20 :                                 break;
    4694                 :             : 
    4695                 :             :                                 /*
    4696                 :             :                                  * These subcommands affect write operations only. XXX
    4697                 :             :                                  * Theoretically, these could be ShareRowExclusiveLock.
    4698                 :             :                                  */
    4699                 :             :                         case AT_ColumnDefault:
    4700                 :             :                         case AT_CookedColumnDefault:
    4701                 :             :                         case AT_AlterConstraint:
    4702                 :             :                         case AT_AddIndex:       /* from ADD CONSTRAINT */
    4703                 :             :                         case AT_AddIndexConstraint:
    4704                 :             :                         case AT_ReplicaIdentity:
    4705                 :             :                         case AT_SetNotNull:
    4706                 :             :                         case AT_EnableRowSecurity:
    4707                 :             :                         case AT_DisableRowSecurity:
    4708                 :             :                         case AT_ForceRowSecurity:
    4709                 :             :                         case AT_NoForceRowSecurity:
    4710                 :             :                         case AT_AddIdentity:
    4711                 :             :                         case AT_DropIdentity:
    4712                 :             :                         case AT_SetIdentity:
    4713                 :             :                         case AT_SetExpression:
    4714                 :             :                         case AT_DropExpression:
    4715                 :             :                         case AT_SetCompression:
    4716                 :         434 :                                 cmd_lockmode = AccessExclusiveLock;
    4717                 :         434 :                                 break;
    4718                 :             : 
    4719                 :             :                         case AT_AddConstraint:
    4720                 :             :                         case AT_ReAddConstraint:        /* becomes AT_AddConstraint */
    4721                 :             :                         case AT_ReAddDomainConstraint:  /* becomes AT_AddConstraint */
    4722         [ -  + ]:         765 :                                 if (IsA(cmd->def, Constraint))
    4723                 :             :                                 {
    4724                 :         765 :                                         Constraint *con = (Constraint *) cmd->def;
    4725                 :             : 
    4726      [ +  +  + ]:         765 :                                         switch (con->contype)
    4727                 :             :                                         {
    4728                 :             :                                                 case CONSTR_EXCLUSION:
    4729                 :             :                                                 case CONSTR_PRIMARY:
    4730                 :             :                                                 case CONSTR_UNIQUE:
    4731                 :             : 
    4732                 :             :                                                         /*
    4733                 :             :                                                          * Cases essentially the same as CREATE INDEX. We
    4734                 :             :                                                          * could reduce the lock strength to ShareLock if
    4735                 :             :                                                          * we can work out how to allow concurrent catalog
    4736                 :             :                                                          * updates. XXX Might be set down to
    4737                 :             :                                                          * ShareRowExclusiveLock but requires further
    4738                 :             :                                                          * analysis.
    4739                 :             :                                                          */
    4740                 :         264 :                                                         cmd_lockmode = AccessExclusiveLock;
    4741                 :         264 :                                                         break;
    4742                 :             :                                                 case CONSTR_FOREIGN:
    4743                 :             : 
    4744                 :             :                                                         /*
    4745                 :             :                                                          * We add triggers to both tables when we add a
    4746                 :             :                                                          * Foreign Key, so the lock level must be at least
    4747                 :             :                                                          * as strong as CREATE TRIGGER.
    4748                 :             :                                                          */
    4749                 :         314 :                                                         cmd_lockmode = ShareRowExclusiveLock;
    4750                 :         314 :                                                         break;
    4751                 :             : 
    4752                 :             :                                                 default:
    4753                 :         187 :                                                         cmd_lockmode = AccessExclusiveLock;
    4754                 :         187 :                                         }
    4755                 :         765 :                                 }
    4756                 :         765 :                                 break;
    4757                 :             : 
    4758                 :             :                                 /*
    4759                 :             :                                  * These subcommands affect inheritance behaviour. Queries
    4760                 :             :                                  * started before us will continue to see the old inheritance
    4761                 :             :                                  * behaviour, while queries started after we commit will see
    4762                 :             :                                  * new behaviour. No need to prevent reads or writes to the
    4763                 :             :                                  * subtable while we hook it up though. Changing the TupDesc
    4764                 :             :                                  * may be a problem, so keep highest lock.
    4765                 :             :                                  */
    4766                 :             :                         case AT_AddInherit:
    4767                 :             :                         case AT_DropInherit:
    4768                 :          67 :                                 cmd_lockmode = AccessExclusiveLock;
    4769                 :          67 :                                 break;
    4770                 :             : 
    4771                 :             :                                 /*
    4772                 :             :                                  * These subcommands affect implicit row type conversion. They
    4773                 :             :                                  * have affects similar to CREATE/DROP CAST on queries. don't
    4774                 :             :                                  * provide for invalidating parse trees as a result of such
    4775                 :             :                                  * changes, so we keep these at AccessExclusiveLock.
    4776                 :             :                                  */
    4777                 :             :                         case AT_AddOf:
    4778                 :             :                         case AT_DropOf:
    4779                 :          10 :                                 cmd_lockmode = AccessExclusiveLock;
    4780                 :          10 :                                 break;
    4781                 :             : 
    4782                 :             :                                 /*
    4783                 :             :                                  * Only used by CREATE OR REPLACE VIEW which must conflict
    4784                 :             :                                  * with an SELECTs currently using the view.
    4785                 :             :                                  */
    4786                 :             :                         case AT_ReplaceRelOptions:
    4787                 :          31 :                                 cmd_lockmode = AccessExclusiveLock;
    4788                 :          31 :                                 break;
    4789                 :             : 
    4790                 :             :                                 /*
    4791                 :             :                                  * These subcommands affect general strategies for performance
    4792                 :             :                                  * and maintenance, though don't change the semantic results
    4793                 :             :                                  * from normal data reads and writes. Delaying an ALTER TABLE
    4794                 :             :                                  * behind currently active writes only delays the point where
    4795                 :             :                                  * the new strategy begins to take effect, so there is no
    4796                 :             :                                  * benefit in waiting. In this case the minimum restriction
    4797                 :             :                                  * applies: we don't currently allow concurrent catalog
    4798                 :             :                                  * updates.
    4799                 :             :                                  */
    4800                 :             :                         case AT_SetStatistics:  /* Uses MVCC in getTableAttrs() */
    4801                 :             :                         case AT_ClusterOn:      /* Uses MVCC in getIndexes() */
    4802                 :             :                         case AT_DropCluster:    /* Uses MVCC in getIndexes() */
    4803                 :             :                         case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
    4804                 :             :                         case AT_ResetOptions:   /* Uses MVCC in getTableAttrs() */
    4805                 :          36 :                                 cmd_lockmode = ShareUpdateExclusiveLock;
    4806                 :          36 :                                 break;
    4807                 :             : 
    4808                 :             :                         case AT_SetLogged:
    4809                 :             :                         case AT_SetUnLogged:
    4810                 :          18 :                                 cmd_lockmode = AccessExclusiveLock;
    4811                 :          18 :                                 break;
    4812                 :             : 
    4813                 :             :                         case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
    4814                 :          34 :                                 cmd_lockmode = ShareUpdateExclusiveLock;
    4815                 :          34 :                                 break;
    4816                 :             : 
    4817                 :             :                                 /*
    4818                 :             :                                  * Rel options are more complex than first appears. Options
    4819                 :             :                                  * are set here for tables, views and indexes; for historical
    4820                 :             :                                  * reasons these can all be used with ALTER TABLE, so we can't
    4821                 :             :                                  * decide between them using the basic grammar.
    4822                 :             :                                  */
    4823                 :             :                         case AT_SetRelOptions:  /* Uses MVCC in getIndexes() and
    4824                 :             :                                                                          * getTables() */
    4825                 :             :                         case AT_ResetRelOptions:        /* Uses MVCC in getIndexes() and
    4826                 :             :                                                                                  * getTables() */
    4827                 :          89 :                                 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
    4828                 :          89 :                                 break;
    4829                 :             : 
    4830                 :             :                         case AT_AttachPartition:
    4831                 :         359 :                                 cmd_lockmode = ShareUpdateExclusiveLock;
    4832                 :         359 :                                 break;
    4833                 :             : 
    4834                 :             :                         case AT_DetachPartition:
    4835         [ +  + ]:          76 :                                 if (((PartitionCmd *) cmd->def)->concurrent)
    4836                 :           5 :                                         cmd_lockmode = ShareUpdateExclusiveLock;
    4837                 :             :                                 else
    4838                 :          71 :                                         cmd_lockmode = AccessExclusiveLock;
    4839                 :          76 :                                 break;
    4840                 :             : 
    4841                 :             :                         case AT_DetachPartitionFinalize:
    4842                 :           1 :                                 cmd_lockmode = ShareUpdateExclusiveLock;
    4843                 :           1 :                                 break;
    4844                 :             : 
    4845                 :             :                         case AT_MergePartitions:
    4846                 :             :                         case AT_SplitPartition:
    4847                 :         102 :                                 cmd_lockmode = AccessExclusiveLock;
    4848                 :         102 :                                 break;
    4849                 :             : 
    4850                 :             :                         default:                        /* oops */
    4851   [ #  #  #  # ]:           0 :                                 elog(ERROR, "unrecognized alter table type: %d",
    4852                 :             :                                          (int) cmd->subtype);
    4853                 :           0 :                                 break;
    4854                 :             :                 }
    4855                 :             : 
    4856                 :             :                 /*
    4857                 :             :                  * Take the greatest lockmode from any subcommand
    4858                 :             :                  */
    4859         [ +  + ]:        3107 :                 if (cmd_lockmode > lockmode)
    4860                 :        2404 :                         lockmode = cmd_lockmode;
    4861                 :        3107 :         }
    4862                 :             : 
    4863                 :        5822 :         return lockmode;
    4864                 :        2911 : }
    4865                 :             : 
    4866                 :             : /*
    4867                 :             :  * ATController provides top level control over the phases.
    4868                 :             :  *
    4869                 :             :  * parsetree is passed in to allow it to be passed to event triggers
    4870                 :             :  * when requested.
    4871                 :             :  */
    4872                 :             : static void
    4873                 :        2803 : ATController(AlterTableStmt *parsetree,
    4874                 :             :                          Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
    4875                 :             :                          AlterTableUtilityContext *context)
    4876                 :             : {
    4877                 :        2803 :         List       *wqueue = NIL;
    4878                 :        2803 :         ListCell   *lcmd;
    4879                 :             : 
    4880                 :             :         /* Phase 1: preliminary examination of commands, create work queue */
    4881   [ +  -  +  +  :        5868 :         foreach(lcmd, cmds)
                   +  + ]
    4882                 :             :         {
    4883                 :        3065 :                 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4884                 :             : 
    4885                 :        3065 :                 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
    4886                 :        3065 :         }
    4887                 :             : 
    4888                 :             :         /* Close the relation, but keep lock until commit */
    4889                 :        2803 :         relation_close(rel, NoLock);
    4890                 :             : 
    4891                 :             :         /* Phase 2: update system catalogs */
    4892                 :        2803 :         ATRewriteCatalogs(&wqueue, lockmode, context);
    4893                 :             : 
    4894                 :             :         /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
    4895                 :        2803 :         ATRewriteTables(parsetree, &wqueue, lockmode, context);
    4896                 :        2803 : }
    4897                 :             : 
    4898                 :             : /*
    4899                 :             :  * ATPrepCmd
    4900                 :             :  *
    4901                 :             :  * Traffic cop for ALTER TABLE Phase 1 operations, including simple
    4902                 :             :  * recursion and permission checks.
    4903                 :             :  *
    4904                 :             :  * Caller must have acquired appropriate lock type on relation already.
    4905                 :             :  * This lock should be held until commit.
    4906                 :             :  */
    4907                 :             : static void
    4908                 :        3194 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
    4909                 :             :                   bool recurse, bool recursing, LOCKMODE lockmode,
    4910                 :             :                   AlterTableUtilityContext *context)
    4911                 :             : {
    4912                 :        3194 :         AlteredTableInfo *tab;
    4913                 :        3194 :         AlterTablePass pass = AT_PASS_UNSET;
    4914                 :             : 
    4915                 :             :         /* Find or create work queue entry for this table */
    4916                 :        3194 :         tab = ATGetQueueEntry(wqueue, rel);
    4917                 :             : 
    4918                 :             :         /*
    4919                 :             :          * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
    4920                 :             :          * partitions that are pending detach.
    4921                 :             :          */
    4922         [ +  + ]:        3194 :         if (rel->rd_rel->relispartition &&
    4923   [ +  -  +  - ]:         164 :                 cmd->subtype != AT_DetachPartitionFinalize &&
    4924                 :         164 :                 PartitionHasPendingDetach(RelationGetRelid(rel)))
    4925   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    4926                 :             :                                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    4927                 :             :                                 errmsg("cannot alter partition \"%s\" with an incomplete detach",
    4928                 :             :                                            RelationGetRelationName(rel)),
    4929                 :             :                                 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
    4930                 :             : 
    4931                 :             :         /*
    4932                 :             :          * Copy the original subcommand for each table, so we can scribble on it.
    4933                 :             :          * This avoids conflicts when different child tables need to make
    4934                 :             :          * different parse transformations (for example, the same column may have
    4935                 :             :          * different column numbers in different children).
    4936                 :             :          */
    4937                 :        3194 :         cmd = copyObject(cmd);
    4938                 :             : 
    4939                 :             :         /*
    4940                 :             :          * Do permissions and relkind checking, recursion to child tables if
    4941                 :             :          * needed, and any additional phase-1 processing needed.  (But beware of
    4942                 :             :          * adding any processing that looks at table details that another
    4943                 :             :          * subcommand could change.  In some cases we reject multiple subcommands
    4944                 :             :          * that could try to change the same state in contrary ways.)
    4945                 :             :          */
    4946   [ +  +  +  +  :        3194 :         switch (cmd->subtype)
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  -  +  
          -  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
                +  +  - ]
    4947                 :             :         {
    4948                 :             :                 case AT_AddColumn:              /* ADD COLUMN */
    4949                 :         303 :                         ATSimplePermissions(cmd->subtype, rel,
    4950                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    4951                 :             :                                                                 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    4952                 :         606 :                         ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
    4953                 :         303 :                                                         lockmode, context);
    4954                 :             :                         /* Recursion occurs during execution phase */
    4955                 :         303 :                         pass = AT_PASS_ADD_COL;
    4956                 :         303 :                         break;
    4957                 :             :                 case AT_AddColumnToView:        /* add column via CREATE OR REPLACE VIEW */
    4958                 :           4 :                         ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
    4959                 :           8 :                         ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
    4960                 :           4 :                                                         lockmode, context);
    4961                 :             :                         /* Recursion occurs during execution phase */
    4962                 :           4 :                         pass = AT_PASS_ADD_COL;
    4963                 :           4 :                         break;
    4964                 :             :                 case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    4965                 :             : 
    4966                 :             :                         /*
    4967                 :             :                          * We allow defaults on views so that INSERT into a view can have
    4968                 :             :                          * default-ish behavior.  This works because the rewriter
    4969                 :             :                          * substitutes default values into INSERTs before it expands
    4970                 :             :                          * rules.
    4971                 :             :                          */
    4972                 :          97 :                         ATSimplePermissions(cmd->subtype, rel,
    4973                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    4974                 :             :                                                                 ATT_FOREIGN_TABLE);
    4975                 :          97 :                         ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    4976                 :             :                         /* No command-specific prep needed */
    4977                 :          97 :                         pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
    4978                 :          97 :                         break;
    4979                 :             :                 case AT_CookedColumnDefault:    /* add a pre-cooked default */
    4980                 :             :                         /* This is currently used only in CREATE TABLE */
    4981                 :             :                         /* (so the permission check really isn't necessary) */
    4982                 :          13 :                         ATSimplePermissions(cmd->subtype, rel,
    4983                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    4984                 :             :                         /* This command never recurses */
    4985                 :          13 :                         pass = AT_PASS_ADD_OTHERCONSTR;
    4986                 :          13 :                         break;
    4987                 :             :                 case AT_AddIdentity:
    4988                 :          21 :                         ATSimplePermissions(cmd->subtype, rel,
    4989                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    4990                 :             :                                                                 ATT_FOREIGN_TABLE);
    4991                 :             :                         /* Set up recursion for phase 2; no other prep needed */
    4992         [ +  + ]:          21 :                         if (recurse)
    4993                 :          20 :                                 cmd->recurse = true;
    4994                 :          21 :                         pass = AT_PASS_ADD_OTHERCONSTR;
    4995                 :          21 :                         break;
    4996                 :             :                 case AT_SetIdentity:
    4997                 :          10 :                         ATSimplePermissions(cmd->subtype, rel,
    4998                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    4999                 :             :                                                                 ATT_FOREIGN_TABLE);
    5000                 :             :                         /* Set up recursion for phase 2; no other prep needed */
    5001         [ +  + ]:          10 :                         if (recurse)
    5002                 :           9 :                                 cmd->recurse = true;
    5003                 :             :                         /* This should run after AddIdentity, so do it in MISC pass */
    5004                 :          10 :                         pass = AT_PASS_MISC;
    5005                 :          10 :                         break;
    5006                 :             :                 case AT_DropIdentity:
    5007                 :           9 :                         ATSimplePermissions(cmd->subtype, rel,
    5008                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    5009                 :             :                                                                 ATT_FOREIGN_TABLE);
    5010                 :             :                         /* Set up recursion for phase 2; no other prep needed */
    5011         [ +  + ]:           9 :                         if (recurse)
    5012                 :           8 :                                 cmd->recurse = true;
    5013                 :           9 :                         pass = AT_PASS_DROP;
    5014                 :           9 :                         break;
    5015                 :             :                 case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    5016                 :          43 :                         ATSimplePermissions(cmd->subtype, rel,
    5017                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5018                 :             :                         /* Set up recursion for phase 2; no other prep needed */
    5019         [ +  + ]:          43 :                         if (recurse)
    5020                 :          40 :                                 cmd->recurse = true;
    5021                 :          43 :                         pass = AT_PASS_DROP;
    5022                 :          43 :                         break;
    5023                 :             :                 case AT_SetNotNull:             /* ALTER COLUMN SET NOT NULL */
    5024                 :          65 :                         ATSimplePermissions(cmd->subtype, rel,
    5025                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5026                 :             :                         /* Set up recursion for phase 2; no other prep needed */
    5027         [ +  + ]:          65 :                         if (recurse)
    5028                 :          60 :                                 cmd->recurse = true;
    5029                 :          65 :                         pass = AT_PASS_COL_ATTRS;
    5030                 :          65 :                         break;
    5031                 :             :                 case AT_SetExpression:  /* ALTER COLUMN SET EXPRESSION */
    5032                 :          37 :                         ATSimplePermissions(cmd->subtype, rel,
    5033                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5034                 :          37 :                         ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5035                 :          37 :                         pass = AT_PASS_SET_EXPRESSION;
    5036                 :          37 :                         break;
    5037                 :             :                 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
    5038                 :          14 :                         ATSimplePermissions(cmd->subtype, rel,
    5039                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5040                 :          14 :                         ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5041                 :          14 :                         ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
    5042                 :          14 :                         pass = AT_PASS_DROP;
    5043                 :          14 :                         break;
    5044                 :             :                 case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    5045                 :          25 :                         ATSimplePermissions(cmd->subtype, rel,
    5046                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
    5047                 :             :                                                                 ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
    5048                 :          25 :                         ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5049                 :             :                         /* No command-specific prep needed */
    5050                 :          25 :                         pass = AT_PASS_MISC;
    5051                 :          25 :                         break;
    5052                 :             :                 case AT_SetOptions:             /* ALTER COLUMN SET ( options ) */
    5053                 :             :                 case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    5054                 :           7 :                         ATSimplePermissions(cmd->subtype, rel,
    5055                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5056                 :             :                                                                 ATT_MATVIEW | ATT_FOREIGN_TABLE);
    5057                 :             :                         /* This command never recurses */
    5058                 :           7 :                         pass = AT_PASS_MISC;
    5059                 :           7 :                         break;
    5060                 :             :                 case AT_SetStorage:             /* ALTER COLUMN SET STORAGE */
    5061                 :          31 :                         ATSimplePermissions(cmd->subtype, rel,
    5062                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5063                 :             :                                                                 ATT_MATVIEW | ATT_FOREIGN_TABLE);
    5064                 :          31 :                         ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5065                 :             :                         /* No command-specific prep needed */
    5066                 :          31 :                         pass = AT_PASS_MISC;
    5067                 :          31 :                         break;
    5068                 :             :                 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    5069                 :           8 :                         ATSimplePermissions(cmd->subtype, rel,
    5070                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5071                 :             :                         /* This command never recurses */
    5072                 :             :                         /* No command-specific prep needed */
    5073                 :           8 :                         pass = AT_PASS_MISC;
    5074                 :           8 :                         break;
    5075                 :             :                 case AT_DropColumn:             /* DROP COLUMN */
    5076                 :         238 :                         ATSimplePermissions(cmd->subtype, rel,
    5077                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5078                 :             :                                                                 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    5079                 :         476 :                         ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
    5080                 :         238 :                                                          lockmode, context);
    5081                 :             :                         /* Recursion occurs during execution phase */
    5082                 :         238 :                         pass = AT_PASS_DROP;
    5083                 :         238 :                         break;
    5084                 :             :                 case AT_AddIndex:               /* ADD INDEX */
    5085                 :           0 :                         ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
    5086                 :             :                         /* This command never recurses */
    5087                 :             :                         /* No command-specific prep needed */
    5088                 :           0 :                         pass = AT_PASS_ADD_INDEX;
    5089                 :           0 :                         break;
    5090                 :             :                 case AT_AddConstraint:  /* ADD CONSTRAINT */
    5091                 :         825 :                         ATSimplePermissions(cmd->subtype, rel,
    5092                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5093                 :         825 :                         ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
    5094         [ +  + ]:         825 :                         if (recurse)
    5095                 :             :                         {
    5096                 :             :                                 /* recurses at exec time; lock descendants and set flag */
    5097                 :         816 :                                 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
    5098                 :         816 :                                 cmd->recurse = true;
    5099                 :         816 :                         }
    5100                 :         825 :                         pass = AT_PASS_ADD_CONSTR;
    5101                 :         825 :                         break;
    5102                 :             :                 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    5103                 :           0 :                         ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
    5104                 :             :                         /* This command never recurses */
    5105                 :             :                         /* No command-specific prep needed */
    5106                 :           0 :                         pass = AT_PASS_ADD_INDEXCONSTR;
    5107                 :           0 :                         break;
    5108                 :             :                 case AT_DropConstraint: /* DROP CONSTRAINT */
    5109                 :         131 :                         ATSimplePermissions(cmd->subtype, rel,
    5110                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5111                 :         131 :                         ATCheckPartitionsNotInUse(rel, lockmode);
    5112                 :             :                         /* Other recursion occurs during execution phase */
    5113                 :             :                         /* No command-specific prep needed except saving recurse flag */
    5114         [ +  + ]:         131 :                         if (recurse)
    5115                 :         125 :                                 cmd->recurse = true;
    5116                 :         131 :                         pass = AT_PASS_DROP;
    5117                 :         131 :                         break;
    5118                 :             :                 case AT_AlterColumnType:        /* ALTER COLUMN TYPE */
    5119                 :         223 :                         ATSimplePermissions(cmd->subtype, rel,
    5120                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5121                 :             :                                                                 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    5122                 :             :                         /* See comments for ATPrepAlterColumnType */
    5123                 :         446 :                         cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
    5124                 :         223 :                                                                           AT_PASS_UNSET, context);
    5125         [ +  - ]:         223 :                         Assert(cmd != NULL);
    5126                 :             :                         /* Performs own recursion */
    5127                 :         446 :                         ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
    5128                 :         223 :                                                                   lockmode, context);
    5129                 :         223 :                         pass = AT_PASS_ALTER_TYPE;
    5130                 :         223 :                         break;
    5131                 :             :                 case AT_AlterColumnGenericOptions:
    5132                 :           7 :                         ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    5133                 :             :                         /* This command never recurses */
    5134                 :             :                         /* No command-specific prep needed */
    5135                 :           7 :                         pass = AT_PASS_MISC;
    5136                 :           7 :                         break;
    5137                 :             :                 case AT_ChangeOwner:    /* ALTER OWNER */
    5138                 :             :                         /* This command never recurses */
    5139                 :             :                         /* No command-specific prep needed */
    5140                 :          42 :                         pass = AT_PASS_MISC;
    5141                 :          42 :                         break;
    5142                 :             :                 case AT_ClusterOn:              /* CLUSTER ON */
    5143                 :             :                 case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    5144                 :          10 :                         ATSimplePermissions(cmd->subtype, rel,
    5145                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5146                 :             :                         /* These commands never recurse */
    5147                 :             :                         /* No command-specific prep needed */
    5148                 :          10 :                         pass = AT_PASS_MISC;
    5149                 :          10 :                         break;
    5150                 :             :                 case AT_SetLogged:              /* SET LOGGED */
    5151                 :             :                 case AT_SetUnLogged:    /* SET UNLOGGED */
    5152                 :          16 :                         ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
    5153         [ +  - ]:          16 :                         if (tab->chgPersistence)
    5154   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    5155                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5156                 :             :                                                  errmsg("cannot change persistence setting twice")));
    5157                 :          16 :                         ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
    5158                 :          16 :                         pass = AT_PASS_MISC;
    5159                 :          16 :                         break;
    5160                 :             :                 case AT_DropOids:               /* SET WITHOUT OIDS */
    5161                 :           1 :                         ATSimplePermissions(cmd->subtype, rel,
    5162                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5163                 :           1 :                         pass = AT_PASS_DROP;
    5164                 :           1 :                         break;
    5165                 :             :                 case AT_SetAccessMethod:        /* SET ACCESS METHOD */
    5166                 :          21 :                         ATSimplePermissions(cmd->subtype, rel,
    5167                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5168                 :             : 
    5169                 :             :                         /* check if another access method change was already requested */
    5170         [ +  + ]:          21 :                         if (tab->chgAccessMethod)
    5171   [ +  -  +  - ]:           3 :                                 ereport(ERROR,
    5172                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5173                 :             :                                                  errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
    5174                 :             : 
    5175                 :          18 :                         ATPrepSetAccessMethod(tab, rel, cmd->name);
    5176                 :          18 :                         pass = AT_PASS_MISC;    /* does not matter; no work in Phase 2 */
    5177                 :          18 :                         break;
    5178                 :             :                 case AT_SetTableSpace:  /* SET TABLESPACE */
    5179                 :          25 :                         ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
    5180                 :             :                                                                 ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
    5181                 :             :                         /* This command never recurses */
    5182                 :          25 :                         ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
    5183                 :          25 :                         pass = AT_PASS_MISC;    /* doesn't actually matter */
    5184                 :          25 :                         break;
    5185                 :             :                 case AT_SetRelOptions:  /* SET (...) */
    5186                 :             :                 case AT_ResetRelOptions:        /* RESET (...) */
    5187                 :             :                 case AT_ReplaceRelOptions:      /* reset them all, then set just these */
    5188                 :         120 :                         ATSimplePermissions(cmd->subtype, rel,
    5189                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    5190                 :             :                                                                 ATT_MATVIEW | ATT_INDEX);
    5191                 :             :                         /* This command never recurses */
    5192                 :             :                         /* No command-specific prep needed */
    5193                 :         120 :                         pass = AT_PASS_MISC;
    5194                 :         120 :                         break;
    5195                 :             :                 case AT_AddInherit:             /* INHERIT */
    5196                 :          53 :                         ATSimplePermissions(cmd->subtype, rel,
    5197                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5198                 :             :                         /* This command never recurses */
    5199                 :          53 :                         ATPrepAddInherit(rel);
    5200                 :          53 :                         pass = AT_PASS_MISC;
    5201                 :          53 :                         break;
    5202                 :             :                 case AT_DropInherit:    /* NO INHERIT */
    5203                 :          14 :                         ATSimplePermissions(cmd->subtype, rel,
    5204                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5205                 :             :                         /* This command never recurses */
    5206                 :             :                         /* No command-specific prep needed */
    5207                 :          14 :                         pass = AT_PASS_MISC;
    5208                 :          14 :                         break;
    5209                 :             :                 case AT_AlterConstraint:        /* ALTER CONSTRAINT */
    5210                 :          49 :                         ATSimplePermissions(cmd->subtype, rel,
    5211                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE);
    5212                 :             :                         /* Recursion occurs during execution phase */
    5213         [ -  + ]:          49 :                         if (recurse)
    5214                 :          49 :                                 cmd->recurse = true;
    5215                 :          49 :                         pass = AT_PASS_MISC;
    5216                 :          49 :                         break;
    5217                 :             :                 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5218                 :          34 :                         ATSimplePermissions(cmd->subtype, rel,
    5219                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5220                 :             :                         /* Recursion occurs during execution phase */
    5221                 :             :                         /* No command-specific prep needed except saving recurse flag */
    5222         [ -  + ]:          34 :                         if (recurse)
    5223                 :          34 :                                 cmd->recurse = true;
    5224                 :          34 :                         pass = AT_PASS_MISC;
    5225                 :          34 :                         break;
    5226                 :             :                 case AT_ReplicaIdentity:        /* REPLICA IDENTITY ... */
    5227                 :          54 :                         ATSimplePermissions(cmd->subtype, rel,
    5228                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5229                 :          54 :                         pass = AT_PASS_MISC;
    5230                 :             :                         /* This command never recurses */
    5231                 :             :                         /* No command-specific prep needed */
    5232                 :          54 :                         break;
    5233                 :             :                 case AT_EnableTrig:             /* ENABLE TRIGGER variants */
    5234                 :             :                 case AT_EnableAlwaysTrig:
    5235                 :             :                 case AT_EnableReplicaTrig:
    5236                 :             :                 case AT_EnableTrigAll:
    5237                 :             :                 case AT_EnableTrigUser:
    5238                 :             :                 case AT_DisableTrig:    /* DISABLE TRIGGER variants */
    5239                 :             :                 case AT_DisableTrigAll:
    5240                 :             :                 case AT_DisableTrigUser:
    5241                 :          20 :                         ATSimplePermissions(cmd->subtype, rel,
    5242                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5243                 :             :                         /* Set up recursion for phase 2; no other prep needed */
    5244         [ +  + ]:          20 :                         if (recurse)
    5245                 :          17 :                                 cmd->recurse = true;
    5246                 :          20 :                         pass = AT_PASS_MISC;
    5247                 :          20 :                         break;
    5248                 :             :                 case AT_EnableRule:             /* ENABLE/DISABLE RULE variants */
    5249                 :             :                 case AT_EnableAlwaysRule:
    5250                 :             :                 case AT_EnableReplicaRule:
    5251                 :             :                 case AT_DisableRule:
    5252                 :             :                 case AT_AddOf:                  /* OF */
    5253                 :             :                 case AT_DropOf:                 /* NOT OF */
    5254                 :             :                 case AT_EnableRowSecurity:
    5255                 :             :                 case AT_DisableRowSecurity:
    5256                 :             :                 case AT_ForceRowSecurity:
    5257                 :             :                 case AT_NoForceRowSecurity:
    5258                 :          89 :                         ATSimplePermissions(cmd->subtype, rel,
    5259                 :             :                                                                 ATT_TABLE | ATT_PARTITIONED_TABLE);
    5260                 :             :                         /* These commands never recurse */
    5261                 :             :                         /* No command-specific prep needed */
    5262                 :          89 :                         pass = AT_PASS_MISC;
    5263                 :          89 :                         break;
    5264                 :             :                 case AT_GenericOptions:
    5265                 :           1 :                         ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    5266                 :             :                         /* No command-specific prep needed */
    5267                 :           1 :                         pass = AT_PASS_MISC;
    5268                 :           1 :                         break;
    5269                 :             :                 case AT_AttachPartition:
    5270                 :         357 :                         ATSimplePermissions(cmd->subtype, rel,
    5271                 :             :                                                                 ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
    5272                 :             :                         /* No command-specific prep needed */
    5273                 :         357 :                         pass = AT_PASS_MISC;
    5274                 :         357 :                         break;
    5275                 :             :                 case AT_DetachPartition:
    5276                 :          76 :                         ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
    5277                 :             :                         /* No command-specific prep needed */
    5278                 :          76 :                         pass = AT_PASS_MISC;
    5279                 :          76 :                         break;
    5280                 :             :                 case AT_DetachPartitionFinalize:
    5281                 :           1 :                         ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
    5282                 :             :                         /* No command-specific prep needed */
    5283                 :           1 :                         pass = AT_PASS_MISC;
    5284                 :           1 :                         break;
    5285                 :             :                 case AT_MergePartitions:
    5286                 :             :                 case AT_SplitPartition:
    5287                 :         100 :                         ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
    5288                 :             :                         /* No command-specific prep needed */
    5289                 :         100 :                         pass = AT_PASS_MISC;
    5290                 :         100 :                         break;
    5291                 :             :                 default:                                /* oops */
    5292   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized alter table type: %d",
    5293                 :             :                                  (int) cmd->subtype);
    5294                 :           0 :                         pass = AT_PASS_UNSET;   /* keep compiler quiet */
    5295                 :           0 :                         break;
    5296                 :             :         }
    5297         [ +  - ]:        3191 :         Assert(pass > AT_PASS_UNSET);
    5298                 :             : 
    5299                 :             :         /* Add the subcommand to the appropriate list for phase 2 */
    5300                 :        3191 :         tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
    5301                 :        3191 : }
    5302                 :             : 
    5303                 :             : /*
    5304                 :             :  * ATRewriteCatalogs
    5305                 :             :  *
    5306                 :             :  * Traffic cop for ALTER TABLE Phase 2 operations.  Subcommands are
    5307                 :             :  * dispatched in a "safe" execution order (designed to avoid unnecessary
    5308                 :             :  * conflicts).
    5309                 :             :  */
    5310                 :             : static void
    5311                 :        2358 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
    5312                 :             :                                   AlterTableUtilityContext *context)
    5313                 :             : {
    5314                 :        2358 :         ListCell   *ltab;
    5315                 :             : 
    5316                 :             :         /*
    5317                 :             :          * We process all the tables "in parallel", one pass at a time.  This is
    5318                 :             :          * needed because we may have to propagate work from one table to another
    5319                 :             :          * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
    5320                 :             :          * re-adding of the foreign key constraint to the other table).  Work can
    5321                 :             :          * only be propagated into later passes, however.
    5322                 :             :          */
    5323         [ +  + ]:       33856 :         for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
    5324                 :             :         {
    5325                 :             :                 /* Go through each table that needs to be processed */
    5326   [ +  -  +  +  :       66045 :                 foreach(ltab, *wqueue)
                   +  + ]
    5327                 :             :                 {
    5328                 :       34547 :                         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5329                 :       34547 :                         List       *subcmds = tab->subcmds[pass];
    5330                 :       34547 :                         ListCell   *lcmd;
    5331                 :             : 
    5332         [ +  + ]:       34547 :                         if (subcmds == NIL)
    5333                 :       30600 :                                 continue;
    5334                 :             : 
    5335                 :             :                         /*
    5336                 :             :                          * Open the relation and store it in tab.  This allows subroutines
    5337                 :             :                          * close and reopen, if necessary.  Appropriate lock was obtained
    5338                 :             :                          * by phase 1, needn't get it again.
    5339                 :             :                          */
    5340                 :        3947 :                         tab->rel = relation_open(tab->relid, NoLock);
    5341                 :             : 
    5342   [ +  -  +  +  :        7693 :                         foreach(lcmd, subcmds)
                   +  + ]
    5343                 :        7492 :                                 ATExecCmd(wqueue, tab,
    5344                 :        3746 :                                                   lfirst_node(AlterTableCmd, lcmd),
    5345                 :        3746 :                                                   lockmode, pass, context);
    5346                 :             : 
    5347                 :             :                         /*
    5348                 :             :                          * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
    5349                 :             :                          * (this is not done in ATExecAlterColumnType since it should be
    5350                 :             :                          * done only once if multiple columns of a table are altered).
    5351                 :             :                          */
    5352   [ +  +  +  + ]:        3947 :                         if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
    5353                 :        1157 :                                 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
    5354                 :             : 
    5355         [ +  + ]:        2993 :                         if (tab->rel)
    5356                 :             :                         {
    5357                 :        3438 :                                 relation_close(tab->rel, NoLock);
    5358                 :        3438 :                                 tab->rel = NULL;
    5359                 :        3438 :                         }
    5360      [ -  +  + ]:       34483 :                 }
    5361                 :       31498 :         }
    5362                 :             : 
    5363                 :             :         /* Check to see if a toast table must be added. */
    5364   [ +  -  +  +  :        5202 :         foreach(ltab, *wqueue)
                   +  + ]
    5365                 :             :         {
    5366                 :        2908 :                 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5367                 :             : 
    5368                 :             :                 /*
    5369                 :             :                  * If the table is source table of ATTACH PARTITION command, we did
    5370                 :             :                  * not modify anything about it that will change its toasting
    5371                 :             :                  * requirement, so no need to check.
    5372                 :             :                  */
    5373         [ +  + ]:        2908 :                 if (((tab->relkind == RELKIND_RELATION ||
    5374                 :         802 :                           tab->relkind == RELKIND_PARTITIONED_TABLE) &&
    5375         [ +  + ]:        2908 :                          tab->partition_constraint == NULL) ||
    5376                 :        2908 :                         tab->relkind == RELKIND_MATVIEW)
    5377                 :        2432 :                         AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
    5378                 :        2908 :         }
    5379                 :        2294 : }
    5380                 :             : 
    5381                 :             : /*
    5382                 :             :  * ATExecCmd: dispatch a subcommand to appropriate execution routine
    5383                 :             :  */
    5384                 :             : static void
    5385                 :        4255 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
    5386                 :             :                   AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
    5387                 :             :                   AlterTableUtilityContext *context)
    5388                 :             : {
    5389                 :        4255 :         ObjectAddress address = InvalidObjectAddress;
    5390                 :        4255 :         Relation        rel = tab->rel;
    5391                 :             : 
    5392   [ +  +  +  +  :        4255 :         switch (cmd->subtype)
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  -  
          +  -  +  -  +  
          +  -  +  +  +  
          +  +  +  +  +  
          +  +  +  +  -  
          +  +  +  +  +  
          +  +  +  +  +  
                      - ]
    5393                 :             :         {
    5394                 :             :                 case AT_AddColumn:              /* ADD COLUMN */
    5395                 :             :                 case AT_AddColumnToView:        /* add column via CREATE OR REPLACE VIEW */
    5396                 :         608 :                         address = ATExecAddColumn(wqueue, tab, rel, &cmd,
    5397                 :         304 :                                                                           cmd->recurse, false,
    5398                 :         304 :                                                                           lockmode, cur_pass, context);
    5399                 :         304 :                         break;
    5400                 :             :                 case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    5401                 :          91 :                         address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
    5402                 :          91 :                         break;
    5403                 :             :                 case AT_CookedColumnDefault:    /* add a pre-cooked default */
    5404                 :          13 :                         address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
    5405                 :          13 :                         break;
    5406                 :             :                 case AT_AddIdentity:
    5407                 :          38 :                         cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5408                 :          19 :                                                                           cur_pass, context);
    5409         [ +  - ]:          19 :                         Assert(cmd != NULL);
    5410                 :          19 :                         address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
    5411                 :          19 :                         break;
    5412                 :             :                 case AT_SetIdentity:
    5413                 :          20 :                         cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5414                 :          10 :                                                                           cur_pass, context);
    5415         [ +  - ]:          10 :                         Assert(cmd != NULL);
    5416                 :          10 :                         address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
    5417                 :          10 :                         break;
    5418                 :             :                 case AT_DropIdentity:
    5419                 :           9 :                         address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
    5420                 :           9 :                         break;
    5421                 :             :                 case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    5422                 :          43 :                         address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
    5423                 :          43 :                         break;
    5424                 :             :                 case AT_SetNotNull:             /* ALTER COLUMN SET NOT NULL */
    5425                 :         130 :                         address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
    5426                 :          65 :                                                                            cmd->recurse, false, lockmode);
    5427                 :          65 :                         break;
    5428                 :             :                 case AT_SetExpression:
    5429                 :          37 :                         address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
    5430                 :          37 :                         break;
    5431                 :             :                 case AT_DropExpression:
    5432                 :           9 :                         address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
    5433                 :           9 :                         break;
    5434                 :             :                 case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    5435                 :          25 :                         address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
    5436                 :          25 :                         break;
    5437                 :             :                 case AT_SetOptions:             /* ALTER COLUMN SET ( options ) */
    5438                 :           4 :                         address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
    5439                 :           4 :                         break;
    5440                 :             :                 case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    5441                 :           1 :                         address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
    5442                 :           1 :                         break;
    5443                 :             :                 case AT_SetStorage:             /* ALTER COLUMN SET STORAGE */
    5444                 :          31 :                         address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
    5445                 :          31 :                         break;
    5446                 :             :                 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    5447                 :          16 :                         address = ATExecSetCompression(rel, cmd->name, cmd->def,
    5448                 :           8 :                                                                                    lockmode);
    5449                 :           8 :                         break;
    5450                 :             :                 case AT_DropColumn:             /* DROP COLUMN */
    5451                 :         470 :                         address = ATExecDropColumn(wqueue, rel, cmd->name,
    5452                 :         235 :                                                                            cmd->behavior, cmd->recurse, false,
    5453                 :         235 :                                                                            cmd->missing_ok, lockmode,
    5454                 :             :                                                                            NULL);
    5455                 :         235 :                         break;
    5456                 :             :                 case AT_AddIndex:               /* ADD INDEX */
    5457                 :         254 :                         address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
    5458                 :         127 :                                                                          lockmode);
    5459                 :         127 :                         break;
    5460                 :             :                 case AT_ReAddIndex:             /* ADD INDEX */
    5461                 :         150 :                         address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
    5462                 :          75 :                                                                          lockmode);
    5463                 :          75 :                         break;
    5464                 :             :                 case AT_ReAddStatistics:        /* ADD STATISTICS */
    5465                 :          24 :                         address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
    5466                 :          12 :                                                                                   true, lockmode);
    5467                 :          12 :                         break;
    5468                 :             :                 case AT_AddConstraint:  /* ADD CONSTRAINT */
    5469                 :             :                         /* Transform the command only during initial examination */
    5470         [ +  + ]:        1546 :                         if (cur_pass == AT_PASS_ADD_CONSTR)
    5471                 :        1650 :                                 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
    5472                 :         825 :                                                                                   cmd->recurse, lockmode,
    5473                 :         825 :                                                                                   cur_pass, context);
    5474                 :             :                         /* Depending on constraint type, might be no more work to do now */
    5475         [ +  + ]:        1546 :                         if (cmd != NULL)
    5476                 :         721 :                                 address =
    5477                 :        1442 :                                         ATExecAddConstraint(wqueue, tab, rel,
    5478                 :         721 :                                                                                 (Constraint *) cmd->def,
    5479                 :         721 :                                                                                 cmd->recurse, false, lockmode);
    5480                 :        1546 :                         break;
    5481                 :             :                 case AT_ReAddConstraint:        /* Re-add pre-existing check constraint */
    5482                 :             :                         address =
    5483                 :         112 :                                 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
    5484                 :          56 :                                                                         true, true, lockmode);
    5485                 :          56 :                         break;
    5486                 :             :                 case AT_ReAddDomainConstraint:  /* Re-add pre-existing domain check
    5487                 :             :                                                                                  * constraint */
    5488                 :             :                         address =
    5489                 :           4 :                                 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
    5490                 :           2 :                                                                                  ((AlterDomainStmt *) cmd->def)->def,
    5491                 :             :                                                                                  NULL);
    5492                 :           2 :                         break;
    5493                 :             :                 case AT_ReAddComment:   /* Re-add existing comment */
    5494                 :          13 :                         address = CommentObject((CommentStmt *) cmd->def);
    5495                 :          13 :                         break;
    5496                 :             :                 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    5497                 :         246 :                         address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
    5498                 :         123 :                                                                                            lockmode);
    5499                 :         123 :                         break;
    5500                 :             :                 case AT_AlterConstraint:        /* ALTER CONSTRAINT */
    5501                 :          98 :                         address = ATExecAlterConstraint(wqueue, rel,
    5502                 :          49 :                                                                                         castNode(ATAlterConstraint, cmd->def),
    5503                 :          49 :                                                                                         cmd->recurse, lockmode);
    5504                 :          49 :                         break;
    5505                 :             :                 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5506                 :          68 :                         address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
    5507                 :          34 :                                                                                            false, lockmode);
    5508                 :          34 :                         break;
    5509                 :             :                 case AT_DropConstraint: /* DROP CONSTRAINT */
    5510                 :         262 :                         ATExecDropConstraint(rel, cmd->name, cmd->behavior,
    5511                 :         131 :                                                                  cmd->recurse,
    5512                 :         131 :                                                                  cmd->missing_ok, lockmode);
    5513                 :         131 :                         break;
    5514                 :             :                 case AT_AlterColumnType:        /* ALTER COLUMN TYPE */
    5515                 :             :                         /* parse transformation was done earlier */
    5516                 :         185 :                         address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
    5517                 :         185 :                         break;
    5518                 :             :                 case AT_AlterColumnGenericOptions:      /* ALTER COLUMN OPTIONS */
    5519                 :             :                         address =
    5520                 :          14 :                                 ATExecAlterColumnGenericOptions(rel, cmd->name,
    5521                 :           7 :                                                                                                 (List *) cmd->def, lockmode);
    5522                 :           7 :                         break;
    5523                 :             :                 case AT_ChangeOwner:    /* ALTER OWNER */
    5524                 :          84 :                         ATExecChangeOwner(RelationGetRelid(rel),
    5525                 :          42 :                                                           get_rolespec_oid(cmd->newowner, false),
    5526                 :          42 :                                                           false, lockmode);
    5527                 :          42 :                         break;
    5528                 :             :                 case AT_ClusterOn:              /* CLUSTER ON */
    5529                 :          10 :                         address = ATExecClusterOn(rel, cmd->name, lockmode);
    5530                 :          10 :                         break;
    5531                 :             :                 case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    5532                 :           3 :                         ATExecDropCluster(rel, lockmode);
    5533                 :           3 :                         break;
    5534                 :             :                 case AT_SetLogged:              /* SET LOGGED */
    5535                 :             :                 case AT_SetUnLogged:    /* SET UNLOGGED */
    5536                 :          14 :                         break;
    5537                 :             :                 case AT_DropOids:               /* SET WITHOUT OIDS */
    5538                 :             :                         /* nothing to do here, oid columns don't exist anymore */
    5539                 :             :                         break;
    5540                 :             :                 case AT_SetAccessMethod:        /* SET ACCESS METHOD */
    5541                 :             : 
    5542                 :             :                         /*
    5543                 :             :                          * Only do this for partitioned tables, for which this is just a
    5544                 :             :                          * catalog change.  Tables with storage are handled by Phase 3.
    5545                 :             :                          */
    5546   [ +  +  +  + ]:          15 :                         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
    5547                 :           8 :                                 tab->chgAccessMethod)
    5548                 :           7 :                                 ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
    5549                 :          15 :                         break;
    5550                 :             :                 case AT_SetTableSpace:  /* SET TABLESPACE */
    5551                 :             : 
    5552                 :             :                         /*
    5553                 :             :                          * Only do this for partitioned tables and indexes, for which this
    5554                 :             :                          * is just a catalog change.  Other relation types which have
    5555                 :             :                          * storage are handled by Phase 3.
    5556                 :             :                          */
    5557   [ +  +  +  + ]:          25 :                         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
    5558                 :          23 :                                 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    5559                 :           6 :                                 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
    5560                 :             : 
    5561                 :          25 :                         break;
    5562                 :             :                 case AT_SetRelOptions:  /* SET (...) */
    5563                 :             :                 case AT_ResetRelOptions:        /* RESET (...) */
    5564                 :             :                 case AT_ReplaceRelOptions:      /* replace entire option list */
    5565                 :         120 :                         ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
    5566                 :         120 :                         break;
    5567                 :             :                 case AT_EnableTrig:             /* ENABLE TRIGGER name */
    5568                 :           8 :                         ATExecEnableDisableTrigger(rel, cmd->name,
    5569                 :             :                                                                            TRIGGER_FIRES_ON_ORIGIN, false,
    5570                 :           4 :                                                                            cmd->recurse,
    5571                 :           4 :                                                                            lockmode);
    5572                 :           4 :                         break;
    5573                 :             :                 case AT_EnableAlwaysTrig:       /* ENABLE ALWAYS TRIGGER name */
    5574                 :          12 :                         ATExecEnableDisableTrigger(rel, cmd->name,
    5575                 :             :                                                                            TRIGGER_FIRES_ALWAYS, false,
    5576                 :           6 :                                                                            cmd->recurse,
    5577                 :           6 :                                                                            lockmode);
    5578                 :           6 :                         break;
    5579                 :             :                 case AT_EnableReplicaTrig:      /* ENABLE REPLICA TRIGGER name */
    5580                 :           0 :                         ATExecEnableDisableTrigger(rel, cmd->name,
    5581                 :             :                                                                            TRIGGER_FIRES_ON_REPLICA, false,
    5582                 :           0 :                                                                            cmd->recurse,
    5583                 :           0 :                                                                            lockmode);
    5584                 :           0 :                         break;
    5585                 :             :                 case AT_DisableTrig:    /* DISABLE TRIGGER name */
    5586                 :          12 :                         ATExecEnableDisableTrigger(rel, cmd->name,
    5587                 :             :                                                                            TRIGGER_DISABLED, false,
    5588                 :           6 :                                                                            cmd->recurse,
    5589                 :           6 :                                                                            lockmode);
    5590                 :           6 :                         break;
    5591                 :             :                 case AT_EnableTrigAll:  /* ENABLE TRIGGER ALL */
    5592                 :           0 :                         ATExecEnableDisableTrigger(rel, NULL,
    5593                 :             :                                                                            TRIGGER_FIRES_ON_ORIGIN, false,
    5594                 :           0 :                                                                            cmd->recurse,
    5595                 :           0 :                                                                            lockmode);
    5596                 :           0 :                         break;
    5597                 :             :                 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
    5598                 :           4 :                         ATExecEnableDisableTrigger(rel, NULL,
    5599                 :             :                                                                            TRIGGER_DISABLED, false,
    5600                 :           2 :                                                                            cmd->recurse,
    5601                 :           2 :                                                                            lockmode);
    5602                 :           2 :                         break;
    5603                 :             :                 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
    5604                 :           0 :                         ATExecEnableDisableTrigger(rel, NULL,
    5605                 :             :                                                                            TRIGGER_FIRES_ON_ORIGIN, true,
    5606                 :           0 :                                                                            cmd->recurse,
    5607                 :           0 :                                                                            lockmode);
    5608                 :           0 :                         break;
    5609                 :             :                 case AT_DisableTrigUser:        /* DISABLE TRIGGER USER */
    5610                 :           4 :                         ATExecEnableDisableTrigger(rel, NULL,
    5611                 :             :                                                                            TRIGGER_DISABLED, true,
    5612                 :           2 :                                                                            cmd->recurse,
    5613                 :           2 :                                                                            lockmode);
    5614                 :           2 :                         break;
    5615                 :             : 
    5616                 :             :                 case AT_EnableRule:             /* ENABLE RULE name */
    5617                 :           2 :                         ATExecEnableDisableRule(rel, cmd->name,
    5618                 :           1 :                                                                         RULE_FIRES_ON_ORIGIN, lockmode);
    5619                 :           1 :                         break;
    5620                 :             :                 case AT_EnableAlwaysRule:       /* ENABLE ALWAYS RULE name */
    5621                 :           0 :                         ATExecEnableDisableRule(rel, cmd->name,
    5622                 :           0 :                                                                         RULE_FIRES_ALWAYS, lockmode);
    5623                 :           0 :                         break;
    5624                 :             :                 case AT_EnableReplicaRule:      /* ENABLE REPLICA RULE name */
    5625                 :           2 :                         ATExecEnableDisableRule(rel, cmd->name,
    5626                 :           1 :                                                                         RULE_FIRES_ON_REPLICA, lockmode);
    5627                 :           1 :                         break;
    5628                 :             :                 case AT_DisableRule:    /* DISABLE RULE name */
    5629                 :           8 :                         ATExecEnableDisableRule(rel, cmd->name,
    5630                 :           4 :                                                                         RULE_DISABLED, lockmode);
    5631                 :           4 :                         break;
    5632                 :             : 
    5633                 :             :                 case AT_AddInherit:
    5634                 :          50 :                         address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
    5635                 :          50 :                         break;
    5636                 :             :                 case AT_DropInherit:
    5637                 :          14 :                         address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
    5638                 :          14 :                         break;
    5639                 :             :                 case AT_AddOf:
    5640                 :           9 :                         address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
    5641                 :           9 :                         break;
    5642                 :             :                 case AT_DropOf:
    5643                 :           1 :                         ATExecDropOf(rel, lockmode);
    5644                 :           1 :                         break;
    5645                 :             :                 case AT_ReplicaIdentity:
    5646                 :          57 :                         ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
    5647                 :          57 :                         break;
    5648                 :             :                 case AT_EnableRowSecurity:
    5649                 :          53 :                         ATExecSetRowSecurity(rel, true);
    5650                 :          53 :                         break;
    5651                 :             :                 case AT_DisableRowSecurity:
    5652                 :           1 :                         ATExecSetRowSecurity(rel, false);
    5653                 :           1 :                         break;
    5654                 :             :                 case AT_ForceRowSecurity:
    5655                 :          15 :                         ATExecForceNoForceRowSecurity(rel, true);
    5656                 :          15 :                         break;
    5657                 :             :                 case AT_NoForceRowSecurity:
    5658                 :           4 :                         ATExecForceNoForceRowSecurity(rel, false);
    5659                 :           4 :                         break;
    5660                 :             :                 case AT_GenericOptions:
    5661                 :           1 :                         ATExecGenericOptions(rel, (List *) cmd->def);
    5662                 :           1 :                         break;
    5663                 :             :                 case AT_AttachPartition:
    5664                 :         704 :                         cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5665                 :         352 :                                                                           cur_pass, context);
    5666         [ +  - ]:         352 :                         Assert(cmd != NULL);
    5667         [ +  + ]:         352 :                         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    5668                 :         614 :                                 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
    5669                 :         307 :                                                                                                 context);
    5670                 :             :                         else
    5671                 :          90 :                                 address = ATExecAttachPartitionIdx(wqueue, rel,
    5672                 :          45 :                                                                                                    ((PartitionCmd *) cmd->def)->name);
    5673                 :         352 :                         break;
    5674                 :             :                 case AT_DetachPartition:
    5675                 :         146 :                         cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5676                 :          73 :                                                                           cur_pass, context);
    5677         [ +  - ]:          73 :                         Assert(cmd != NULL);
    5678                 :             :                         /* ATPrepCmd ensures it must be a table */
    5679         [ +  - ]:          73 :                         Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5680                 :         146 :                         address = ATExecDetachPartition(wqueue, tab, rel,
    5681                 :          73 :                                                                                         ((PartitionCmd *) cmd->def)->name,
    5682                 :          73 :                                                                                         ((PartitionCmd *) cmd->def)->concurrent);
    5683                 :          73 :                         break;
    5684                 :             :                 case AT_DetachPartitionFinalize:
    5685                 :           0 :                         address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
    5686                 :           0 :                         break;
    5687                 :             :                 case AT_MergePartitions:
    5688                 :          52 :                         cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5689                 :          26 :                                                                           cur_pass, context);
    5690         [ +  - ]:          26 :                         Assert(cmd != NULL);
    5691         [ +  - ]:          26 :                         Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5692                 :          52 :                         ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
    5693                 :          26 :                                                                   context);
    5694                 :          26 :                         break;
    5695                 :             :                 case AT_SplitPartition:
    5696                 :          58 :                         cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5697                 :          29 :                                                                           cur_pass, context);
    5698         [ +  - ]:          29 :                         Assert(cmd != NULL);
    5699         [ +  - ]:          29 :                         Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5700                 :          58 :                         ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
    5701                 :          29 :                                                                  context);
    5702                 :          29 :                         break;
    5703                 :             :                 default:                                /* oops */
    5704   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized alter table type: %d",
    5705                 :             :                                  (int) cmd->subtype);
    5706                 :           0 :                         break;
    5707                 :             :         }
    5708                 :             : 
    5709                 :             :         /*
    5710                 :             :          * Report the subcommand to interested event triggers.
    5711                 :             :          */
    5712         [ +  + ]:        4255 :         if (cmd)
    5713                 :        2926 :                 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
    5714                 :             : 
    5715                 :             :         /*
    5716                 :             :          * Bump the command counter to ensure the next subcommand in the sequence
    5717                 :             :          * can see the changes so far
    5718                 :             :          */
    5719                 :        4255 :         CommandCounterIncrement();
    5720                 :        4255 : }
    5721                 :             : 
    5722                 :             : /*
    5723                 :             :  * ATParseTransformCmd: perform parse transformation for one subcommand
    5724                 :             :  *
    5725                 :             :  * Returns the transformed subcommand tree, if there is one, else NULL.
    5726                 :             :  *
    5727                 :             :  * The parser may hand back additional AlterTableCmd(s) and/or other
    5728                 :             :  * utility statements, either before or after the original subcommand.
    5729                 :             :  * Other AlterTableCmds are scheduled into the appropriate slot of the
    5730                 :             :  * AlteredTableInfo (they had better be for later passes than the current one).
    5731                 :             :  * Utility statements that are supposed to happen before the AlterTableCmd
    5732                 :             :  * are executed immediately.  Those that are supposed to happen afterwards
    5733                 :             :  * are added to the tab->afterStmts list to be done at the very end.
    5734                 :             :  */
    5735                 :             : static AlterTableCmd *
    5736                 :        1835 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
    5737                 :             :                                         AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    5738                 :             :                                         AlterTablePass cur_pass, AlterTableUtilityContext *context)
    5739                 :             : {
    5740                 :        1835 :         AlterTableCmd *newcmd = NULL;
    5741                 :        1835 :         AlterTableStmt *atstmt = makeNode(AlterTableStmt);
    5742                 :        1835 :         List       *beforeStmts;
    5743                 :        1835 :         List       *afterStmts;
    5744                 :        1835 :         ListCell   *lc;
    5745                 :             : 
    5746                 :             :         /* Gin up an AlterTableStmt with just this subcommand and this table */
    5747                 :        1835 :         atstmt->relation =
    5748                 :        3670 :                 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
    5749                 :        1835 :                                          pstrdup(RelationGetRelationName(rel)),
    5750                 :             :                                          -1);
    5751                 :        1835 :         atstmt->relation->inh = recurse;
    5752                 :        1835 :         atstmt->cmds = list_make1(cmd);
    5753                 :        1835 :         atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
    5754                 :        1835 :         atstmt->missing_ok = false;
    5755                 :             : 
    5756                 :             :         /* Transform the AlterTableStmt */
    5757                 :        3670 :         atstmt = transformAlterTableStmt(RelationGetRelid(rel),
    5758                 :        1835 :                                                                          atstmt,
    5759                 :        1835 :                                                                          context->queryString,
    5760                 :             :                                                                          &beforeStmts,
    5761                 :             :                                                                          &afterStmts);
    5762                 :             : 
    5763                 :             :         /* Execute any statements that should happen before these subcommand(s) */
    5764   [ +  +  +  +  :        1901 :         foreach(lc, beforeStmts)
                   +  + ]
    5765                 :             :         {
    5766                 :          66 :                 Node       *stmt = (Node *) lfirst(lc);
    5767                 :             : 
    5768                 :          66 :                 ProcessUtilityForAlterTable(stmt, context);
    5769                 :          66 :                 CommandCounterIncrement();
    5770                 :          66 :         }
    5771                 :             : 
    5772                 :             :         /* Examine the transformed subcommands and schedule them appropriately */
    5773   [ +  -  +  +  :        3827 :         foreach(lc, atstmt->cmds)
                   +  + ]
    5774                 :             :         {
    5775                 :        1992 :                 AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
    5776                 :        1992 :                 AlterTablePass pass;
    5777                 :             : 
    5778                 :             :                 /*
    5779                 :             :                  * This switch need only cover the subcommand types that can be added
    5780                 :             :                  * by parse_utilcmd.c; otherwise, we'll use the default strategy of
    5781                 :             :                  * executing the subcommand immediately, as a substitute for the
    5782                 :             :                  * original subcommand.  (Note, however, that this does cause
    5783                 :             :                  * AT_AddConstraint subcommands to be rescheduled into later passes,
    5784                 :             :                  * which is important for index and foreign key constraints.)
    5785                 :             :                  *
    5786                 :             :                  * We assume we needn't do any phase-1 checks for added subcommands.
    5787                 :             :                  */
    5788   [ +  +  +  +  :        1992 :                 switch (cmd2->subtype)
                      - ]
    5789                 :             :                 {
    5790                 :             :                         case AT_AddIndex:
    5791                 :         131 :                                 pass = AT_PASS_ADD_INDEX;
    5792                 :         131 :                                 break;
    5793                 :             :                         case AT_AddIndexConstraint:
    5794                 :         123 :                                 pass = AT_PASS_ADD_INDEXCONSTR;
    5795                 :         123 :                                 break;
    5796                 :             :                         case AT_AddConstraint:
    5797                 :             :                                 /* Recursion occurs during execution phase */
    5798         [ +  + ]:         723 :                                 if (recurse)
    5799                 :         720 :                                         cmd2->recurse = true;
    5800      [ -  +  + ]:         723 :                                 switch (castNode(Constraint, cmd2->def)->contype)
    5801                 :             :                                 {
    5802                 :             :                                         case CONSTR_NOTNULL:
    5803                 :         251 :                                                 pass = AT_PASS_COL_ATTRS;
    5804                 :         251 :                                                 break;
    5805                 :             :                                         case CONSTR_PRIMARY:
    5806                 :             :                                         case CONSTR_UNIQUE:
    5807                 :             :                                         case CONSTR_EXCLUSION:
    5808                 :           0 :                                                 pass = AT_PASS_ADD_INDEXCONSTR;
    5809                 :           0 :                                                 break;
    5810                 :             :                                         default:
    5811                 :         472 :                                                 pass = AT_PASS_ADD_OTHERCONSTR;
    5812                 :         472 :                                                 break;
    5813                 :             :                                 }
    5814                 :         723 :                                 break;
    5815                 :             :                         case AT_AlterColumnGenericOptions:
    5816                 :             :                                 /* This command never recurses */
    5817                 :             :                                 /* No command-specific prep needed */
    5818                 :           0 :                                 pass = AT_PASS_MISC;
    5819                 :           0 :                                 break;
    5820                 :             :                         default:
    5821                 :        1015 :                                 pass = cur_pass;
    5822                 :        1015 :                                 break;
    5823                 :             :                 }
    5824                 :             : 
    5825         [ +  - ]:        1992 :                 if (pass < cur_pass)
    5826                 :             :                 {
    5827                 :             :                         /* Cannot schedule into a pass we already finished */
    5828   [ #  #  #  # ]:           0 :                         elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
    5829                 :             :                                  pass);
    5830                 :           0 :                 }
    5831         [ +  + ]:        1992 :                 else if (pass > cur_pass)
    5832                 :             :                 {
    5833                 :             :                         /* OK, queue it up for later */
    5834                 :         977 :                         tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
    5835                 :         977 :                 }
    5836                 :             :                 else
    5837                 :             :                 {
    5838                 :             :                         /*
    5839                 :             :                          * We should see at most one subcommand for the current pass,
    5840                 :             :                          * which is the transformed version of the original subcommand.
    5841                 :             :                          */
    5842         [ +  - ]:        1015 :                         if (newcmd == NULL && cmd->subtype == cmd2->subtype)
    5843                 :             :                         {
    5844                 :             :                                 /* Found the transformed version of our subcommand */
    5845                 :        1015 :                                 newcmd = cmd2;
    5846                 :        1015 :                         }
    5847                 :             :                         else
    5848   [ #  #  #  # ]:           0 :                                 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
    5849                 :             :                                          pass);
    5850                 :             :                 }
    5851                 :        1992 :         }
    5852                 :             : 
    5853                 :             :         /* Queue up any after-statements to happen at the end */
    5854                 :        1835 :         tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
    5855                 :             : 
    5856                 :        3670 :         return newcmd;
    5857                 :        1835 : }
    5858                 :             : 
    5859                 :             : /*
    5860                 :             :  * ATRewriteTables: ALTER TABLE phase 3
    5861                 :             :  */
    5862                 :             : static void
    5863                 :        2354 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
    5864                 :             :                                 AlterTableUtilityContext *context)
    5865                 :             : {
    5866                 :        2354 :         ListCell   *ltab;
    5867                 :             : 
    5868                 :             :         /* Go through each table that needs to be checked or rewritten */
    5869   [ +  +  +  +  :        5197 :         foreach(ltab, *wqueue)
                   +  + ]
    5870                 :             :         {
    5871                 :        2843 :                 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5872                 :             : 
    5873                 :             :                 /* Relations without storage may be ignored here */
    5874   [ +  +  +  +  :        2843 :                 if (!RELKIND_HAS_STORAGE(tab->relkind))
          +  +  +  -  +  
                      + ]
    5875                 :         776 :                         continue;
    5876                 :             : 
    5877                 :             :                 /*
    5878                 :             :                  * If we change column data types, the operation has to be propagated
    5879                 :             :                  * to tables that use this table's rowtype as a column type.
    5880                 :             :                  * tab->newvals will also be non-NULL in the case where we're adding a
    5881                 :             :                  * column with a default.  We choose to forbid that case as well,
    5882                 :             :                  * since composite types might eventually support defaults.
    5883                 :             :                  *
    5884                 :             :                  * (Eventually we'll probably need to check for composite type
    5885                 :             :                  * dependencies even when we're just scanning the table without a
    5886                 :             :                  * rewrite, but at the moment a composite type does not enforce any
    5887                 :             :                  * constraints, so it's not necessary/appropriate to enforce them just
    5888                 :             :                  * during ALTER.)
    5889                 :             :                  */
    5890   [ +  +  +  + ]:        2067 :                 if (tab->newvals != NIL || tab->rewrite > 0)
    5891                 :             :                 {
    5892                 :         276 :                         Relation        rel;
    5893                 :             : 
    5894                 :         276 :                         rel = table_open(tab->relid, NoLock);
    5895                 :         276 :                         find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
    5896                 :         276 :                         table_close(rel, NoLock);
    5897                 :         276 :                 }
    5898                 :             : 
    5899                 :             :                 /*
    5900                 :             :                  * We only need to rewrite the table if at least one column needs to
    5901                 :             :                  * be recomputed, or we are changing its persistence or access method.
    5902                 :             :                  *
    5903                 :             :                  * There are two reasons for requiring a rewrite when changing
    5904                 :             :                  * persistence: on one hand, we need to ensure that the buffers
    5905                 :             :                  * belonging to each of the two relations are marked with or without
    5906                 :             :                  * BM_PERMANENT properly.  On the other hand, since rewriting creates
    5907                 :             :                  * and assigns a new relfilenumber, we automatically create or drop an
    5908                 :             :                  * init fork for the relation as appropriate.
    5909                 :             :                  */
    5910   [ +  +  +  + ]:        2067 :                 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
    5911                 :             :                 {
    5912                 :             :                         /* Build a temporary relation and copy data */
    5913                 :         172 :                         Relation        OldHeap;
    5914                 :         172 :                         Oid                     OIDNewHeap;
    5915                 :         172 :                         Oid                     NewAccessMethod;
    5916                 :         172 :                         Oid                     NewTableSpace;
    5917                 :         172 :                         char            persistence;
    5918                 :             : 
    5919                 :         172 :                         OldHeap = table_open(tab->relid, NoLock);
    5920                 :             : 
    5921                 :             :                         /*
    5922                 :             :                          * We don't support rewriting of system catalogs; there are too
    5923                 :             :                          * many corner cases and too little benefit.  In particular this
    5924                 :             :                          * is certainly not going to work for mapped catalogs.
    5925                 :             :                          */
    5926         [ +  - ]:         172 :                         if (IsSystemRelation(OldHeap))
    5927   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    5928                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5929                 :             :                                                  errmsg("cannot rewrite system relation \"%s\"",
    5930                 :             :                                                                 RelationGetRelationName(OldHeap))));
    5931                 :             : 
    5932   [ +  +  -  +  :         172 :                         if (RelationIsUsedAsCatalogTable(OldHeap))
                   +  - ]
    5933   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    5934                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5935                 :             :                                                  errmsg("cannot rewrite table \"%s\" used as a catalog table",
    5936                 :             :                                                                 RelationGetRelationName(OldHeap))));
    5937                 :             : 
    5938                 :             :                         /*
    5939                 :             :                          * Don't allow rewrite on temp tables of other backends ... their
    5940                 :             :                          * local buffer manager is not going to cope.  (This is redundant
    5941                 :             :                          * with the check in CheckAlterTableIsSafe, but for safety we'll
    5942                 :             :                          * check here too.)
    5943                 :             :                          */
    5944   [ +  +  +  - ]:         172 :                         if (RELATION_IS_OTHER_TEMP(OldHeap))
    5945   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    5946                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5947                 :             :                                                  errmsg("cannot rewrite temporary tables of other sessions")));
    5948                 :             : 
    5949                 :             :                         /*
    5950                 :             :                          * Select destination tablespace (same as original unless user
    5951                 :             :                          * requested a change)
    5952                 :             :                          */
    5953         [ -  + ]:         172 :                         if (tab->newTableSpace)
    5954                 :           0 :                                 NewTableSpace = tab->newTableSpace;
    5955                 :             :                         else
    5956                 :         172 :                                 NewTableSpace = OldHeap->rd_rel->reltablespace;
    5957                 :             : 
    5958                 :             :                         /*
    5959                 :             :                          * Select destination access method (same as original unless user
    5960                 :             :                          * requested a change)
    5961                 :             :                          */
    5962         [ +  + ]:         172 :                         if (tab->chgAccessMethod)
    5963                 :           6 :                                 NewAccessMethod = tab->newAccessMethod;
    5964                 :             :                         else
    5965                 :         166 :                                 NewAccessMethod = OldHeap->rd_rel->relam;
    5966                 :             : 
    5967                 :             :                         /*
    5968                 :             :                          * Select persistence of transient table (same as original unless
    5969                 :             :                          * user requested a change)
    5970                 :             :                          */
    5971         [ +  + ]:         172 :                         persistence = tab->chgPersistence ?
    5972                 :         172 :                                 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
    5973                 :             : 
    5974                 :         172 :                         table_close(OldHeap, NoLock);
    5975                 :             : 
    5976                 :             :                         /*
    5977                 :             :                          * Fire off an Event Trigger now, before actually rewriting the
    5978                 :             :                          * table.
    5979                 :             :                          *
    5980                 :             :                          * We don't support Event Trigger for nested commands anywhere,
    5981                 :             :                          * here included, and parsetree is given NULL when coming from
    5982                 :             :                          * AlterTableInternal.
    5983                 :             :                          *
    5984                 :             :                          * And fire it only once.
    5985                 :             :                          */
    5986         [ +  - ]:         172 :                         if (parsetree)
    5987                 :         344 :                                 EventTriggerTableRewrite((Node *) parsetree,
    5988                 :         172 :                                                                                  tab->relid,
    5989                 :         172 :                                                                                  tab->rewrite);
    5990                 :             : 
    5991                 :             :                         /*
    5992                 :             :                          * Create transient table that will receive the modified data.
    5993                 :             :                          *
    5994                 :             :                          * Ensure it is marked correctly as logged or unlogged.  We have
    5995                 :             :                          * to do this here so that buffers for the new relfilenumber will
    5996                 :             :                          * have the right persistence set, and at the same time ensure
    5997                 :             :                          * that the original filenumbers's buffers will get read in with
    5998                 :             :                          * the correct setting (i.e. the original one).  Otherwise a
    5999                 :             :                          * rollback after the rewrite would possibly result with buffers
    6000                 :             :                          * for the original filenumbers having the wrong persistence
    6001                 :             :                          * setting.
    6002                 :             :                          *
    6003                 :             :                          * NB: This relies on swap_relation_files() also swapping the
    6004                 :             :                          * persistence. That wouldn't work for pg_class, but that can't be
    6005                 :             :                          * unlogged anyway.
    6006                 :             :                          */
    6007                 :         344 :                         OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
    6008                 :         172 :                                                                            persistence, lockmode);
    6009                 :             : 
    6010                 :             :                         /*
    6011                 :             :                          * Copy the heap data into the new table with the desired
    6012                 :             :                          * modifications, and test the current data within the table
    6013                 :             :                          * against new constraints generated by ALTER TABLE commands.
    6014                 :             :                          */
    6015                 :         172 :                         ATRewriteTable(tab, OIDNewHeap);
    6016                 :             : 
    6017                 :             :                         /*
    6018                 :             :                          * Swap the physical files of the old and new heaps, then rebuild
    6019                 :             :                          * indexes and discard the old heap.  We can use RecentXmin for
    6020                 :             :                          * the table's new relfrozenxid because we rewrote all the tuples
    6021                 :             :                          * in ATRewriteTable, so no older Xid remains in the table.  Also,
    6022                 :             :                          * we never try to swap toast tables by content, since we have no
    6023                 :             :                          * interest in letting this code work on system catalogs.
    6024                 :             :                          */
    6025                 :         344 :                         finish_heap_swap(tab->relid, OIDNewHeap,
    6026                 :             :                                                          false, false, true,
    6027                 :         172 :                                                          !OidIsValid(tab->newTableSpace),
    6028                 :         172 :                                                          RecentXmin,
    6029                 :         172 :                                                          ReadNextMultiXactId(),
    6030                 :         172 :                                                          persistence);
    6031                 :             : 
    6032         [ -  + ]:         172 :                         InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
    6033                 :         172 :                 }
    6034   [ +  +  -  + ]:        1895 :                 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
    6035                 :             :                 {
    6036         [ -  + ]:           4 :                         if (tab->chgPersistence)
    6037                 :           4 :                                 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
    6038                 :           4 :                 }
    6039                 :             :                 else
    6040                 :             :                 {
    6041                 :             :                         /*
    6042                 :             :                          * If required, test the current data within the table against new
    6043                 :             :                          * constraints generated by ALTER TABLE commands, but don't
    6044                 :             :                          * rebuild data.
    6045                 :             :                          */
    6046   [ +  +  +  +  :        1891 :                         if (tab->constraints != NIL || tab->verify_new_notnull ||
                   +  + ]
    6047                 :        1529 :                                 tab->partition_constraint != NULL)
    6048                 :         656 :                                 ATRewriteTable(tab, InvalidOid);
    6049                 :             : 
    6050                 :             :                         /*
    6051                 :             :                          * If we had SET TABLESPACE but no reason to reconstruct tuples,
    6052                 :             :                          * just do a block-by-block copy.
    6053                 :             :                          */
    6054         [ +  + ]:        1891 :                         if (tab->newTableSpace)
    6055                 :          19 :                                 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
    6056                 :             :                 }
    6057                 :             : 
    6058                 :             :                 /*
    6059                 :             :                  * Also change persistence of owned sequences, so that it matches the
    6060                 :             :                  * table persistence.
    6061                 :             :                  */
    6062         [ +  + ]:        2067 :                 if (tab->chgPersistence)
    6063                 :             :                 {
    6064                 :          12 :                         List       *seqlist = getOwnedSequences(tab->relid);
    6065                 :          12 :                         ListCell   *lc;
    6066                 :             : 
    6067   [ +  +  +  +  :          20 :                         foreach(lc, seqlist)
                   +  + ]
    6068                 :             :                         {
    6069                 :           8 :                                 Oid                     seq_relid = lfirst_oid(lc);
    6070                 :             : 
    6071                 :           8 :                                 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
    6072                 :           8 :                         }
    6073                 :          12 :                 }
    6074         [ +  + ]:        2843 :         }
    6075                 :             : 
    6076                 :             :         /*
    6077                 :             :          * Foreign key constraints are checked in a final pass, since (a) it's
    6078                 :             :          * generally best to examine each one separately, and (b) it's at least
    6079                 :             :          * theoretically possible that we have changed both relations of the
    6080                 :             :          * foreign key, and we'd better have finished both rewrites before we try
    6081                 :             :          * to read the tables.
    6082                 :             :          */
    6083   [ +  -  +  +  :        5023 :         foreach(ltab, *wqueue)
                   +  + ]
    6084                 :             :         {
    6085                 :        2807 :                 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    6086                 :        2807 :                 Relation        rel = NULL;
    6087                 :        2807 :                 ListCell   *lcon;
    6088                 :             : 
    6089                 :             :                 /* Relations without storage may be ignored here too */
    6090   [ +  +  +  +  :        2807 :                 if (!RELKIND_HAS_STORAGE(tab->relkind))
          +  +  +  -  +  
                      + ]
    6091                 :         759 :                         continue;
    6092                 :             : 
    6093   [ +  +  +  +  :        2291 :                 foreach(lcon, tab->constraints)
                   +  + ]
    6094                 :             :                 {
    6095                 :         243 :                         NewConstraint *con = lfirst(lcon);
    6096                 :             : 
    6097         [ +  + ]:         243 :                         if (con->contype == CONSTR_FOREIGN)
    6098                 :             :                         {
    6099                 :         131 :                                 Constraint *fkconstraint = (Constraint *) con->qual;
    6100                 :         131 :                                 Relation        refrel;
    6101                 :             : 
    6102         [ +  + ]:         131 :                                 if (rel == NULL)
    6103                 :             :                                 {
    6104                 :             :                                         /* Long since locked, no need for another */
    6105                 :         129 :                                         rel = table_open(tab->relid, NoLock);
    6106                 :         129 :                                 }
    6107                 :             : 
    6108                 :         131 :                                 refrel = table_open(con->refrelid, RowShareLock);
    6109                 :             : 
    6110                 :         262 :                                 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
    6111                 :         131 :                                                                                          con->refindid,
    6112                 :         131 :                                                                                          con->conid,
    6113                 :         131 :                                                                                          con->conwithperiod);
    6114                 :             : 
    6115                 :             :                                 /*
    6116                 :             :                                  * No need to mark the constraint row as validated, we did
    6117                 :             :                                  * that when we inserted the row earlier.
    6118                 :             :                                  */
    6119                 :             : 
    6120                 :         131 :                                 table_close(refrel, NoLock);
    6121                 :         131 :                         }
    6122                 :         243 :                 }
    6123                 :             : 
    6124         [ +  + ]:        2048 :                 if (rel)
    6125                 :         114 :                         table_close(rel, NoLock);
    6126         [ +  + ]:        2807 :         }
    6127                 :             : 
    6128                 :             :         /* Finally, run any afterStmts that were queued up */
    6129   [ +  -  +  +  :        5003 :         foreach(ltab, *wqueue)
                   +  + ]
    6130                 :             :         {
    6131                 :        2787 :                 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    6132                 :        2787 :                 ListCell   *lc;
    6133                 :             : 
    6134   [ +  +  +  +  :        2800 :                 foreach(lc, tab->afterStmts)
                   +  + ]
    6135                 :             :                 {
    6136                 :          13 :                         Node       *stmt = (Node *) lfirst(lc);
    6137                 :             : 
    6138                 :          13 :                         ProcessUtilityForAlterTable(stmt, context);
    6139                 :          13 :                         CommandCounterIncrement();
    6140                 :          13 :                 }
    6141                 :        2787 :         }
    6142                 :        2216 : }
    6143                 :             : 
    6144                 :             : /*
    6145                 :             :  * ATRewriteTable: scan or rewrite one table
    6146                 :             :  *
    6147                 :             :  * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
    6148                 :             :  * must already hold AccessExclusiveLock on it.
    6149                 :             :  */
    6150                 :             : static void
    6151                 :         827 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
    6152                 :             : {
    6153                 :         827 :         Relation        oldrel;
    6154                 :         827 :         Relation        newrel;
    6155                 :         827 :         TupleDesc       oldTupDesc;
    6156                 :         827 :         TupleDesc       newTupDesc;
    6157                 :         827 :         bool            needscan = false;
    6158                 :         827 :         List       *notnull_attrs;
    6159                 :         827 :         List       *notnull_virtual_attrs;
    6160                 :         827 :         int                     i;
    6161                 :         827 :         ListCell   *l;
    6162                 :         827 :         EState     *estate;
    6163                 :         827 :         CommandId       mycid;
    6164                 :         827 :         BulkInsertState bistate;
    6165                 :         827 :         int                     ti_options;
    6166                 :         827 :         ExprState  *partqualstate = NULL;
    6167                 :             : 
    6168                 :             :         /*
    6169                 :             :          * Open the relation(s).  We have surely already locked the existing
    6170                 :             :          * table.
    6171                 :             :          */
    6172                 :         827 :         oldrel = table_open(tab->relid, NoLock);
    6173                 :         827 :         oldTupDesc = tab->oldDesc;
    6174                 :         827 :         newTupDesc = RelationGetDescr(oldrel);  /* includes all mods */
    6175                 :             : 
    6176         [ +  + ]:         827 :         if (OidIsValid(OIDNewHeap))
    6177                 :             :         {
    6178         [ +  - ]:         171 :                 Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
    6179                 :             :                                                                                   false));
    6180                 :         171 :                 newrel = table_open(OIDNewHeap, NoLock);
    6181                 :         171 :         }
    6182                 :             :         else
    6183                 :         656 :                 newrel = NULL;
    6184                 :             : 
    6185                 :             :         /*
    6186                 :             :          * Prepare a BulkInsertState and options for table_tuple_insert.  The FSM
    6187                 :             :          * is empty, so don't bother using it.
    6188                 :             :          */
    6189         [ +  + ]:         827 :         if (newrel)
    6190                 :             :         {
    6191                 :         171 :                 mycid = GetCurrentCommandId(true);
    6192                 :         171 :                 bistate = GetBulkInsertState();
    6193                 :         171 :                 ti_options = TABLE_INSERT_SKIP_FSM;
    6194                 :         171 :         }
    6195                 :             :         else
    6196                 :             :         {
    6197                 :             :                 /* keep compiler quiet about using these uninitialized */
    6198                 :         656 :                 mycid = 0;
    6199                 :         656 :                 bistate = NULL;
    6200                 :         656 :                 ti_options = 0;
    6201                 :             :         }
    6202                 :             : 
    6203                 :             :         /*
    6204                 :             :          * Generate the constraint and default execution states
    6205                 :             :          */
    6206                 :             : 
    6207                 :         827 :         estate = CreateExecutorState();
    6208                 :             : 
    6209                 :             :         /* Build the needed expression execution states */
    6210   [ +  +  +  +  :        1105 :         foreach(l, tab->constraints)
                   +  + ]
    6211                 :             :         {
    6212                 :         278 :                 NewConstraint *con = lfirst(l);
    6213                 :             : 
    6214      [ +  +  - ]:         278 :                 switch (con->contype)
    6215                 :             :                 {
    6216                 :             :                         case CONSTR_CHECK:
    6217                 :         146 :                                 needscan = true;
    6218                 :         146 :                                 con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
    6219                 :         146 :                                 break;
    6220                 :             :                         case CONSTR_FOREIGN:
    6221                 :             :                                 /* Nothing to do here */
    6222                 :             :                                 break;
    6223                 :             :                         default:
    6224   [ #  #  #  # ]:           0 :                                 elog(ERROR, "unrecognized constraint type: %d",
    6225                 :             :                                          (int) con->contype);
    6226                 :           0 :                 }
    6227                 :         278 :         }
    6228                 :             : 
    6229                 :             :         /* Build expression execution states for partition check quals */
    6230         [ +  + ]:         827 :         if (tab->partition_constraint)
    6231                 :             :         {
    6232                 :         269 :                 needscan = true;
    6233                 :         269 :                 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
    6234                 :         269 :         }
    6235                 :             : 
    6236   [ +  +  +  +  :        1009 :         foreach(l, tab->newvals)
                   +  + ]
    6237                 :             :         {
    6238                 :         182 :                 NewColumnValue *ex = lfirst(l);
    6239                 :             : 
    6240                 :             :                 /* expr already planned */
    6241                 :         182 :                 ex->exprstate = ExecInitExpr(ex->expr, NULL);
    6242                 :         182 :         }
    6243                 :             : 
    6244                 :         827 :         notnull_attrs = notnull_virtual_attrs = NIL;
    6245   [ +  +  +  + ]:         827 :         if (newrel || tab->verify_new_notnull)
    6246                 :             :         {
    6247                 :             :                 /*
    6248                 :             :                  * If we are rebuilding the tuples OR if we added any new but not
    6249                 :             :                  * verified not-null constraints, check all *valid* not-null
    6250                 :             :                  * constraints. This is a bit of overkill but it minimizes risk of
    6251                 :             :                  * bugs.
    6252                 :             :                  *
    6253                 :             :                  * notnull_attrs does *not* collect attribute numbers for valid
    6254                 :             :                  * not-null constraints over virtual generated columns; instead, they
    6255                 :             :                  * are collected in notnull_virtual_attrs for verification elsewhere.
    6256                 :             :                  */
    6257         [ +  + ]:        1211 :                 for (i = 0; i < newTupDesc->natts; i++)
    6258                 :             :                 {
    6259                 :         883 :                         CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
    6260                 :             : 
    6261   [ +  +  -  + ]:         883 :                         if (attr->attnullability == ATTNULLABLE_VALID &&
    6262                 :         328 :                                 !attr->attisdropped)
    6263                 :             :                         {
    6264                 :         328 :                                 Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
    6265                 :             : 
    6266         [ +  + ]:         328 :                                 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
    6267                 :         313 :                                         notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
    6268                 :             :                                 else
    6269                 :          30 :                                         notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
    6270                 :          15 :                                                                                                                 wholeatt->attnum);
    6271                 :         328 :                         }
    6272                 :         883 :                 }
    6273   [ +  +  +  + ]:         328 :                 if (notnull_attrs || notnull_virtual_attrs)
    6274                 :         240 :                         needscan = true;
    6275                 :         328 :         }
    6276                 :             : 
    6277   [ +  +  +  + ]:         827 :         if (newrel || needscan)
    6278                 :             :         {
    6279                 :         724 :                 ExprContext *econtext;
    6280                 :         724 :                 TupleTableSlot *oldslot;
    6281                 :         724 :                 TupleTableSlot *newslot;
    6282                 :         724 :                 TableScanDesc scan;
    6283                 :         724 :                 MemoryContext oldCxt;
    6284                 :         724 :                 List       *dropped_attrs = NIL;
    6285                 :         724 :                 ListCell   *lc;
    6286                 :         724 :                 Snapshot        snapshot;
    6287                 :         724 :                 ResultRelInfo *rInfo = NULL;
    6288                 :             : 
    6289                 :             :                 /*
    6290                 :             :                  * When adding or changing a virtual generated column with a not-null
    6291                 :             :                  * constraint, we need to evaluate whether the generation expression
    6292                 :             :                  * is null.  For that, we borrow ExecRelGenVirtualNotNull().  Here, we
    6293                 :             :                  * prepare a dummy ResultRelInfo.
    6294                 :             :                  */
    6295         [ +  + ]:         724 :                 if (notnull_virtual_attrs != NIL)
    6296                 :             :                 {
    6297                 :          10 :                         MemoryContext oldcontext;
    6298                 :             : 
    6299         [ +  - ]:          10 :                         Assert(newTupDesc->constr->has_generated_virtual);
    6300         [ +  - ]:          10 :                         Assert(newTupDesc->constr->has_not_null);
    6301                 :          10 :                         oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
    6302                 :          10 :                         rInfo = makeNode(ResultRelInfo);
    6303                 :          20 :                         InitResultRelInfo(rInfo,
    6304                 :          10 :                                                           oldrel,
    6305                 :             :                                                           0,    /* dummy rangetable index */
    6306                 :             :                                                           NULL,
    6307                 :          10 :                                                           estate->es_instrument);
    6308                 :          10 :                         MemoryContextSwitchTo(oldcontext);
    6309                 :          10 :                 }
    6310                 :             : 
    6311         [ +  + ]:         724 :                 if (newrel)
    6312   [ -  +  -  + ]:         171 :                         ereport(DEBUG1,
    6313                 :             :                                         (errmsg_internal("rewriting table \"%s\"",
    6314                 :             :                                                                          RelationGetRelationName(oldrel))));
    6315                 :             :                 else
    6316   [ -  +  -  + ]:         553 :                         ereport(DEBUG1,
    6317                 :             :                                         (errmsg_internal("verifying table \"%s\"",
    6318                 :             :                                                                          RelationGetRelationName(oldrel))));
    6319                 :             : 
    6320         [ +  + ]:         724 :                 if (newrel)
    6321                 :             :                 {
    6322                 :             :                         /*
    6323                 :             :                          * All predicate locks on the tuples or pages are about to be made
    6324                 :             :                          * invalid, because we move tuples around.  Promote them to
    6325                 :             :                          * relation locks.
    6326                 :             :                          */
    6327                 :         171 :                         TransferPredicateLocksToHeapRelation(oldrel);
    6328                 :         171 :                 }
    6329                 :             : 
    6330         [ -  + ]:         724 :                 econtext = GetPerTupleExprContext(estate);
    6331                 :             : 
    6332                 :             :                 /*
    6333                 :             :                  * Create necessary tuple slots. When rewriting, two slots are needed,
    6334                 :             :                  * otherwise one suffices. In the case where one slot suffices, we
    6335                 :             :                  * need to use the new tuple descriptor, otherwise some constraints
    6336                 :             :                  * can't be evaluated.  Note that even when the tuple layout is the
    6337                 :             :                  * same and no rewrite is required, the tupDescs might not be
    6338                 :             :                  * (consider ADD COLUMN without a default).
    6339                 :             :                  */
    6340         [ +  + ]:         724 :                 if (tab->rewrite)
    6341                 :             :                 {
    6342         [ +  - ]:         171 :                         Assert(newrel != NULL);
    6343                 :         342 :                         oldslot = MakeSingleTupleTableSlot(oldTupDesc,
    6344                 :         171 :                                                                                            table_slot_callbacks(oldrel));
    6345                 :         342 :                         newslot = MakeSingleTupleTableSlot(newTupDesc,
    6346                 :         171 :                                                                                            table_slot_callbacks(newrel));
    6347                 :             : 
    6348                 :             :                         /*
    6349                 :             :                          * Set all columns in the new slot to NULL initially, to ensure
    6350                 :             :                          * columns added as part of the rewrite are initialized to NULL.
    6351                 :             :                          * That is necessary as tab->newvals will not contain an
    6352                 :             :                          * expression for columns with a NULL default, e.g. when adding a
    6353                 :             :                          * column without a default together with a column with a default
    6354                 :             :                          * requiring an actual rewrite.
    6355                 :             :                          */
    6356                 :         171 :                         ExecStoreAllNullTuple(newslot);
    6357                 :         171 :                 }
    6358                 :             :                 else
    6359                 :             :                 {
    6360                 :        1106 :                         oldslot = MakeSingleTupleTableSlot(newTupDesc,
    6361                 :         553 :                                                                                            table_slot_callbacks(oldrel));
    6362                 :         553 :                         newslot = NULL;
    6363                 :             :                 }
    6364                 :             : 
    6365                 :             :                 /*
    6366                 :             :                  * Any attributes that are dropped according to the new tuple
    6367                 :             :                  * descriptor can be set to NULL. We precompute the list of dropped
    6368                 :             :                  * attributes to avoid needing to do so in the per-tuple loop.
    6369                 :             :                  */
    6370         [ +  + ]:        2572 :                 for (i = 0; i < newTupDesc->natts; i++)
    6371                 :             :                 {
    6372         [ +  + ]:        1848 :                         if (TupleDescAttr(newTupDesc, i)->attisdropped)
    6373                 :         119 :                                 dropped_attrs = lappend_int(dropped_attrs, i);
    6374                 :        1848 :                 }
    6375                 :             : 
    6376                 :             :                 /*
    6377                 :             :                  * Scan through the rows, generating a new row if needed and then
    6378                 :             :                  * checking all the constraints.
    6379                 :             :                  */
    6380                 :         724 :                 snapshot = RegisterSnapshot(GetLatestSnapshot());
    6381                 :         724 :                 scan = table_beginscan(oldrel, snapshot, 0, NULL);
    6382                 :             : 
    6383                 :             :                 /*
    6384                 :             :                  * Switch to per-tuple memory context and reset it for each tuple
    6385                 :             :                  * produced, so we don't leak memory.
    6386                 :             :                  */
    6387         [ +  - ]:         724 :                 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
    6388                 :             : 
    6389         [ +  + ]:      100931 :                 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
    6390                 :             :                 {
    6391                 :      100257 :                         TupleTableSlot *insertslot;
    6392                 :             : 
    6393         [ +  + ]:      100257 :                         if (tab->rewrite > 0)
    6394                 :             :                         {
    6395                 :             :                                 /* Extract data from old tuple */
    6396                 :       16340 :                                 slot_getallattrs(oldslot);
    6397                 :       16340 :                                 ExecClearTuple(newslot);
    6398                 :             : 
    6399                 :             :                                 /* copy attributes */
    6400                 :       16340 :                                 memcpy(newslot->tts_values, oldslot->tts_values,
    6401                 :             :                                            sizeof(Datum) * oldslot->tts_nvalid);
    6402                 :       16340 :                                 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
    6403                 :             :                                            sizeof(bool) * oldslot->tts_nvalid);
    6404                 :             : 
    6405                 :             :                                 /* Set dropped attributes to null in new tuple */
    6406   [ +  +  +  +  :       16357 :                                 foreach(lc, dropped_attrs)
                   +  + ]
    6407                 :          17 :                                         newslot->tts_isnull[lfirst_int(lc)] = true;
    6408                 :             : 
    6409                 :             :                                 /*
    6410                 :             :                                  * Constraints and GENERATED expressions might reference the
    6411                 :             :                                  * tableoid column, so fill tts_tableOid with the desired
    6412                 :             :                                  * value.  (We must do this each time, because it gets
    6413                 :             :                                  * overwritten with newrel's OID during storing.)
    6414                 :             :                                  */
    6415                 :       16340 :                                 newslot->tts_tableOid = RelationGetRelid(oldrel);
    6416                 :             : 
    6417                 :             :                                 /*
    6418                 :             :                                  * Process supplied expressions to replace selected columns.
    6419                 :             :                                  *
    6420                 :             :                                  * First, evaluate expressions whose inputs come from the old
    6421                 :             :                                  * tuple.
    6422                 :             :                                  */
    6423                 :       16340 :                                 econtext->ecxt_scantuple = oldslot;
    6424                 :             : 
    6425   [ +  +  +  +  :       33676 :                                 foreach(l, tab->newvals)
                   +  + ]
    6426                 :             :                                 {
    6427                 :       17336 :                                         NewColumnValue *ex = lfirst(l);
    6428                 :             : 
    6429         [ +  + ]:       17336 :                                         if (ex->is_generated)
    6430                 :          52 :                                                 continue;
    6431                 :             : 
    6432                 :       17284 :                                         newslot->tts_values[ex->attnum - 1]
    6433                 :       51852 :                                                 = ExecEvalExpr(ex->exprstate,
    6434                 :       17284 :                                                                            econtext,
    6435                 :       17284 :                                                                            &newslot->tts_isnull[ex->attnum - 1]);
    6436         [ +  + ]:       17336 :                                 }
    6437                 :             : 
    6438                 :       16340 :                                 ExecStoreVirtualTuple(newslot);
    6439                 :             : 
    6440                 :             :                                 /*
    6441                 :             :                                  * Now, evaluate any expressions whose inputs come from the
    6442                 :             :                                  * new tuple.  We assume these columns won't reference each
    6443                 :             :                                  * other, so that there's no ordering dependency.
    6444                 :             :                                  */
    6445                 :       16340 :                                 econtext->ecxt_scantuple = newslot;
    6446                 :             : 
    6447   [ +  +  +  +  :       33674 :                                 foreach(l, tab->newvals)
                   +  + ]
    6448                 :             :                                 {
    6449                 :       17334 :                                         NewColumnValue *ex = lfirst(l);
    6450                 :             : 
    6451         [ +  + ]:       17334 :                                         if (!ex->is_generated)
    6452                 :       17282 :                                                 continue;
    6453                 :             : 
    6454                 :          52 :                                         newslot->tts_values[ex->attnum - 1]
    6455                 :         156 :                                                 = ExecEvalExpr(ex->exprstate,
    6456                 :          52 :                                                                            econtext,
    6457                 :          52 :                                                                            &newslot->tts_isnull[ex->attnum - 1]);
    6458         [ +  + ]:       17334 :                                 }
    6459                 :             : 
    6460                 :       16340 :                                 insertslot = newslot;
    6461                 :       16340 :                         }
    6462                 :             :                         else
    6463                 :             :                         {
    6464                 :             :                                 /*
    6465                 :             :                                  * If there's no rewrite, old and new table are guaranteed to
    6466                 :             :                                  * have the same AM, so we can just use the old slot to verify
    6467                 :             :                                  * new constraints etc.
    6468                 :             :                                  */
    6469                 :       83917 :                                 insertslot = oldslot;
    6470                 :             :                         }
    6471                 :             : 
    6472                 :             :                         /* Now check any constraints on the possibly-changed tuple */
    6473                 :      100257 :                         econtext->ecxt_scantuple = insertslot;
    6474                 :             : 
    6475   [ +  +  +  +  :      523181 :                         foreach_int(attn, notnull_attrs)
             +  +  +  + ]
    6476                 :             :                         {
    6477         [ +  + ]:      322701 :                                 if (slot_attisnull(insertslot, attn))
    6478                 :             :                                 {
    6479                 :          17 :                                         Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
    6480                 :             : 
    6481   [ +  -  +  - ]:          17 :                                         ereport(ERROR,
    6482                 :             :                                                         (errcode(ERRCODE_NOT_NULL_VIOLATION),
    6483                 :             :                                                          errmsg("column \"%s\" of relation \"%s\" contains null values",
    6484                 :             :                                                                         NameStr(attr->attname),
    6485                 :             :                                                                         RelationGetRelationName(oldrel)),
    6486                 :             :                                                          errtablecol(oldrel, attn)));
    6487                 :           0 :                                 }
    6488                 :      422924 :                         }
    6489                 :             : 
    6490         [ +  + ]:      100240 :                         if (notnull_virtual_attrs != NIL)
    6491                 :             :                         {
    6492                 :          14 :                                 AttrNumber      attnum;
    6493                 :             : 
    6494                 :          28 :                                 attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
    6495                 :          14 :                                                                                                   estate,
    6496                 :          14 :                                                                                                   notnull_virtual_attrs);
    6497         [ +  + ]:          14 :                                 if (attnum != InvalidAttrNumber)
    6498                 :             :                                 {
    6499                 :           5 :                                         Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
    6500                 :             : 
    6501   [ +  -  +  - ]:           5 :                                         ereport(ERROR,
    6502                 :             :                                                         errcode(ERRCODE_NOT_NULL_VIOLATION),
    6503                 :             :                                                         errmsg("column \"%s\" of relation \"%s\" contains null values",
    6504                 :             :                                                                    NameStr(attr->attname),
    6505                 :             :                                                                    RelationGetRelationName(oldrel)),
    6506                 :             :                                                         errtablecol(oldrel, attnum));
    6507                 :           0 :                                 }
    6508                 :           9 :                         }
    6509                 :             : 
    6510   [ +  +  +  +  :      101316 :                         foreach(l, tab->constraints)
                   +  + ]
    6511                 :             :                         {
    6512                 :        1097 :                                 NewConstraint *con = lfirst(l);
    6513                 :             : 
    6514      [ -  +  + ]:        1097 :                                 switch (con->contype)
    6515                 :             :                                 {
    6516                 :             :                                         case CONSTR_CHECK:
    6517         [ +  + ]:        1084 :                                                 if (!ExecCheck(con->qualstate, econtext))
    6518   [ +  -  +  - ]:          16 :                                                         ereport(ERROR,
    6519                 :             :                                                                         (errcode(ERRCODE_CHECK_VIOLATION),
    6520                 :             :                                                                          errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
    6521                 :             :                                                                                         con->name,
    6522                 :             :                                                                                         RelationGetRelationName(oldrel)),
    6523                 :             :                                                                          errtableconstraint(oldrel, con->name)));
    6524                 :        1068 :                                                 break;
    6525                 :             :                                         case CONSTR_NOTNULL:
    6526                 :             :                                         case CONSTR_FOREIGN:
    6527                 :             :                                                 /* Nothing to do here */
    6528                 :          13 :                                                 break;
    6529                 :             :                                         default:
    6530   [ #  #  #  # ]:           0 :                                                 elog(ERROR, "unrecognized constraint type: %d",
    6531                 :             :                                                          (int) con->contype);
    6532                 :           0 :                                 }
    6533                 :        1081 :                         }
    6534                 :             : 
    6535   [ +  +  +  + ]:      100219 :                         if (partqualstate && !ExecCheck(partqualstate, econtext))
    6536                 :             :                         {
    6537         [ +  + ]:          12 :                                 if (tab->validate_default)
    6538   [ +  -  +  - ]:           4 :                                         ereport(ERROR,
    6539                 :             :                                                         (errcode(ERRCODE_CHECK_VIOLATION),
    6540                 :             :                                                          errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
    6541                 :             :                                                                         RelationGetRelationName(oldrel)),
    6542                 :             :                                                          errtable(oldrel)));
    6543                 :             :                                 else
    6544   [ +  -  +  - ]:           8 :                                         ereport(ERROR,
    6545                 :             :                                                         (errcode(ERRCODE_CHECK_VIOLATION),
    6546                 :             :                                                          errmsg("partition constraint of relation \"%s\" is violated by some row",
    6547                 :             :                                                                         RelationGetRelationName(oldrel)),
    6548                 :             :                                                          errtable(oldrel)));
    6549                 :           0 :                         }
    6550                 :             : 
    6551                 :             :                         /* Write the tuple out to the new relation */
    6552         [ +  + ]:      100207 :                         if (newrel)
    6553                 :       32670 :                                 table_tuple_insert(newrel, insertslot, mycid,
    6554                 :       16335 :                                                                    ti_options, bistate);
    6555                 :             : 
    6556                 :      100207 :                         ResetExprContext(econtext);
    6557                 :             : 
    6558         [ +  - ]:      100207 :                         CHECK_FOR_INTERRUPTS();
    6559                 :      100207 :                 }
    6560                 :             : 
    6561                 :         674 :                 MemoryContextSwitchTo(oldCxt);
    6562                 :         674 :                 table_endscan(scan);
    6563                 :         674 :                 UnregisterSnapshot(snapshot);
    6564                 :             : 
    6565                 :         674 :                 ExecDropSingleTupleTableSlot(oldslot);
    6566         [ +  + ]:         674 :                 if (newslot)
    6567                 :         164 :                         ExecDropSingleTupleTableSlot(newslot);
    6568                 :         674 :         }
    6569                 :             : 
    6570                 :         777 :         FreeExecutorState(estate);
    6571                 :             : 
    6572                 :         777 :         table_close(oldrel, NoLock);
    6573         [ +  + ]:         777 :         if (newrel)
    6574                 :             :         {
    6575                 :         164 :                 FreeBulkInsertState(bistate);
    6576                 :             : 
    6577                 :         164 :                 table_finish_bulk_insert(newrel, ti_options);
    6578                 :             : 
    6579                 :         164 :                 table_close(newrel, NoLock);
    6580                 :         164 :         }
    6581                 :         777 : }
    6582                 :             : 
    6583                 :             : /*
    6584                 :             :  * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
    6585                 :             :  */
    6586                 :             : static AlteredTableInfo *
    6587                 :        4429 : ATGetQueueEntry(List **wqueue, Relation rel)
    6588                 :             : {
    6589                 :        4429 :         Oid                     relid = RelationGetRelid(rel);
    6590                 :        4429 :         AlteredTableInfo *tab;
    6591                 :        4429 :         ListCell   *ltab;
    6592                 :             : 
    6593   [ +  +  +  +  :        7113 :         foreach(ltab, *wqueue)
             +  +  +  + ]
    6594                 :             :         {
    6595                 :        2684 :                 tab = (AlteredTableInfo *) lfirst(ltab);
    6596         [ +  + ]:        2684 :                 if (tab->relid == relid)
    6597                 :         825 :                         return tab;
    6598                 :        1859 :         }
    6599                 :             : 
    6600                 :             :         /*
    6601                 :             :          * Not there, so add it.  Note that we make a copy of the relation's
    6602                 :             :          * existing descriptor before anything interesting can happen to it.
    6603                 :             :          */
    6604                 :        3604 :         tab = palloc0_object(AlteredTableInfo);
    6605                 :        3604 :         tab->relid = relid;
    6606                 :        3604 :         tab->rel = NULL;                     /* set later */
    6607                 :        3604 :         tab->relkind = rel->rd_rel->relkind;
    6608                 :        3604 :         tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
    6609                 :        3604 :         tab->newAccessMethod = InvalidOid;
    6610                 :        3604 :         tab->chgAccessMethod = false;
    6611                 :        3604 :         tab->newTableSpace = InvalidOid;
    6612                 :        3604 :         tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
    6613                 :        3604 :         tab->chgPersistence = false;
    6614                 :             : 
    6615                 :        3604 :         *wqueue = lappend(*wqueue, tab);
    6616                 :             : 
    6617                 :        3604 :         return tab;
    6618                 :        4429 : }
    6619                 :             : 
    6620                 :             : static const char *
    6621                 :          14 : alter_table_type_to_string(AlterTableType cmdtype)
    6622                 :             : {
    6623   [ +  +  -  -  :          14 :         switch (cmdtype)
          -  +  -  -  -  
          +  +  -  -  -  
          -  -  -  -  -  
          -  +  +  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  -  
          +  +  +  -  +  
          -  -  -  -  -  
             -  -  -  - ]
    6624                 :             :         {
    6625                 :             :                 case AT_AddColumn:
    6626                 :             :                 case AT_AddColumnToView:
    6627                 :           0 :                         return "ADD COLUMN";
    6628                 :             :                 case AT_ColumnDefault:
    6629                 :             :                 case AT_CookedColumnDefault:
    6630                 :           0 :                         return "ALTER COLUMN ... SET DEFAULT";
    6631                 :             :                 case AT_DropNotNull:
    6632                 :           1 :                         return "ALTER COLUMN ... DROP NOT NULL";
    6633                 :             :                 case AT_SetNotNull:
    6634                 :           1 :                         return "ALTER COLUMN ... SET NOT NULL";
    6635                 :             :                 case AT_SetExpression:
    6636                 :           0 :                         return "ALTER COLUMN ... SET EXPRESSION";
    6637                 :             :                 case AT_DropExpression:
    6638                 :           0 :                         return "ALTER COLUMN ... DROP EXPRESSION";
    6639                 :             :                 case AT_SetStatistics:
    6640                 :           0 :                         return "ALTER COLUMN ... SET STATISTICS";
    6641                 :             :                 case AT_SetOptions:
    6642                 :           2 :                         return "ALTER COLUMN ... SET";
    6643                 :             :                 case AT_ResetOptions:
    6644                 :           0 :                         return "ALTER COLUMN ... RESET";
    6645                 :             :                 case AT_SetStorage:
    6646                 :           0 :                         return "ALTER COLUMN ... SET STORAGE";
    6647                 :             :                 case AT_SetCompression:
    6648                 :           0 :                         return "ALTER COLUMN ... SET COMPRESSION";
    6649                 :             :                 case AT_DropColumn:
    6650                 :           1 :                         return "DROP COLUMN";
    6651                 :             :                 case AT_AddIndex:
    6652                 :             :                 case AT_ReAddIndex:
    6653                 :           0 :                         return NULL;            /* not real grammar */
    6654                 :             :                 case AT_AddConstraint:
    6655                 :             :                 case AT_ReAddConstraint:
    6656                 :             :                 case AT_ReAddDomainConstraint:
    6657                 :             :                 case AT_AddIndexConstraint:
    6658                 :           0 :                         return "ADD CONSTRAINT";
    6659                 :             :                 case AT_AlterConstraint:
    6660                 :           1 :                         return "ALTER CONSTRAINT";
    6661                 :             :                 case AT_ValidateConstraint:
    6662                 :           0 :                         return "VALIDATE CONSTRAINT";
    6663                 :             :                 case AT_DropConstraint:
    6664                 :           0 :                         return "DROP CONSTRAINT";
    6665                 :             :                 case AT_ReAddComment:
    6666                 :           0 :                         return NULL;            /* not real grammar */
    6667                 :             :                 case AT_AlterColumnType:
    6668                 :           0 :                         return "ALTER COLUMN ... SET DATA TYPE";
    6669                 :             :                 case AT_AlterColumnGenericOptions:
    6670                 :           0 :                         return "ALTER COLUMN ... OPTIONS";
    6671                 :             :                 case AT_ChangeOwner:
    6672                 :           0 :                         return "OWNER TO";
    6673                 :             :                 case AT_ClusterOn:
    6674                 :           0 :                         return "CLUSTER ON";
    6675                 :             :                 case AT_DropCluster:
    6676                 :           0 :                         return "SET WITHOUT CLUSTER";
    6677                 :             :                 case AT_SetAccessMethod:
    6678                 :           0 :                         return "SET ACCESS METHOD";
    6679                 :             :                 case AT_SetLogged:
    6680                 :           1 :                         return "SET LOGGED";
    6681                 :             :                 case AT_SetUnLogged:
    6682                 :           1 :                         return "SET UNLOGGED";
    6683                 :             :                 case AT_DropOids:
    6684                 :           0 :                         return "SET WITHOUT OIDS";
    6685                 :             :                 case AT_SetTableSpace:
    6686                 :           0 :                         return "SET TABLESPACE";
    6687                 :             :                 case AT_SetRelOptions:
    6688                 :           0 :                         return "SET";
    6689                 :             :                 case AT_ResetRelOptions:
    6690                 :           0 :                         return "RESET";
    6691                 :             :                 case AT_ReplaceRelOptions:
    6692                 :           0 :                         return NULL;            /* not real grammar */
    6693                 :             :                 case AT_EnableTrig:
    6694                 :           0 :                         return "ENABLE TRIGGER";
    6695                 :             :                 case AT_EnableAlwaysTrig:
    6696                 :           0 :                         return "ENABLE ALWAYS TRIGGER";
    6697                 :             :                 case AT_EnableReplicaTrig:
    6698                 :           0 :                         return "ENABLE REPLICA TRIGGER";
    6699                 :             :                 case AT_DisableTrig:
    6700                 :           0 :                         return "DISABLE TRIGGER";
    6701                 :             :                 case AT_EnableTrigAll:
    6702                 :           0 :                         return "ENABLE TRIGGER ALL";
    6703                 :             :                 case AT_DisableTrigAll:
    6704                 :           0 :                         return "DISABLE TRIGGER ALL";
    6705                 :             :                 case AT_EnableTrigUser:
    6706                 :           0 :                         return "ENABLE TRIGGER USER";
    6707                 :             :                 case AT_DisableTrigUser:
    6708                 :           0 :                         return "DISABLE TRIGGER USER";
    6709                 :             :                 case AT_EnableRule:
    6710                 :           0 :                         return "ENABLE RULE";
    6711                 :             :                 case AT_EnableAlwaysRule:
    6712                 :           0 :                         return "ENABLE ALWAYS RULE";
    6713                 :             :                 case AT_EnableReplicaRule:
    6714                 :           0 :                         return "ENABLE REPLICA RULE";
    6715                 :             :                 case AT_DisableRule:
    6716                 :           0 :                         return "DISABLE RULE";
    6717                 :             :                 case AT_AddInherit:
    6718                 :           0 :                         return "INHERIT";
    6719                 :             :                 case AT_DropInherit:
    6720                 :           0 :                         return "NO INHERIT";
    6721                 :             :                 case AT_AddOf:
    6722                 :           0 :                         return "OF";
    6723                 :             :                 case AT_DropOf:
    6724                 :           0 :                         return "NOT OF";
    6725                 :             :                 case AT_ReplicaIdentity:
    6726                 :           0 :                         return "REPLICA IDENTITY";
    6727                 :             :                 case AT_EnableRowSecurity:
    6728                 :           0 :                         return "ENABLE ROW SECURITY";
    6729                 :             :                 case AT_DisableRowSecurity:
    6730                 :           0 :                         return "DISABLE ROW SECURITY";
    6731                 :             :                 case AT_ForceRowSecurity:
    6732                 :           0 :                         return "FORCE ROW SECURITY";
    6733                 :             :                 case AT_NoForceRowSecurity:
    6734                 :           0 :                         return "NO FORCE ROW SECURITY";
    6735                 :             :                 case AT_GenericOptions:
    6736                 :           0 :                         return "OPTIONS";
    6737                 :             :                 case AT_AttachPartition:
    6738                 :           1 :                         return "ATTACH PARTITION";
    6739                 :             :                 case AT_DetachPartition:
    6740                 :           3 :                         return "DETACH PARTITION";
    6741                 :             :                 case AT_DetachPartitionFinalize:
    6742                 :           1 :                         return "DETACH PARTITION ... FINALIZE";
    6743                 :             :                 case AT_MergePartitions:
    6744                 :           0 :                         return "MERGE PARTITIONS";
    6745                 :             :                 case AT_SplitPartition:
    6746                 :           1 :                         return "SPLIT PARTITION";
    6747                 :             :                 case AT_AddIdentity:
    6748                 :           0 :                         return "ALTER COLUMN ... ADD IDENTITY";
    6749                 :             :                 case AT_SetIdentity:
    6750                 :           0 :                         return "ALTER COLUMN ... SET";
    6751                 :             :                 case AT_DropIdentity:
    6752                 :           0 :                         return "ALTER COLUMN ... DROP IDENTITY";
    6753                 :             :                 case AT_ReAddStatistics:
    6754                 :           0 :                         return NULL;            /* not real grammar */
    6755                 :             :         }
    6756                 :             : 
    6757                 :           0 :         return NULL;
    6758                 :          14 : }
    6759                 :             : 
    6760                 :             : /*
    6761                 :             :  * ATSimplePermissions
    6762                 :             :  *
    6763                 :             :  * - Ensure that it is a relation (or possibly a view)
    6764                 :             :  * - Ensure this user is the owner
    6765                 :             :  * - Ensure that it is not a system table
    6766                 :             :  */
    6767                 :             : static void
    6768                 :        3957 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
    6769                 :             : {
    6770                 :        3957 :         int                     actual_target;
    6771                 :             : 
    6772   [ +  -  +  +  :        3957 :         switch (rel->rd_rel->relkind)
          +  +  +  +  +  
                      + ]
    6773                 :             :         {
    6774                 :             :                 case RELKIND_RELATION:
    6775                 :        2767 :                         actual_target = ATT_TABLE;
    6776                 :        2767 :                         break;
    6777                 :             :                 case RELKIND_PARTITIONED_TABLE:
    6778                 :         892 :                         actual_target = ATT_PARTITIONED_TABLE;
    6779                 :         892 :                         break;
    6780                 :             :                 case RELKIND_VIEW:
    6781                 :          65 :                         actual_target = ATT_VIEW;
    6782                 :          65 :                         break;
    6783                 :             :                 case RELKIND_MATVIEW:
    6784                 :           7 :                         actual_target = ATT_MATVIEW;
    6785                 :           7 :                         break;
    6786                 :             :                 case RELKIND_INDEX:
    6787                 :          24 :                         actual_target = ATT_INDEX;
    6788                 :          24 :                         break;
    6789                 :             :                 case RELKIND_PARTITIONED_INDEX:
    6790                 :          52 :                         actual_target = ATT_PARTITIONED_INDEX;
    6791                 :          52 :                         break;
    6792                 :             :                 case RELKIND_COMPOSITE_TYPE:
    6793                 :          33 :                         actual_target = ATT_COMPOSITE_TYPE;
    6794                 :          33 :                         break;
    6795                 :             :                 case RELKIND_FOREIGN_TABLE:
    6796                 :         113 :                         actual_target = ATT_FOREIGN_TABLE;
    6797                 :         113 :                         break;
    6798                 :             :                 case RELKIND_SEQUENCE:
    6799                 :           4 :                         actual_target = ATT_SEQUENCE;
    6800                 :           4 :                         break;
    6801                 :             :                 default:
    6802                 :           0 :                         actual_target = 0;
    6803                 :           0 :                         break;
    6804                 :             :         }
    6805                 :             : 
    6806                 :             :         /* Wrong target type? */
    6807         [ +  + ]:        3957 :         if ((actual_target & allowed_targets) == 0)
    6808                 :             :         {
    6809                 :          14 :                 const char *action_str = alter_table_type_to_string(cmdtype);
    6810                 :             : 
    6811         [ +  - ]:          14 :                 if (action_str)
    6812   [ +  -  +  - ]:          14 :                         ereport(ERROR,
    6813                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6814                 :             :                         /* translator: %s is a group of some SQL keywords */
    6815                 :             :                                          errmsg("ALTER action %s cannot be performed on relation \"%s\"",
    6816                 :             :                                                         action_str, RelationGetRelationName(rel)),
    6817                 :             :                                          errdetail_relkind_not_supported(rel->rd_rel->relkind)));
    6818                 :             :                 else
    6819                 :             :                         /* internal error? */
    6820   [ #  #  #  # ]:           0 :                         elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
    6821                 :             :                                  RelationGetRelationName(rel));
    6822                 :           0 :         }
    6823                 :             : 
    6824                 :             :         /* Permissions checks */
    6825         [ +  + ]:        3943 :         if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
    6826                 :           4 :                 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
    6827                 :           2 :                                            RelationGetRelationName(rel));
    6828                 :             : 
    6829   [ +  +  +  - ]:        3943 :         if (!allowSystemTableMods && IsSystemRelation(rel))
    6830   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    6831                 :             :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    6832                 :             :                                  errmsg("permission denied: \"%s\" is a system catalog",
    6833                 :             :                                                 RelationGetRelationName(rel))));
    6834                 :        3943 : }
    6835                 :             : 
    6836                 :             : /*
    6837                 :             :  * ATSimpleRecursion
    6838                 :             :  *
    6839                 :             :  * Simple table recursion sufficient for most ALTER TABLE operations.
    6840                 :             :  * All direct and indirect children are processed in an unspecified order.
    6841                 :             :  * Note that if a child inherits from the original table via multiple
    6842                 :             :  * inheritance paths, it will be visited just once.
    6843                 :             :  */
    6844                 :             : static void
    6845                 :         204 : ATSimpleRecursion(List **wqueue, Relation rel,
    6846                 :             :                                   AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    6847                 :             :                                   AlterTableUtilityContext *context)
    6848                 :             : {
    6849                 :             :         /*
    6850                 :             :          * Propagate to children, if desired and if there are (or might be) any
    6851                 :             :          * children.
    6852                 :             :          */
    6853   [ +  +  +  + ]:         204 :         if (recurse && rel->rd_rel->relhassubclass)
    6854                 :             :         {
    6855                 :          13 :                 Oid                     relid = RelationGetRelid(rel);
    6856                 :          13 :                 ListCell   *child;
    6857                 :          13 :                 List       *children;
    6858                 :             : 
    6859                 :          13 :                 children = find_all_inheritors(relid, lockmode, NULL);
    6860                 :             : 
    6861                 :             :                 /*
    6862                 :             :                  * find_all_inheritors does the recursive search of the inheritance
    6863                 :             :                  * hierarchy, so all we have to do is process all of the relids in the
    6864                 :             :                  * list that it returns.
    6865                 :             :                  */
    6866   [ +  -  +  +  :          57 :                 foreach(child, children)
                   +  + ]
    6867                 :             :                 {
    6868                 :          44 :                         Oid                     childrelid = lfirst_oid(child);
    6869                 :          44 :                         Relation        childrel;
    6870                 :             : 
    6871         [ +  + ]:          44 :                         if (childrelid == relid)
    6872                 :          13 :                                 continue;
    6873                 :             :                         /* find_all_inheritors already got lock */
    6874                 :          31 :                         childrel = relation_open(childrelid, NoLock);
    6875                 :          31 :                         CheckAlterTableIsSafe(childrel);
    6876                 :          31 :                         ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
    6877                 :          31 :                         relation_close(childrel, NoLock);
    6878      [ -  +  + ]:          44 :                 }
    6879                 :          13 :         }
    6880                 :         204 : }
    6881                 :             : 
    6882                 :             : /*
    6883                 :             :  * Obtain list of partitions of the given table, locking them all at the given
    6884                 :             :  * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
    6885                 :             :  *
    6886                 :             :  * This function is a no-op if the given relation is not a partitioned table;
    6887                 :             :  * in particular, nothing is done if it's a legacy inheritance parent.
    6888                 :             :  */
    6889                 :             : static void
    6890                 :         132 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
    6891                 :             : {
    6892         [ +  + ]:         132 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    6893                 :             :         {
    6894                 :          28 :                 List       *inh;
    6895                 :          28 :                 ListCell   *cell;
    6896                 :             : 
    6897                 :          28 :                 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
    6898                 :             :                 /* first element is the parent rel; must ignore it */
    6899   [ +  -  +  +  :          95 :                 for_each_from(cell, inh, 1)
                   +  + ]
    6900                 :             :                 {
    6901                 :          67 :                         Relation        childrel;
    6902                 :             : 
    6903                 :             :                         /* find_all_inheritors already got lock */
    6904                 :          67 :                         childrel = table_open(lfirst_oid(cell), NoLock);
    6905                 :          67 :                         CheckAlterTableIsSafe(childrel);
    6906                 :          67 :                         table_close(childrel, NoLock);
    6907                 :          67 :                 }
    6908                 :          28 :                 list_free(inh);
    6909                 :          28 :         }
    6910                 :         132 : }
    6911                 :             : 
    6912                 :             : /*
    6913                 :             :  * ATTypedTableRecursion
    6914                 :             :  *
    6915                 :             :  * Propagate ALTER TYPE operations to the typed tables of that type.
    6916                 :             :  * Also check the RESTRICT/CASCADE behavior.  Given CASCADE, also permit
    6917                 :             :  * recursion to inheritance children of the typed tables.
    6918                 :             :  */
    6919                 :             : static void
    6920                 :          29 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
    6921                 :             :                                           LOCKMODE lockmode, AlterTableUtilityContext *context)
    6922                 :             : {
    6923                 :          29 :         ListCell   *child;
    6924                 :          29 :         List       *children;
    6925                 :             : 
    6926         [ +  - ]:          29 :         Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    6927                 :             : 
    6928                 :          58 :         children = find_typed_table_dependencies(rel->rd_rel->reltype,
    6929                 :          29 :                                                                                          RelationGetRelationName(rel),
    6930                 :          29 :                                                                                          cmd->behavior);
    6931                 :             : 
    6932   [ +  +  +  +  :          31 :         foreach(child, children)
                   +  + ]
    6933                 :             :         {
    6934                 :           2 :                 Oid                     childrelid = lfirst_oid(child);
    6935                 :           2 :                 Relation        childrel;
    6936                 :             : 
    6937                 :           2 :                 childrel = relation_open(childrelid, lockmode);
    6938                 :           2 :                 CheckAlterTableIsSafe(childrel);
    6939                 :           2 :                 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
    6940                 :           2 :                 relation_close(childrel, NoLock);
    6941                 :           2 :         }
    6942                 :          29 : }
    6943                 :             : 
    6944                 :             : 
    6945                 :             : /*
    6946                 :             :  * find_composite_type_dependencies
    6947                 :             :  *
    6948                 :             :  * Check to see if the type "typeOid" is being used as a column in some table
    6949                 :             :  * (possibly nested several levels deep in composite types, arrays, etc!).
    6950                 :             :  * Eventually, we'd like to propagate the check or rewrite operation
    6951                 :             :  * into such tables, but for now, just error out if we find any.
    6952                 :             :  *
    6953                 :             :  * Caller should provide either the associated relation of a rowtype,
    6954                 :             :  * or a type name (not both) for use in the error message, if any.
    6955                 :             :  *
    6956                 :             :  * Note that "typeOid" is not necessarily a composite type; it could also be
    6957                 :             :  * another container type such as an array or range, or a domain over one of
    6958                 :             :  * these things.  The name of this function is therefore somewhat historical,
    6959                 :             :  * but it's not worth changing.
    6960                 :             :  *
    6961                 :             :  * We assume that functions and views depending on the type are not reasons
    6962                 :             :  * to reject the ALTER.  (How safe is this really?)
    6963                 :             :  */
    6964                 :             : void
    6965                 :         723 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
    6966                 :             :                                                                  const char *origTypeName)
    6967                 :             : {
    6968                 :         723 :         Relation        depRel;
    6969                 :         723 :         ScanKeyData key[2];
    6970                 :         723 :         SysScanDesc depScan;
    6971                 :         723 :         HeapTuple       depTup;
    6972                 :             : 
    6973                 :             :         /* since this function recurses, it could be driven to stack overflow */
    6974                 :         723 :         check_stack_depth();
    6975                 :             : 
    6976                 :             :         /*
    6977                 :             :          * We scan pg_depend to find those things that depend on the given type.
    6978                 :             :          * (We assume we can ignore refobjsubid for a type.)
    6979                 :             :          */
    6980                 :         723 :         depRel = table_open(DependRelationId, AccessShareLock);
    6981                 :             : 
    6982                 :        1446 :         ScanKeyInit(&key[0],
    6983                 :             :                                 Anum_pg_depend_refclassid,
    6984                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
    6985                 :         723 :                                 ObjectIdGetDatum(TypeRelationId));
    6986                 :        1446 :         ScanKeyInit(&key[1],
    6987                 :             :                                 Anum_pg_depend_refobjid,
    6988                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
    6989                 :         723 :                                 ObjectIdGetDatum(typeOid));
    6990                 :             : 
    6991                 :        1446 :         depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
    6992                 :         723 :                                                                  NULL, 2, key);
    6993                 :             : 
    6994         [ +  + ]:        1116 :         while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
    6995                 :             :         {
    6996                 :         415 :                 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
    6997                 :         415 :                 Relation        rel;
    6998                 :         415 :                 TupleDesc       tupleDesc;
    6999                 :         415 :                 Form_pg_attribute att;
    7000                 :             : 
    7001                 :             :                 /* Check for directly dependent types */
    7002         [ +  + ]:         415 :                 if (pg_depend->classid == TypeRelationId)
    7003                 :             :                 {
    7004                 :             :                         /*
    7005                 :             :                          * This must be an array, domain, or range containing the given
    7006                 :             :                          * type, so recursively check for uses of this type.  Note that
    7007                 :             :                          * any error message will mention the original type not the
    7008                 :             :                          * container; this is intentional.
    7009                 :             :                          */
    7010                 :         696 :                         find_composite_type_dependencies(pg_depend->objid,
    7011                 :         348 :                                                                                          origRelation, origTypeName);
    7012                 :         348 :                         continue;
    7013                 :             :                 }
    7014                 :             : 
    7015                 :             :                 /* Else, ignore dependees that aren't relations */
    7016         [ +  + ]:          67 :                 if (pg_depend->classid != RelationRelationId)
    7017                 :          20 :                         continue;
    7018                 :             : 
    7019                 :          47 :                 rel = relation_open(pg_depend->objid, AccessShareLock);
    7020                 :          47 :                 tupleDesc = RelationGetDescr(rel);
    7021                 :             : 
    7022                 :             :                 /*
    7023                 :             :                  * If objsubid identifies a specific column, refer to that in error
    7024                 :             :                  * messages.  Otherwise, search to see if there's a user column of the
    7025                 :             :                  * type.  (We assume system columns are never of interesting types.)
    7026                 :             :                  * The search is needed because an index containing an expression
    7027                 :             :                  * column of the target type will just be recorded as a whole-relation
    7028                 :             :                  * dependency.  If we do not find a column of the type, the dependency
    7029                 :             :                  * must indicate that the type is transiently referenced in an index
    7030                 :             :                  * expression but not stored on disk, which we assume is OK, just as
    7031                 :             :                  * we do for references in views.  (It could also be that the target
    7032                 :             :                  * type is embedded in some container type that is stored in an index
    7033                 :             :                  * column, but the previous recursion should catch such cases.)
    7034                 :             :                  */
    7035   [ +  +  -  + ]:          47 :                 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
    7036                 :          21 :                         att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
    7037                 :             :                 else
    7038                 :             :                 {
    7039                 :          26 :                         att = NULL;
    7040         [ +  + ]:          68 :                         for (int attno = 1; attno <= tupleDesc->natts; attno++)
    7041                 :             :                         {
    7042                 :          42 :                                 att = TupleDescAttr(tupleDesc, attno - 1);
    7043   [ +  +  +  - ]:          42 :                                 if (att->atttypid == typeOid && !att->attisdropped)
    7044                 :           1 :                                         break;
    7045                 :          41 :                                 att = NULL;
    7046                 :          41 :                         }
    7047         [ +  + ]:          26 :                         if (att == NULL)
    7048                 :             :                         {
    7049                 :             :                                 /* No such column, so assume OK */
    7050                 :          25 :                                 relation_close(rel, AccessShareLock);
    7051                 :          25 :                                 continue;
    7052                 :             :                         }
    7053                 :             :                 }
    7054                 :             : 
    7055                 :             :                 /*
    7056                 :             :                  * We definitely should reject if the relation has storage.  If it's
    7057                 :             :                  * partitioned, then perhaps we don't have to reject: if there are
    7058                 :             :                  * partitions then we'll fail when we find one, else there is no
    7059                 :             :                  * stored data to worry about.  However, it's possible that the type
    7060                 :             :                  * change would affect conclusions about whether the type is sortable
    7061                 :             :                  * or hashable and thus (if it's a partitioning column) break the
    7062                 :             :                  * partitioning rule.  For now, reject for partitioned rels too.
    7063                 :             :                  */
    7064         [ -  + ]:          22 :                 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
    7065                 :           0 :                         RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
    7066                 :             :                 {
    7067         [ +  + ]:          22 :                         if (origTypeName)
    7068   [ +  -  +  - ]:           5 :                                 ereport(ERROR,
    7069                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7070                 :             :                                                  errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    7071                 :             :                                                                 origTypeName,
    7072                 :             :                                                                 RelationGetRelationName(rel),
    7073                 :             :                                                                 NameStr(att->attname))));
    7074         [ +  + ]:          17 :                         else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    7075   [ +  -  +  - ]:           3 :                                 ereport(ERROR,
    7076                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7077                 :             :                                                  errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    7078                 :             :                                                                 RelationGetRelationName(origRelation),
    7079                 :             :                                                                 RelationGetRelationName(rel),
    7080                 :             :                                                                 NameStr(att->attname))));
    7081         [ +  + ]:          14 :                         else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    7082   [ +  -  +  - ]:           1 :                                 ereport(ERROR,
    7083                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7084                 :             :                                                  errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
    7085                 :             :                                                                 RelationGetRelationName(origRelation),
    7086                 :             :                                                                 RelationGetRelationName(rel),
    7087                 :             :                                                                 NameStr(att->attname))));
    7088                 :             :                         else
    7089   [ +  -  +  - ]:          13 :                                 ereport(ERROR,
    7090                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7091                 :             :                                                  errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
    7092                 :             :                                                                 RelationGetRelationName(origRelation),
    7093                 :             :                                                                 RelationGetRelationName(rel),
    7094                 :             :                                                                 NameStr(att->attname))));
    7095                 :           0 :                 }
    7096         [ #  # ]:           0 :                 else if (OidIsValid(rel->rd_rel->reltype))
    7097                 :             :                 {
    7098                 :             :                         /*
    7099                 :             :                          * A view or composite type itself isn't a problem, but we must
    7100                 :             :                          * recursively check for indirect dependencies via its rowtype.
    7101                 :             :                          */
    7102                 :           0 :                         find_composite_type_dependencies(rel->rd_rel->reltype,
    7103                 :           0 :                                                                                          origRelation, origTypeName);
    7104                 :           0 :                 }
    7105                 :             : 
    7106                 :           0 :                 relation_close(rel, AccessShareLock);
    7107      [ -  +  - ]:         393 :         }
    7108                 :             : 
    7109                 :         701 :         systable_endscan(depScan);
    7110                 :             : 
    7111                 :         701 :         relation_close(depRel, AccessShareLock);
    7112                 :         701 : }
    7113                 :             : 
    7114                 :             : 
    7115                 :             : /*
    7116                 :             :  * find_typed_table_dependencies
    7117                 :             :  *
    7118                 :             :  * Check to see if a composite type is being used as the type of a
    7119                 :             :  * typed table.  Abort if any are found and behavior is RESTRICT.
    7120                 :             :  * Else return the list of tables.
    7121                 :             :  */
    7122                 :             : static List *
    7123                 :          33 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
    7124                 :             : {
    7125                 :          33 :         Relation        classRel;
    7126                 :          33 :         ScanKeyData key[1];
    7127                 :          33 :         TableScanDesc scan;
    7128                 :          33 :         HeapTuple       tuple;
    7129                 :          33 :         List       *result = NIL;
    7130                 :             : 
    7131                 :          33 :         classRel = table_open(RelationRelationId, AccessShareLock);
    7132                 :             : 
    7133                 :          66 :         ScanKeyInit(&key[0],
    7134                 :             :                                 Anum_pg_class_reloftype,
    7135                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
    7136                 :          33 :                                 ObjectIdGetDatum(typeOid));
    7137                 :             : 
    7138                 :          33 :         scan = table_beginscan_catalog(classRel, 1, key);
    7139                 :             : 
    7140         [ +  + ]:          39 :         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    7141                 :             :         {
    7142                 :          10 :                 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
    7143                 :             : 
    7144         [ +  + ]:          10 :                 if (behavior == DROP_RESTRICT)
    7145   [ +  -  +  - ]:           4 :                         ereport(ERROR,
    7146                 :             :                                         (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
    7147                 :             :                                          errmsg("cannot alter type \"%s\" because it is the type of a typed table",
    7148                 :             :                                                         typeName),
    7149                 :             :                                          errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
    7150                 :             :                 else
    7151                 :           6 :                         result = lappend_oid(result, classform->oid);
    7152                 :           6 :         }
    7153                 :             : 
    7154                 :          29 :         table_endscan(scan);
    7155                 :          29 :         table_close(classRel, AccessShareLock);
    7156                 :             : 
    7157                 :          58 :         return result;
    7158                 :          29 : }
    7159                 :             : 
    7160                 :             : 
    7161                 :             : /*
    7162                 :             :  * check_of_type
    7163                 :             :  *
    7164                 :             :  * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF.  If it
    7165                 :             :  * isn't suitable, throw an error.  Currently, we require that the type
    7166                 :             :  * originated with CREATE TYPE AS.  We could support any row type, but doing so
    7167                 :             :  * would require handling a number of extra corner cases in the DDL commands.
    7168                 :             :  * (Also, allowing domain-over-composite would open up a can of worms about
    7169                 :             :  * whether and how the domain's constraints should apply to derived tables.)
    7170                 :             :  */
    7171                 :             : void
    7172                 :          28 : check_of_type(HeapTuple typetuple)
    7173                 :             : {
    7174                 :          28 :         Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
    7175                 :          28 :         bool            typeOk = false;
    7176                 :             : 
    7177         [ +  + ]:          28 :         if (typ->typtype == TYPTYPE_COMPOSITE)
    7178                 :             :         {
    7179                 :          27 :                 Relation        typeRelation;
    7180                 :             : 
    7181         [ +  - ]:          27 :                 Assert(OidIsValid(typ->typrelid));
    7182                 :          27 :                 typeRelation = relation_open(typ->typrelid, AccessShareLock);
    7183                 :          27 :                 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    7184                 :             : 
    7185                 :             :                 /*
    7186                 :             :                  * Close the parent rel, but keep our AccessShareLock on it until xact
    7187                 :             :                  * commit.  That will prevent someone else from deleting or ALTERing
    7188                 :             :                  * the type before the typed table creation/conversion commits.
    7189                 :             :                  */
    7190                 :          27 :                 relation_close(typeRelation, NoLock);
    7191                 :             : 
    7192         [ +  + ]:          27 :                 if (!typeOk)
    7193   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    7194                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7195                 :             :                                          errmsg("type %s is the row type of another table",
    7196                 :             :                                                         format_type_be(typ->oid)),
    7197                 :             :                                          errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
    7198                 :          26 :         }
    7199                 :             :         else
    7200   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    7201                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7202                 :             :                                  errmsg("type %s is not a composite type",
    7203                 :             :                                                 format_type_be(typ->oid))));
    7204                 :          26 : }
    7205                 :             : 
    7206                 :             : 
    7207                 :             : /*
    7208                 :             :  * ALTER TABLE ADD COLUMN
    7209                 :             :  *
    7210                 :             :  * Adds an additional attribute to a relation making the assumption that
    7211                 :             :  * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
    7212                 :             :  * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
    7213                 :             :  * AlterTableCmd's.
    7214                 :             :  *
    7215                 :             :  * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
    7216                 :             :  * have to decide at runtime whether to recurse or not depending on whether we
    7217                 :             :  * actually add a column or merely merge with an existing column.  (We can't
    7218                 :             :  * check this in a static pre-pass because it won't handle multiple inheritance
    7219                 :             :  * situations correctly.)
    7220                 :             :  */
    7221                 :             : static void
    7222                 :         307 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    7223                 :             :                                 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
    7224                 :             :                                 AlterTableUtilityContext *context)
    7225                 :             : {
    7226   [ +  +  +  + ]:         307 :         if (rel->rd_rel->reloftype && !recursing)
    7227   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    7228                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7229                 :             :                                  errmsg("cannot add column to typed table")));
    7230                 :             : 
    7231         [ +  + ]:         306 :         if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    7232                 :           9 :                 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    7233                 :             : 
    7234   [ +  +  +  + ]:         306 :         if (recurse && !is_view)
    7235                 :         289 :                 cmd->recurse = true;
    7236                 :         306 : }
    7237                 :             : 
    7238                 :             : /*
    7239                 :             :  * Add a column to a table.  The return value is the address of the
    7240                 :             :  * new column in the parent relation.
    7241                 :             :  *
    7242                 :             :  * cmd is pass-by-ref so that we can replace it with the parse-transformed
    7243                 :             :  * copy (but that happens only after we check for IF NOT EXISTS).
    7244                 :             :  */
    7245                 :             : static ObjectAddress
    7246                 :         425 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
    7247                 :             :                                 AlterTableCmd **cmd, bool recurse, bool recursing,
    7248                 :             :                                 LOCKMODE lockmode, AlterTablePass cur_pass,
    7249                 :             :                                 AlterTableUtilityContext *context)
    7250                 :             : {
    7251                 :         425 :         Oid                     myrelid = RelationGetRelid(rel);
    7252                 :         425 :         ColumnDef  *colDef = castNode(ColumnDef, (*cmd)->def);
    7253                 :         425 :         bool            if_not_exists = (*cmd)->missing_ok;
    7254                 :         425 :         Relation        pgclass,
    7255                 :             :                                 attrdesc;
    7256                 :         425 :         HeapTuple       reltup;
    7257                 :         425 :         Form_pg_class relform;
    7258                 :         425 :         Form_pg_attribute attribute;
    7259                 :         425 :         int                     newattnum;
    7260                 :         425 :         char            relkind;
    7261                 :         425 :         Expr       *defval;
    7262                 :         425 :         List       *children;
    7263                 :         425 :         ListCell   *child;
    7264                 :         425 :         AlterTableCmd *childcmd;
    7265                 :         425 :         ObjectAddress address;
    7266                 :         425 :         TupleDesc       tupdesc;
    7267                 :             : 
    7268                 :             :         /* since this function recurses, it could be driven to stack overflow */
    7269                 :         425 :         check_stack_depth();
    7270                 :             : 
    7271                 :             :         /* At top level, permission check was done in ATPrepCmd, else do it */
    7272         [ +  + ]:         425 :         if (recursing)
    7273                 :         121 :                 ATSimplePermissions((*cmd)->subtype, rel,
    7274                 :             :                                                         ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    7275                 :             : 
    7276   [ +  +  +  + ]:         425 :         if (rel->rd_rel->relispartition && !recursing)
    7277   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    7278                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7279                 :             :                                  errmsg("cannot add column to a partition")));
    7280                 :             : 
    7281                 :         423 :         attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
    7282                 :             : 
    7283                 :             :         /*
    7284                 :             :          * Are we adding the column to a recursion child?  If so, check whether to
    7285                 :             :          * merge with an existing definition for the column.  If we do merge, we
    7286                 :             :          * must not recurse.  Children will already have the column, and recursing
    7287                 :             :          * into them would mess up attinhcount.
    7288                 :             :          */
    7289         [ +  + ]:         423 :         if (colDef->inhcount > 0)
    7290                 :             :         {
    7291                 :         121 :                 HeapTuple       tuple;
    7292                 :             : 
    7293                 :             :                 /* Does child already have a column by this name? */
    7294                 :         121 :                 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
    7295         [ +  + ]:         121 :                 if (HeapTupleIsValid(tuple))
    7296                 :             :                 {
    7297                 :          10 :                         Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    7298                 :          10 :                         Oid                     ctypeId;
    7299                 :          10 :                         int32           ctypmod;
    7300                 :          10 :                         Oid                     ccollid;
    7301                 :             : 
    7302                 :             :                         /* Child column must match on type, typmod, and collation */
    7303                 :          10 :                         typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
    7304         [ +  - ]:          10 :                         if (ctypeId != childatt->atttypid ||
    7305                 :          10 :                                 ctypmod != childatt->atttypmod)
    7306   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    7307                 :             :                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    7308                 :             :                                                  errmsg("child table \"%s\" has different type for column \"%s\"",
    7309                 :             :                                                                 RelationGetRelationName(rel), colDef->colname)));
    7310                 :          10 :                         ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
    7311         [ +  - ]:          10 :                         if (ccollid != childatt->attcollation)
    7312   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    7313                 :             :                                                 (errcode(ERRCODE_COLLATION_MISMATCH),
    7314                 :             :                                                  errmsg("child table \"%s\" has different collation for column \"%s\"",
    7315                 :             :                                                                 RelationGetRelationName(rel), colDef->colname),
    7316                 :             :                                                  errdetail("\"%s\" versus \"%s\"",
    7317                 :             :                                                                    get_collation_name(ccollid),
    7318                 :             :                                                                    get_collation_name(childatt->attcollation))));
    7319                 :             : 
    7320                 :             :                         /* Bump the existing child att's inhcount */
    7321   [ +  -  +  - ]:          20 :                         if (pg_add_s16_overflow(childatt->attinhcount, 1,
    7322                 :          10 :                                                                         &childatt->attinhcount))
    7323   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    7324                 :             :                                                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    7325                 :             :                                                 errmsg("too many inheritance parents"));
    7326                 :          10 :                         CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
    7327                 :             : 
    7328                 :          10 :                         heap_freetuple(tuple);
    7329                 :             : 
    7330                 :             :                         /* Inform the user about the merge */
    7331   [ -  +  +  - ]:          10 :                         ereport(NOTICE,
    7332                 :             :                                         (errmsg("merging definition of column \"%s\" for child \"%s\"",
    7333                 :             :                                                         colDef->colname, RelationGetRelationName(rel))));
    7334                 :             : 
    7335                 :          10 :                         table_close(attrdesc, RowExclusiveLock);
    7336                 :             : 
    7337                 :             :                         /* Make the child column change visible */
    7338                 :          10 :                         CommandCounterIncrement();
    7339                 :             : 
    7340                 :          10 :                         return InvalidObjectAddress;
    7341                 :          10 :                 }
    7342         [ +  + ]:         121 :         }
    7343                 :             : 
    7344                 :             :         /* skip if the name already exists and if_not_exists is true */
    7345         [ +  + ]:         413 :         if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
    7346                 :             :         {
    7347                 :           9 :                 table_close(attrdesc, RowExclusiveLock);
    7348                 :           9 :                 return InvalidObjectAddress;
    7349                 :             :         }
    7350                 :             : 
    7351                 :             :         /*
    7352                 :             :          * Okay, we need to add the column, so go ahead and do parse
    7353                 :             :          * transformation.  This can result in queueing up, or even immediately
    7354                 :             :          * executing, subsidiary operations (such as creation of unique indexes);
    7355                 :             :          * so we mustn't do it until we have made the if_not_exists check.
    7356                 :             :          *
    7357                 :             :          * When recursing, the command was already transformed and we needn't do
    7358                 :             :          * so again.  Also, if context isn't given we can't transform.  (That
    7359                 :             :          * currently happens only for AT_AddColumnToView; we expect that view.c
    7360                 :             :          * passed us a ColumnDef that doesn't need work.)
    7361                 :             :          */
    7362   [ +  +  +  + ]:         404 :         if (context != NULL && !recursing)
    7363                 :             :         {
    7364                 :         566 :                 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
    7365                 :         283 :                                                                    cur_pass, context);
    7366         [ +  - ]:         283 :                 Assert(*cmd != NULL);
    7367                 :         283 :                 colDef = castNode(ColumnDef, (*cmd)->def);
    7368                 :         283 :         }
    7369                 :             : 
    7370                 :             :         /*
    7371                 :             :          * Regular inheritance children are independent enough not to inherit the
    7372                 :             :          * identity column from parent hence cannot recursively add identity
    7373                 :             :          * column if the table has inheritance children.
    7374                 :             :          *
    7375                 :             :          * Partitions, on the other hand, are integral part of a partitioned table
    7376                 :             :          * and inherit identity column.  Hence propagate identity column down the
    7377                 :             :          * partition hierarchy.
    7378                 :             :          */
    7379         [ +  + ]:         404 :         if (colDef->identity &&
    7380         [ +  - ]:           9 :                 recurse &&
    7381   [ +  +  +  + ]:           9 :                 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
    7382                 :           8 :                 find_inheritance_children(myrelid, NoLock) != NIL)
    7383   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    7384                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7385                 :             :                                  errmsg("cannot recursively add identity column to table that has child tables")));
    7386                 :             : 
    7387                 :         385 :         pgclass = table_open(RelationRelationId, RowExclusiveLock);
    7388                 :             : 
    7389                 :         385 :         reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    7390         [ +  - ]:         385 :         if (!HeapTupleIsValid(reltup))
    7391   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", myrelid);
    7392                 :         385 :         relform = (Form_pg_class) GETSTRUCT(reltup);
    7393                 :         385 :         relkind = relform->relkind;
    7394                 :             : 
    7395                 :             :         /* Determine the new attribute's number */
    7396                 :         385 :         newattnum = relform->relnatts + 1;
    7397         [ +  - ]:         385 :         if (newattnum > MaxHeapAttributeNumber)
    7398   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    7399                 :             :                                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    7400                 :             :                                  errmsg("tables can have at most %d columns",
    7401                 :             :                                                 MaxHeapAttributeNumber)));
    7402                 :             : 
    7403                 :             :         /*
    7404                 :             :          * Construct new attribute's pg_attribute entry.
    7405                 :             :          */
    7406                 :         385 :         tupdesc = BuildDescForRelation(list_make1(colDef));
    7407                 :             : 
    7408                 :         385 :         attribute = TupleDescAttr(tupdesc, 0);
    7409                 :             : 
    7410                 :             :         /* Fix up attribute number */
    7411                 :         385 :         attribute->attnum = newattnum;
    7412                 :             : 
    7413                 :             :         /* make sure datatype is legal for a column */
    7414                 :         770 :         CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
    7415                 :         385 :                                            list_make1_oid(rel->rd_rel->reltype),
    7416                 :         385 :                                            (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
    7417                 :             : 
    7418                 :         385 :         InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
    7419                 :             : 
    7420                 :         385 :         table_close(attrdesc, RowExclusiveLock);
    7421                 :             : 
    7422                 :             :         /*
    7423                 :             :          * Update pg_class tuple as appropriate
    7424                 :             :          */
    7425                 :         385 :         relform->relnatts = newattnum;
    7426                 :             : 
    7427                 :         385 :         CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
    7428                 :             : 
    7429                 :         385 :         heap_freetuple(reltup);
    7430                 :             : 
    7431                 :             :         /* Post creation hook for new attribute */
    7432         [ +  - ]:         385 :         InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
    7433                 :             : 
    7434                 :         385 :         table_close(pgclass, RowExclusiveLock);
    7435                 :             : 
    7436                 :             :         /* Make the attribute's catalog entry visible */
    7437                 :         385 :         CommandCounterIncrement();
    7438                 :             : 
    7439                 :             :         /*
    7440                 :             :          * Store the DEFAULT, if any, in the catalogs
    7441                 :             :          */
    7442         [ +  + ]:         385 :         if (colDef->raw_default)
    7443                 :             :         {
    7444                 :         116 :                 RawColumnDefault *rawEnt;
    7445                 :             : 
    7446                 :         116 :                 rawEnt = palloc_object(RawColumnDefault);
    7447                 :         116 :                 rawEnt->attnum = attribute->attnum;
    7448                 :         116 :                 rawEnt->raw_default = copyObject(colDef->raw_default);
    7449                 :         116 :                 rawEnt->generated = colDef->generated;
    7450                 :             : 
    7451                 :             :                 /*
    7452                 :             :                  * This function is intended for CREATE TABLE, so it processes a
    7453                 :             :                  * _list_ of defaults, but we just do one.
    7454                 :             :                  */
    7455                 :         116 :                 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    7456                 :             :                                                                   false, true, false, NULL);
    7457                 :             : 
    7458                 :             :                 /* Make the additional catalog changes visible */
    7459                 :         116 :                 CommandCounterIncrement();
    7460                 :         116 :         }
    7461                 :             : 
    7462                 :             :         /*
    7463                 :             :          * Tell Phase 3 to fill in the default expression, if there is one.
    7464                 :             :          *
    7465                 :             :          * If there is no default, Phase 3 doesn't have to do anything, because
    7466                 :             :          * that effectively means that the default is NULL.  The heap tuple access
    7467                 :             :          * routines always check for attnum > # of attributes in tuple, and return
    7468                 :             :          * NULL if so, so without any modification of the tuple data we will get
    7469                 :             :          * the effect of NULL values in the new column.
    7470                 :             :          *
    7471                 :             :          * An exception occurs when the new column is of a domain type: the domain
    7472                 :             :          * might have a not-null constraint, or a check constraint that indirectly
    7473                 :             :          * rejects nulls.  If there are any domain constraints then we construct
    7474                 :             :          * an explicit NULL default value that will be passed through
    7475                 :             :          * CoerceToDomain processing.  (This is a tad inefficient, since it causes
    7476                 :             :          * rewriting the table which we really wouldn't have to do; but we do it
    7477                 :             :          * to preserve the historical behavior that such a failure will be raised
    7478                 :             :          * only if the table currently contains some rows.)
    7479                 :             :          *
    7480                 :             :          * Note: we use build_column_default, and not just the cooked default
    7481                 :             :          * returned by AddRelationNewConstraints, so that the right thing happens
    7482                 :             :          * when a datatype's default applies.
    7483                 :             :          *
    7484                 :             :          * Note: it might seem that this should happen at the end of Phase 2, so
    7485                 :             :          * that the effects of subsequent subcommands can be taken into account.
    7486                 :             :          * It's intentional that we do it now, though.  The new column should be
    7487                 :             :          * filled according to what is said in the ADD COLUMN subcommand, so that
    7488                 :             :          * the effects are the same as if this subcommand had been run by itself
    7489                 :             :          * and the later subcommands had been issued in new ALTER TABLE commands.
    7490                 :             :          *
    7491                 :             :          * We can skip this entirely for relations without storage, since Phase 3
    7492                 :             :          * is certainly not going to touch them.
    7493                 :             :          */
    7494   [ +  +  +  -  :         385 :         if (RELKIND_HAS_STORAGE(relkind))
          +  -  +  -  -  
                      + ]
    7495                 :             :         {
    7496                 :         324 :                 bool            has_domain_constraints;
    7497                 :         324 :                 bool            has_missing = false;
    7498                 :             : 
    7499                 :             :                 /*
    7500                 :             :                  * For an identity column, we can't use build_column_default(),
    7501                 :             :                  * because the sequence ownership isn't set yet.  So do it manually.
    7502                 :             :                  */
    7503         [ +  + ]:         324 :                 if (colDef->identity)
    7504                 :             :                 {
    7505                 :           7 :                         NextValueExpr *nve = makeNode(NextValueExpr);
    7506                 :             : 
    7507                 :           7 :                         nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
    7508                 :           7 :                         nve->typeId = attribute->atttypid;
    7509                 :             : 
    7510                 :           7 :                         defval = (Expr *) nve;
    7511                 :           7 :                 }
    7512                 :             :                 else
    7513                 :         317 :                         defval = (Expr *) build_column_default(rel, attribute->attnum);
    7514                 :             : 
    7515                 :             :                 /* Build CoerceToDomain(NULL) expression if needed */
    7516                 :         324 :                 has_domain_constraints = DomainHasConstraints(attribute->atttypid);
    7517   [ +  +  +  + ]:         324 :                 if (!defval && has_domain_constraints)
    7518                 :             :                 {
    7519                 :           1 :                         Oid                     baseTypeId;
    7520                 :           1 :                         int32           baseTypeMod;
    7521                 :           1 :                         Oid                     baseTypeColl;
    7522                 :             : 
    7523                 :           1 :                         baseTypeMod = attribute->atttypmod;
    7524                 :           1 :                         baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
    7525                 :           1 :                         baseTypeColl = get_typcollation(baseTypeId);
    7526                 :           1 :                         defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
    7527                 :           1 :                         defval = (Expr *) coerce_to_target_type(NULL,
    7528                 :           1 :                                                                                                         (Node *) defval,
    7529                 :           1 :                                                                                                         baseTypeId,
    7530                 :           1 :                                                                                                         attribute->atttypid,
    7531                 :           1 :                                                                                                         attribute->atttypmod,
    7532                 :             :                                                                                                         COERCION_ASSIGNMENT,
    7533                 :             :                                                                                                         COERCE_IMPLICIT_CAST,
    7534                 :             :                                                                                                         -1);
    7535         [ +  - ]:           1 :                         if (defval == NULL) /* should not happen */
    7536   [ #  #  #  # ]:           0 :                                 elog(ERROR, "failed to coerce base type to domain");
    7537                 :           1 :                 }
    7538                 :             : 
    7539         [ +  + ]:         324 :                 if (defval)
    7540                 :             :                 {
    7541                 :         114 :                         NewColumnValue *newval;
    7542                 :             : 
    7543                 :             :                         /* Prepare defval for execution, either here or in Phase 3 */
    7544                 :         114 :                         defval = expression_planner(defval);
    7545                 :             : 
    7546                 :             :                         /* Add the new default to the newvals list */
    7547                 :         114 :                         newval = palloc0_object(NewColumnValue);
    7548                 :         114 :                         newval->attnum = attribute->attnum;
    7549                 :         114 :                         newval->expr = defval;
    7550                 :         114 :                         newval->is_generated = (colDef->generated != '\0');
    7551                 :             : 
    7552                 :         114 :                         tab->newvals = lappend(tab->newvals, newval);
    7553                 :             : 
    7554                 :             :                         /*
    7555                 :             :                          * Attempt to skip a complete table rewrite by storing the
    7556                 :             :                          * specified DEFAULT value outside of the heap.  This is only
    7557                 :             :                          * allowed for plain relations and non-generated columns, and the
    7558                 :             :                          * default expression can't be volatile (stable is OK).  Note that
    7559                 :             :                          * contain_volatile_functions deems CoerceToDomain immutable, but
    7560                 :             :                          * here we consider that coercion to a domain with constraints is
    7561                 :             :                          * volatile; else it might fail even when the table is empty.
    7562                 :             :                          */
    7563         [ +  - ]:         114 :                         if (rel->rd_rel->relkind == RELKIND_RELATION &&
    7564         [ +  + ]:         114 :                                 !colDef->generated &&
    7565   [ +  +  +  + ]:          93 :                                 !has_domain_constraints &&
    7566                 :          91 :                                 !contain_volatile_functions((Node *) defval))
    7567                 :             :                         {
    7568                 :          65 :                                 EState     *estate;
    7569                 :          65 :                                 ExprState  *exprState;
    7570                 :          65 :                                 Datum           missingval;
    7571                 :          65 :                                 bool            missingIsNull;
    7572                 :             : 
    7573                 :             :                                 /* Evaluate the default expression */
    7574                 :          65 :                                 estate = CreateExecutorState();
    7575                 :          65 :                                 exprState = ExecPrepareExpr(defval, estate);
    7576                 :         130 :                                 missingval = ExecEvalExpr(exprState,
    7577         [ +  - ]:          65 :                                                                                   GetPerTupleExprContext(estate),
    7578                 :             :                                                                                   &missingIsNull);
    7579                 :             :                                 /* If it turns out NULL, nothing to do; else store it */
    7580         [ -  + ]:          65 :                                 if (!missingIsNull)
    7581                 :             :                                 {
    7582                 :          65 :                                         StoreAttrMissingVal(rel, attribute->attnum, missingval);
    7583                 :             :                                         /* Make the additional catalog change visible */
    7584                 :          65 :                                         CommandCounterIncrement();
    7585                 :          65 :                                         has_missing = true;
    7586                 :          65 :                                 }
    7587                 :          65 :                                 FreeExecutorState(estate);
    7588                 :          65 :                         }
    7589                 :             :                         else
    7590                 :             :                         {
    7591                 :             :                                 /*
    7592                 :             :                                  * Failed to use missing mode.  We have to do a table rewrite
    7593                 :             :                                  * to install the value --- unless it's a virtual generated
    7594                 :             :                                  * column.
    7595                 :             :                                  */
    7596         [ +  + ]:          49 :                                 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
    7597                 :          34 :                                         tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7598                 :             :                         }
    7599                 :         114 :                 }
    7600                 :             : 
    7601         [ +  + ]:         324 :                 if (!has_missing)
    7602                 :             :                 {
    7603                 :             :                         /*
    7604                 :             :                          * If the new column is NOT NULL, and there is no missing value,
    7605                 :             :                          * tell Phase 3 it needs to check for NULLs.
    7606                 :             :                          */
    7607                 :         259 :                         tab->verify_new_notnull |= colDef->is_not_null;
    7608                 :         259 :                 }
    7609                 :         324 :         }
    7610                 :             : 
    7611                 :             :         /*
    7612                 :             :          * Add needed dependency entries for the new column.
    7613                 :             :          */
    7614                 :         385 :         add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
    7615                 :         385 :         add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
    7616                 :             : 
    7617                 :             :         /*
    7618                 :             :          * Propagate to children as appropriate.  Unlike most other ALTER
    7619                 :             :          * routines, we have to do this one level of recursion at a time; we can't
    7620                 :             :          * use find_all_inheritors to do it in one pass.
    7621                 :             :          */
    7622                 :         385 :         children =
    7623                 :         385 :                 find_inheritance_children(RelationGetRelid(rel), lockmode);
    7624                 :             : 
    7625                 :             :         /*
    7626                 :             :          * If we are told not to recurse, there had better not be any child
    7627                 :             :          * tables; else the addition would put them out of step.
    7628                 :             :          */
    7629   [ +  +  +  + ]:         385 :         if (children && !recurse)
    7630   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    7631                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7632                 :             :                                  errmsg("column must be added to child tables too")));
    7633                 :             : 
    7634                 :             :         /* Children should see column as singly inherited */
    7635         [ +  + ]:         383 :         if (!recursing)
    7636                 :             :         {
    7637                 :         272 :                 childcmd = copyObject(*cmd);
    7638                 :         272 :                 colDef = castNode(ColumnDef, childcmd->def);
    7639                 :         272 :                 colDef->inhcount = 1;
    7640                 :         272 :                 colDef->is_local = false;
    7641                 :         272 :         }
    7642                 :             :         else
    7643                 :         111 :                 childcmd = *cmd;                /* no need to copy again */
    7644                 :             : 
    7645   [ +  +  +  +  :         504 :         foreach(child, children)
                   +  + ]
    7646                 :             :         {
    7647                 :         121 :                 Oid                     childrelid = lfirst_oid(child);
    7648                 :         121 :                 Relation        childrel;
    7649                 :         121 :                 AlteredTableInfo *childtab;
    7650                 :             : 
    7651                 :             :                 /* find_inheritance_children already got lock */
    7652                 :         121 :                 childrel = table_open(childrelid, NoLock);
    7653                 :         121 :                 CheckAlterTableIsSafe(childrel);
    7654                 :             : 
    7655                 :             :                 /* Find or create work queue entry for this table */
    7656                 :         121 :                 childtab = ATGetQueueEntry(wqueue, childrel);
    7657                 :             : 
    7658                 :             :                 /* Recurse to child; return value is ignored */
    7659                 :         242 :                 ATExecAddColumn(wqueue, childtab, childrel,
    7660                 :         121 :                                                 &childcmd, recurse, true,
    7661                 :         121 :                                                 lockmode, cur_pass, context);
    7662                 :             : 
    7663                 :         121 :                 table_close(childrel, NoLock);
    7664                 :         121 :         }
    7665                 :             : 
    7666                 :         383 :         ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
    7667                 :         383 :         return address;
    7668                 :         402 : }
    7669                 :             : 
    7670                 :             : /*
    7671                 :             :  * If a new or renamed column will collide with the name of an existing
    7672                 :             :  * column and if_not_exists is false then error out, else do nothing.
    7673                 :             :  */
    7674                 :             : static bool
    7675                 :         486 : check_for_column_name_collision(Relation rel, const char *colname,
    7676                 :             :                                                                 bool if_not_exists)
    7677                 :             : {
    7678                 :         486 :         HeapTuple       attTuple;
    7679                 :         486 :         int                     attnum;
    7680                 :             : 
    7681                 :             :         /*
    7682                 :             :          * this test is deliberately not attisdropped-aware, since if one tries to
    7683                 :             :          * add a column matching a dropped column name, it's gonna fail anyway.
    7684                 :             :          */
    7685                 :         486 :         attTuple = SearchSysCache2(ATTNAME,
    7686                 :         486 :                                                            ObjectIdGetDatum(RelationGetRelid(rel)),
    7687                 :         486 :                                                            PointerGetDatum(colname));
    7688         [ +  + ]:         486 :         if (!HeapTupleIsValid(attTuple))
    7689                 :         470 :                 return true;
    7690                 :             : 
    7691                 :          16 :         attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
    7692                 :          16 :         ReleaseSysCache(attTuple);
    7693                 :             : 
    7694                 :             :         /*
    7695                 :             :          * We throw a different error message for conflicts with system column
    7696                 :             :          * names, since they are normally not shown and the user might otherwise
    7697                 :             :          * be confused about the reason for the conflict.
    7698                 :             :          */
    7699         [ +  + ]:          16 :         if (attnum <= 0)
    7700   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    7701                 :             :                                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7702                 :             :                                  errmsg("column name \"%s\" conflicts with a system column name",
    7703                 :             :                                                 colname)));
    7704                 :             :         else
    7705                 :             :         {
    7706         [ +  + ]:          14 :                 if (if_not_exists)
    7707                 :             :                 {
    7708   [ -  +  +  - ]:           9 :                         ereport(NOTICE,
    7709                 :             :                                         (errcode(ERRCODE_DUPLICATE_COLUMN),
    7710                 :             :                                          errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
    7711                 :             :                                                         colname, RelationGetRelationName(rel))));
    7712                 :           9 :                         return false;
    7713                 :             :                 }
    7714                 :             : 
    7715   [ +  -  +  - ]:           5 :                 ereport(ERROR,
    7716                 :             :                                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7717                 :             :                                  errmsg("column \"%s\" of relation \"%s\" already exists",
    7718                 :             :                                                 colname, RelationGetRelationName(rel))));
    7719                 :             :         }
    7720                 :             : 
    7721                 :           0 :         return true;
    7722                 :         479 : }
    7723                 :             : 
    7724                 :             : /*
    7725                 :             :  * Install a column's dependency on its datatype.
    7726                 :             :  */
    7727                 :             : static void
    7728                 :         563 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
    7729                 :             : {
    7730                 :         563 :         ObjectAddress myself,
    7731                 :             :                                 referenced;
    7732                 :             : 
    7733                 :         563 :         myself.classId = RelationRelationId;
    7734                 :         563 :         myself.objectId = relid;
    7735                 :         563 :         myself.objectSubId = attnum;
    7736                 :         563 :         referenced.classId = TypeRelationId;
    7737                 :         563 :         referenced.objectId = typid;
    7738                 :         563 :         referenced.objectSubId = 0;
    7739                 :         563 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7740                 :         563 : }
    7741                 :             : 
    7742                 :             : /*
    7743                 :             :  * Install a column's dependency on its collation.
    7744                 :             :  */
    7745                 :             : static void
    7746                 :         563 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
    7747                 :             : {
    7748                 :         563 :         ObjectAddress myself,
    7749                 :             :                                 referenced;
    7750                 :             : 
    7751                 :             :         /* We know the default collation is pinned, so don't bother recording it */
    7752   [ +  +  +  + ]:         563 :         if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
    7753                 :             :         {
    7754                 :           3 :                 myself.classId = RelationRelationId;
    7755                 :           3 :                 myself.objectId = relid;
    7756                 :           3 :                 myself.objectSubId = attnum;
    7757                 :           3 :                 referenced.classId = CollationRelationId;
    7758                 :           3 :                 referenced.objectId = collid;
    7759                 :           3 :                 referenced.objectSubId = 0;
    7760                 :           3 :                 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7761                 :           3 :         }
    7762                 :         563 : }
    7763                 :             : 
    7764                 :             : /*
    7765                 :             :  * ALTER TABLE ALTER COLUMN DROP NOT NULL
    7766                 :             :  *
    7767                 :             :  * Return the address of the modified column.  If the column was already
    7768                 :             :  * nullable, InvalidObjectAddress is returned.
    7769                 :             :  */
    7770                 :             : static ObjectAddress
    7771                 :          34 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
    7772                 :             :                                   LOCKMODE lockmode)
    7773                 :             : {
    7774                 :          34 :         HeapTuple       tuple;
    7775                 :          34 :         HeapTuple       conTup;
    7776                 :          34 :         Form_pg_attribute attTup;
    7777                 :          34 :         AttrNumber      attnum;
    7778                 :          34 :         Relation        attr_rel;
    7779                 :          34 :         ObjectAddress address;
    7780                 :             : 
    7781                 :             :         /*
    7782                 :             :          * lookup the attribute
    7783                 :             :          */
    7784                 :          34 :         attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7785                 :             : 
    7786                 :          34 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    7787         [ +  + ]:          34 :         if (!HeapTupleIsValid(tuple))
    7788   [ +  -  +  - ]:           3 :                 ereport(ERROR,
    7789                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7790                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7791                 :             :                                                 colName, RelationGetRelationName(rel))));
    7792                 :          31 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    7793                 :          31 :         attnum = attTup->attnum;
    7794                 :          31 :         ObjectAddressSubSet(address, RelationRelationId,
    7795                 :             :                                                 RelationGetRelid(rel), attnum);
    7796                 :             : 
    7797                 :             :         /* If the column is already nullable there's nothing to do. */
    7798         [ +  - ]:          31 :         if (!attTup->attnotnull)
    7799                 :             :         {
    7800                 :           0 :                 table_close(attr_rel, RowExclusiveLock);
    7801                 :           0 :                 return InvalidObjectAddress;
    7802                 :             :         }
    7803                 :             : 
    7804                 :             :         /* Prevent them from altering a system attribute */
    7805         [ +  - ]:          31 :         if (attnum <= 0)
    7806   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    7807                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7808                 :             :                                  errmsg("cannot alter system column \"%s\"",
    7809                 :             :                                                 colName)));
    7810                 :             : 
    7811         [ +  + ]:          31 :         if (attTup->attidentity)
    7812   [ +  -  +  - ]:           3 :                 ereport(ERROR,
    7813                 :             :                                 (errcode(ERRCODE_SYNTAX_ERROR),
    7814                 :             :                                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    7815                 :             :                                                 colName, RelationGetRelationName(rel))));
    7816                 :             : 
    7817                 :             :         /*
    7818                 :             :          * If rel is partition, shouldn't drop NOT NULL if parent has the same.
    7819                 :             :          */
    7820         [ +  + ]:          28 :         if (rel->rd_rel->relispartition)
    7821                 :             :         {
    7822                 :           2 :                 Oid                     parentId = get_partition_parent(RelationGetRelid(rel), false);
    7823                 :           2 :                 Relation        parent = table_open(parentId, AccessShareLock);
    7824                 :           2 :                 TupleDesc       tupDesc = RelationGetDescr(parent);
    7825                 :           2 :                 AttrNumber      parent_attnum;
    7826                 :             : 
    7827                 :           2 :                 parent_attnum = get_attnum(parentId, colName);
    7828         [ -  + ]:           2 :                 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
    7829   [ +  -  +  - ]:           2 :                         ereport(ERROR,
    7830                 :             :                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7831                 :             :                                          errmsg("column \"%s\" is marked NOT NULL in parent table",
    7832                 :             :                                                         colName)));
    7833                 :           0 :                 table_close(parent, AccessShareLock);
    7834                 :           0 :         }
    7835                 :             : 
    7836                 :             :         /*
    7837                 :             :          * Find the constraint that makes this column NOT NULL, and drop it.
    7838                 :             :          * dropconstraint_internal() resets attnotnull.
    7839                 :             :          */
    7840                 :          26 :         conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
    7841         [ +  - ]:          26 :         if (conTup == NULL)
    7842   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
    7843                 :             :                          colName, RelationGetRelationName(rel));
    7844                 :             : 
    7845                 :             :         /* The normal case: we have a pg_constraint row, remove it */
    7846                 :          52 :         dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
    7847                 :          26 :                                                         false, lockmode);
    7848                 :          26 :         heap_freetuple(conTup);
    7849                 :             : 
    7850         [ +  - ]:          26 :         InvokeObjectPostAlterHook(RelationRelationId,
    7851                 :             :                                                           RelationGetRelid(rel), attnum);
    7852                 :             : 
    7853                 :          26 :         table_close(attr_rel, RowExclusiveLock);
    7854                 :             : 
    7855                 :          26 :         return address;
    7856                 :          26 : }
    7857                 :             : 
    7858                 :             : /*
    7859                 :             :  * set_attnotnull
    7860                 :             :  *              Helper to update/validate the pg_attribute status of a not-null
    7861                 :             :  *              constraint
    7862                 :             :  *
    7863                 :             :  * pg_attribute.attnotnull is set true, if it isn't already.
    7864                 :             :  * If queue_validation is true, also set up wqueue to validate the constraint.
    7865                 :             :  * wqueue may be given as NULL when validation is not needed (e.g., on table
    7866                 :             :  * creation).
    7867                 :             :  */
    7868                 :             : static void
    7869                 :        1811 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
    7870                 :             :                            bool is_valid, bool queue_validation)
    7871                 :             : {
    7872                 :        1811 :         Form_pg_attribute attr;
    7873                 :        1811 :         CompactAttribute *thisatt;
    7874                 :             : 
    7875   [ +  +  +  - ]:        1811 :         Assert(!queue_validation || wqueue);
    7876                 :             : 
    7877                 :        1811 :         CheckAlterTableIsSafe(rel);
    7878                 :             : 
    7879                 :             :         /*
    7880                 :             :          * Exit quickly by testing attnotnull from the tupledesc's copy of the
    7881                 :             :          * attribute.
    7882                 :             :          */
    7883                 :        1811 :         attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
    7884         [ -  + ]:        1811 :         if (attr->attisdropped)
    7885                 :           0 :                 return;
    7886                 :             : 
    7887         [ +  + ]:        1811 :         if (!attr->attnotnull)
    7888                 :             :         {
    7889                 :         227 :                 Relation        attr_rel;
    7890                 :         227 :                 HeapTuple       tuple;
    7891                 :             : 
    7892                 :         227 :                 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7893                 :             : 
    7894                 :         227 :                 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
    7895         [ +  - ]:         227 :                 if (!HeapTupleIsValid(tuple))
    7896   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
    7897                 :             :                                  attnum, RelationGetRelid(rel));
    7898                 :             : 
    7899                 :         227 :                 thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
    7900                 :         227 :                 thisatt->attnullability = ATTNULLABLE_VALID;
    7901                 :             : 
    7902                 :         227 :                 attr = (Form_pg_attribute) GETSTRUCT(tuple);
    7903                 :             : 
    7904                 :         227 :                 attr->attnotnull = true;
    7905                 :         227 :                 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    7906                 :             : 
    7907                 :             :                 /*
    7908                 :             :                  * If the nullness isn't already proven by validated constraints, have
    7909                 :             :                  * ALTER TABLE phase 3 test for it.
    7910                 :             :                  */
    7911   [ +  +  +  -  :         227 :                 if (queue_validation && wqueue &&
                   +  + ]
    7912                 :         189 :                         !NotNullImpliedByRelConstraints(rel, attr))
    7913                 :             :                 {
    7914                 :         182 :                         AlteredTableInfo *tab;
    7915                 :             : 
    7916                 :         182 :                         tab = ATGetQueueEntry(wqueue, rel);
    7917                 :         182 :                         tab->verify_new_notnull = true;
    7918                 :         182 :                 }
    7919                 :             : 
    7920                 :         227 :                 CommandCounterIncrement();
    7921                 :             : 
    7922                 :         227 :                 table_close(attr_rel, RowExclusiveLock);
    7923                 :         227 :                 heap_freetuple(tuple);
    7924                 :         227 :         }
    7925                 :             :         else
    7926                 :             :         {
    7927                 :        1584 :                 CacheInvalidateRelcache(rel);
    7928                 :             :         }
    7929         [ -  + ]:        1811 : }
    7930                 :             : 
    7931                 :             : /*
    7932                 :             :  * ALTER TABLE ALTER COLUMN SET NOT NULL
    7933                 :             :  *
    7934                 :             :  * Add a not-null constraint to a single table and its children.  Returns
    7935                 :             :  * the address of the constraint added to the parent relation, if one gets
    7936                 :             :  * added, or InvalidObjectAddress otherwise.
    7937                 :             :  *
    7938                 :             :  * We must recurse to child tables during execution, rather than using
    7939                 :             :  * ALTER TABLE's normal prep-time recursion.
    7940                 :             :  */
    7941                 :             : static ObjectAddress
    7942                 :         112 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
    7943                 :             :                                  bool recurse, bool recursing, LOCKMODE lockmode)
    7944                 :             : {
    7945                 :         112 :         HeapTuple       tuple;
    7946                 :         112 :         AttrNumber      attnum;
    7947                 :         112 :         ObjectAddress address;
    7948                 :         112 :         Constraint *constraint;
    7949                 :         112 :         CookedConstraint *ccon;
    7950                 :         112 :         List       *cooked;
    7951                 :         112 :         bool            is_no_inherit = false;
    7952                 :             : 
    7953                 :             :         /* Guard against stack overflow due to overly deep inheritance tree. */
    7954                 :         112 :         check_stack_depth();
    7955                 :             : 
    7956                 :             :         /* At top level, permission check was done in ATPrepCmd, else do it */
    7957         [ +  + ]:         112 :         if (recursing)
    7958                 :             :         {
    7959                 :          48 :                 ATSimplePermissions(AT_AddConstraint, rel,
    7960                 :             :                                                         ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
    7961         [ +  - ]:          48 :                 Assert(conName != NULL);
    7962                 :          48 :         }
    7963                 :             : 
    7964                 :         112 :         attnum = get_attnum(RelationGetRelid(rel), colName);
    7965         [ +  + ]:         112 :         if (attnum == InvalidAttrNumber)
    7966   [ +  -  +  - ]:           3 :                 ereport(ERROR,
    7967                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7968                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7969                 :             :                                                 colName, RelationGetRelationName(rel))));
    7970                 :             : 
    7971                 :             :         /* Prevent them from altering a system attribute */
    7972         [ +  - ]:         109 :         if (attnum <= 0)
    7973   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    7974                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7975                 :             :                                  errmsg("cannot alter system column \"%s\"",
    7976                 :             :                                                 colName)));
    7977                 :             : 
    7978                 :             :         /* See if there's already a constraint */
    7979                 :         109 :         tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
    7980         [ +  + ]:         109 :         if (HeapTupleIsValid(tuple))
    7981                 :             :         {
    7982                 :          26 :                 Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
    7983                 :          26 :                 bool            changed = false;
    7984                 :             : 
    7985                 :             :                 /*
    7986                 :             :                  * Don't let a NO INHERIT constraint be changed into inherit.
    7987                 :             :                  */
    7988   [ +  +  -  + ]:          26 :                 if (conForm->connoinherit && recurse)
    7989   [ +  -  +  - ]:           2 :                         ereport(ERROR,
    7990                 :             :                                         errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7991                 :             :                                         errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
    7992                 :             :                                                    NameStr(conForm->conname),
    7993                 :             :                                                    RelationGetRelationName(rel)));
    7994                 :             : 
    7995                 :             :                 /*
    7996                 :             :                  * If we find an appropriate constraint, we're almost done, but just
    7997                 :             :                  * need to change some properties on it: if we're recursing, increment
    7998                 :             :                  * coninhcount; if not, set conislocal if not already set.
    7999                 :             :                  */
    8000         [ +  + ]:          24 :                 if (recursing)
    8001                 :             :                 {
    8002   [ +  -  +  - ]:          34 :                         if (pg_add_s16_overflow(conForm->coninhcount, 1,
    8003                 :          17 :                                                                         &conForm->coninhcount))
    8004   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    8005                 :             :                                                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    8006                 :             :                                                 errmsg("too many inheritance parents"));
    8007                 :          17 :                         changed = true;
    8008                 :          17 :                 }
    8009         [ +  - ]:           7 :                 else if (!conForm->conislocal)
    8010                 :             :                 {
    8011                 :           0 :                         conForm->conislocal = true;
    8012                 :           0 :                         changed = true;
    8013                 :           0 :                 }
    8014         [ +  + ]:           7 :                 else if (!conForm->convalidated)
    8015                 :             :                 {
    8016                 :             :                         /*
    8017                 :             :                          * Flip attnotnull and convalidated, and also validate the
    8018                 :             :                          * constraint.
    8019                 :             :                          */
    8020                 :           8 :                         return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
    8021                 :           4 :                                                                                         recurse, recursing, lockmode);
    8022                 :             :                 }
    8023                 :             : 
    8024         [ +  + ]:          20 :                 if (changed)
    8025                 :             :                 {
    8026                 :          17 :                         Relation        constr_rel;
    8027                 :             : 
    8028                 :          17 :                         constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
    8029                 :             : 
    8030                 :          17 :                         CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
    8031                 :          17 :                         ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
    8032                 :          17 :                         table_close(constr_rel, RowExclusiveLock);
    8033                 :          17 :                 }
    8034                 :             : 
    8035         [ +  + ]:          20 :                 if (changed)
    8036                 :          17 :                         return address;
    8037                 :             :                 else
    8038                 :           3 :                         return InvalidObjectAddress;
    8039                 :          24 :         }
    8040                 :             : 
    8041                 :             :         /*
    8042                 :             :          * If we're asked not to recurse, and children exist, raise an error for
    8043                 :             :          * partitioned tables.  For inheritance, we act as if NO INHERIT had been
    8044                 :             :          * specified.
    8045                 :             :          */
    8046   [ +  +  +  + ]:          83 :         if (!recurse &&
    8047                 :           5 :                 find_inheritance_children(RelationGetRelid(rel),
    8048                 :           5 :                                                                   NoLock) != NIL)
    8049                 :             :         {
    8050         [ +  + ]:           3 :                 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    8051   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    8052                 :             :                                         errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8053                 :             :                                         errmsg("constraint must be added to child tables too"),
    8054                 :             :                                         errhint("Do not specify the ONLY keyword."));
    8055                 :             :                 else
    8056                 :           2 :                         is_no_inherit = true;
    8057                 :           2 :         }
    8058                 :             : 
    8059                 :             :         /*
    8060                 :             :          * No constraint exists; we must add one.  First determine a name to use,
    8061                 :             :          * if we haven't already.
    8062                 :             :          */
    8063         [ +  + ]:          82 :         if (!recursing)
    8064                 :             :         {
    8065         [ +  - ]:          53 :                 Assert(conName == NULL);
    8066                 :         106 :                 conName = ChooseConstraintName(RelationGetRelationName(rel),
    8067                 :          53 :                                                                            colName, "not_null",
    8068                 :          53 :                                                                            RelationGetNamespace(rel),
    8069                 :             :                                                                            NIL);
    8070                 :          53 :         }
    8071                 :             : 
    8072                 :          82 :         constraint = makeNotNullConstraint(makeString(colName));
    8073                 :          82 :         constraint->is_no_inherit = is_no_inherit;
    8074                 :          82 :         constraint->conname = conName;
    8075                 :             : 
    8076                 :             :         /* and do it */
    8077                 :         164 :         cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
    8078                 :          82 :                                                                            false, !recursing, false, NULL);
    8079                 :          82 :         ccon = linitial(cooked);
    8080                 :          82 :         ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
    8081                 :             : 
    8082         [ +  - ]:          82 :         InvokeObjectPostAlterHook(RelationRelationId,
    8083                 :             :                                                           RelationGetRelid(rel), attnum);
    8084                 :             : 
    8085                 :             :         /* Mark pg_attribute.attnotnull for the column and queue validation */
    8086                 :          82 :         set_attnotnull(wqueue, rel, attnum, true, true);
    8087                 :             : 
    8088                 :             :         /*
    8089                 :             :          * Recurse to propagate the constraint to children that don't have one.
    8090                 :             :          */
    8091         [ +  + ]:          82 :         if (recurse)
    8092                 :             :         {
    8093                 :          78 :                 List       *children;
    8094                 :             : 
    8095                 :         156 :                 children = find_inheritance_children(RelationGetRelid(rel),
    8096                 :          78 :                                                                                          lockmode);
    8097                 :             : 
    8098   [ +  +  +  +  :         195 :                 foreach_oid(childoid, children)
             +  +  +  + ]
    8099                 :             :                 {
    8100                 :          38 :                         Relation        childrel = table_open(childoid, NoLock);
    8101                 :             : 
    8102                 :          38 :                         CommandCounterIncrement();
    8103                 :             : 
    8104                 :          76 :                         ATExecSetNotNull(wqueue, childrel, conName, colName,
    8105                 :          38 :                                                          recurse, true, lockmode);
    8106                 :          38 :                         table_close(childrel, NoLock);
    8107                 :         117 :                 }
    8108                 :          78 :         }
    8109                 :             : 
    8110                 :          82 :         return address;
    8111                 :         106 : }
    8112                 :             : 
    8113                 :             : /*
    8114                 :             :  * NotNullImpliedByRelConstraints
    8115                 :             :  *              Does rel's existing constraints imply NOT NULL for the given attribute?
    8116                 :             :  */
    8117                 :             : static bool
    8118                 :         189 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
    8119                 :             : {
    8120                 :         189 :         NullTest   *nnulltest = makeNode(NullTest);
    8121                 :             : 
    8122                 :         189 :         nnulltest->arg = (Expr *) makeVar(1,
    8123                 :         189 :                                                                           attr->attnum,
    8124                 :         189 :                                                                           attr->atttypid,
    8125                 :         189 :                                                                           attr->atttypmod,
    8126                 :         189 :                                                                           attr->attcollation,
    8127                 :             :                                                                           0);
    8128                 :         189 :         nnulltest->nulltesttype = IS_NOT_NULL;
    8129                 :             : 
    8130                 :             :         /*
    8131                 :             :          * argisrow = false is correct even for a composite column, because
    8132                 :             :          * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
    8133                 :             :          * case, just IS DISTINCT FROM NULL.
    8134                 :             :          */
    8135                 :         189 :         nnulltest->argisrow = false;
    8136                 :         189 :         nnulltest->location = -1;
    8137                 :             : 
    8138         [ +  + ]:         189 :         if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
    8139                 :             :         {
    8140   [ -  +  -  + ]:           7 :                 ereport(DEBUG1,
    8141                 :             :                                 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
    8142                 :             :                                                                  RelationGetRelationName(rel), NameStr(attr->attname))));
    8143                 :           7 :                 return true;
    8144                 :             :         }
    8145                 :             : 
    8146                 :         182 :         return false;
    8147                 :         189 : }
    8148                 :             : 
    8149                 :             : /*
    8150                 :             :  * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
    8151                 :             :  *
    8152                 :             :  * Return the address of the affected column.
    8153                 :             :  */
    8154                 :             : static ObjectAddress
    8155                 :          90 : ATExecColumnDefault(Relation rel, const char *colName,
    8156                 :             :                                         Node *newDefault, LOCKMODE lockmode)
    8157                 :             : {
    8158                 :          90 :         TupleDesc       tupdesc = RelationGetDescr(rel);
    8159                 :          90 :         AttrNumber      attnum;
    8160                 :             :         ObjectAddress address;
    8161                 :             : 
    8162                 :             :         /*
    8163                 :             :          * get the number of the attribute
    8164                 :             :          */
    8165                 :          90 :         attnum = get_attnum(RelationGetRelid(rel), colName);
    8166         [ +  + ]:          90 :         if (attnum == InvalidAttrNumber)
    8167   [ +  -  +  - ]:           5 :                 ereport(ERROR,
    8168                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8169                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8170                 :             :                                                 colName, RelationGetRelationName(rel))));
    8171                 :             : 
    8172                 :             :         /* Prevent them from altering a system attribute */
    8173         [ +  - ]:          85 :         if (attnum <= 0)
    8174   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    8175                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8176                 :             :                                  errmsg("cannot alter system column \"%s\"",
    8177                 :             :                                                 colName)));
    8178                 :             : 
    8179         [ +  + ]:          85 :         if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
    8180   [ +  -  +  -  :           3 :                 ereport(ERROR,
                   +  - ]
    8181                 :             :                                 (errcode(ERRCODE_SYNTAX_ERROR),
    8182                 :             :                                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    8183                 :             :                                                 colName, RelationGetRelationName(rel)),
    8184                 :             :                 /* translator: %s is an SQL ALTER command */
    8185                 :             :                                  newDefault ? 0 : errhint("Use %s instead.",
    8186                 :             :                                                                                   "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
    8187                 :             : 
    8188         [ +  + ]:          82 :         if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
    8189   [ +  -  +  -  :           2 :                 ereport(ERROR,
             -  +  +  + ]
    8190                 :             :                                 (errcode(ERRCODE_SYNTAX_ERROR),
    8191                 :             :                                  errmsg("column \"%s\" of relation \"%s\" is a generated column",
    8192                 :             :                                                 colName, RelationGetRelationName(rel)),
    8193                 :             :                                  newDefault ?
    8194                 :             :                 /* translator: %s is an SQL ALTER command */
    8195                 :             :                                  errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
    8196                 :             :                                  (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
    8197                 :             :                                   errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
    8198                 :             : 
    8199                 :             :         /*
    8200                 :             :          * Remove any old default for the column.  We use RESTRICT here for
    8201                 :             :          * safety, but at present we do not expect anything to depend on the
    8202                 :             :          * default.
    8203                 :             :          *
    8204                 :             :          * We treat removing the existing default as an internal operation when it
    8205                 :             :          * is preparatory to adding a new default, but as a user-initiated
    8206                 :             :          * operation when the user asked for a drop.
    8207                 :             :          */
    8208                 :         160 :         RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    8209                 :          80 :                                           newDefault != NULL);
    8210                 :             : 
    8211         [ +  + ]:          80 :         if (newDefault)
    8212                 :             :         {
    8213                 :             :                 /* SET DEFAULT */
    8214                 :          52 :                 RawColumnDefault *rawEnt;
    8215                 :             : 
    8216                 :          52 :                 rawEnt = palloc_object(RawColumnDefault);
    8217                 :          52 :                 rawEnt->attnum = attnum;
    8218                 :          52 :                 rawEnt->raw_default = newDefault;
    8219                 :          52 :                 rawEnt->generated = '\0';
    8220                 :             : 
    8221                 :             :                 /*
    8222                 :             :                  * This function is intended for CREATE TABLE, so it processes a
    8223                 :             :                  * _list_ of defaults, but we just do one.
    8224                 :             :                  */
    8225                 :          52 :                 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    8226                 :             :                                                                   false, true, false, NULL);
    8227                 :          52 :         }
    8228                 :             : 
    8229                 :          80 :         ObjectAddressSubSet(address, RelationRelationId,
    8230                 :             :                                                 RelationGetRelid(rel), attnum);
    8231                 :             :         return address;
    8232                 :          80 : }
    8233                 :             : 
    8234                 :             : /*
    8235                 :             :  * Add a pre-cooked default expression.
    8236                 :             :  *
    8237                 :             :  * Return the address of the affected column.
    8238                 :             :  */
    8239                 :             : static ObjectAddress
    8240                 :          13 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
    8241                 :             :                                                   Node *newDefault)
    8242                 :             : {
    8243                 :             :         ObjectAddress address;
    8244                 :             : 
    8245                 :             :         /* We assume no checking is required */
    8246                 :             : 
    8247                 :             :         /*
    8248                 :             :          * Remove any old default for the column.  We use RESTRICT here for
    8249                 :             :          * safety, but at present we do not expect anything to depend on the
    8250                 :             :          * default.  (In ordinary cases, there could not be a default in place
    8251                 :             :          * anyway, but it's possible when combining LIKE with inheritance.)
    8252                 :             :          */
    8253                 :          13 :         RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    8254                 :             :                                           true);
    8255                 :             : 
    8256                 :          13 :         (void) StoreAttrDefault(rel, attnum, newDefault, true);
    8257                 :             : 
    8258                 :          13 :         ObjectAddressSubSet(address, RelationRelationId,
    8259                 :             :                                                 RelationGetRelid(rel), attnum);
    8260                 :          13 :         return address;
    8261                 :             : }
    8262                 :             : 
    8263                 :             : /*
    8264                 :             :  * ALTER TABLE ALTER COLUMN ADD IDENTITY
    8265                 :             :  *
    8266                 :             :  * Return the address of the affected column.
    8267                 :             :  */
    8268                 :             : static ObjectAddress
    8269                 :          20 : ATExecAddIdentity(Relation rel, const char *colName,
    8270                 :             :                                   Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
    8271                 :             : {
    8272                 :          20 :         Relation        attrelation;
    8273                 :          20 :         HeapTuple       tuple;
    8274                 :          20 :         Form_pg_attribute attTup;
    8275                 :          20 :         AttrNumber      attnum;
    8276                 :             :         ObjectAddress address;
    8277                 :          20 :         ColumnDef  *cdef = castNode(ColumnDef, def);
    8278                 :          20 :         bool            ispartitioned;
    8279                 :             : 
    8280                 :          20 :         ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8281   [ +  +  +  + ]:          20 :         if (ispartitioned && !recurse)
    8282   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    8283                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8284                 :             :                                  errmsg("cannot add identity to a column of only the partitioned table"),
    8285                 :             :                                  errhint("Do not specify the ONLY keyword.")));
    8286                 :             : 
    8287   [ +  +  +  + ]:          19 :         if (rel->rd_rel->relispartition && !recursing)
    8288   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    8289                 :             :                                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8290                 :             :                                 errmsg("cannot add identity to a column of a partition"));
    8291                 :             : 
    8292                 :          17 :         attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8293                 :             : 
    8294                 :          17 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8295         [ +  - ]:          17 :         if (!HeapTupleIsValid(tuple))
    8296   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    8297                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8298                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8299                 :             :                                                 colName, RelationGetRelationName(rel))));
    8300                 :          17 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8301                 :          17 :         attnum = attTup->attnum;
    8302                 :             : 
    8303                 :             :         /* Can't alter a system attribute */
    8304         [ +  - ]:          17 :         if (attnum <= 0)
    8305   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    8306                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8307                 :             :                                  errmsg("cannot alter system column \"%s\"",
    8308                 :             :                                                 colName)));
    8309                 :             : 
    8310                 :             :         /*
    8311                 :             :          * Creating a column as identity implies NOT NULL, so adding the identity
    8312                 :             :          * to an existing column that is not NOT NULL would create a state that
    8313                 :             :          * cannot be reproduced without contortions.
    8314                 :             :          */
    8315         [ +  + ]:          17 :         if (!attTup->attnotnull)
    8316   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    8317                 :             :                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8318                 :             :                                  errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
    8319                 :             :                                                 colName, RelationGetRelationName(rel))));
    8320                 :             : 
    8321                 :             :         /*
    8322                 :             :          * On the other hand, if a not-null constraint exists, then verify that
    8323                 :             :          * it's compatible.
    8324                 :             :          */
    8325         [ -  + ]:          16 :         if (attTup->attnotnull)
    8326                 :             :         {
    8327                 :          16 :                 HeapTuple       contup;
    8328                 :          16 :                 Form_pg_constraint conForm;
    8329                 :             : 
    8330                 :          32 :                 contup = findNotNullConstraintAttnum(RelationGetRelid(rel),
    8331                 :          16 :                                                                                          attnum);
    8332         [ +  - ]:          16 :                 if (!HeapTupleIsValid(contup))
    8333   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
    8334                 :             :                                  colName, RelationGetRelationName(rel));
    8335                 :             : 
    8336                 :          16 :                 conForm = (Form_pg_constraint) GETSTRUCT(contup);
    8337         [ +  + ]:          16 :                 if (!conForm->convalidated)
    8338   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    8339                 :             :                                         errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8340                 :             :                                         errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
    8341                 :             :                                                    NameStr(conForm->conname), RelationGetRelationName(rel)),
    8342                 :             :                                         errhint("You might need to validate it using %s.",
    8343                 :             :                                                         "ALTER TABLE ... VALIDATE CONSTRAINT"));
    8344                 :          15 :         }
    8345                 :             : 
    8346         [ +  + ]:          15 :         if (attTup->attidentity)
    8347   [ +  -  +  - ]:           3 :                 ereport(ERROR,
    8348                 :             :                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8349                 :             :                                  errmsg("column \"%s\" of relation \"%s\" is already an identity column",
    8350                 :             :                                                 colName, RelationGetRelationName(rel))));
    8351                 :             : 
    8352         [ +  + ]:          12 :         if (attTup->atthasdef)
    8353   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    8354                 :             :                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8355                 :             :                                  errmsg("column \"%s\" of relation \"%s\" already has a default value",
    8356                 :             :                                                 colName, RelationGetRelationName(rel))));
    8357                 :             : 
    8358                 :          11 :         attTup->attidentity = cdef->identity;
    8359                 :          11 :         CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8360                 :             : 
    8361         [ +  - ]:          11 :         InvokeObjectPostAlterHook(RelationRelationId,
    8362                 :             :                                                           RelationGetRelid(rel),
    8363                 :             :                                                           attTup->attnum);
    8364                 :          11 :         ObjectAddressSubSet(address, RelationRelationId,
    8365                 :             :                                                 RelationGetRelid(rel), attnum);
    8366                 :          11 :         heap_freetuple(tuple);
    8367                 :             : 
    8368                 :          11 :         table_close(attrelation, RowExclusiveLock);
    8369                 :             : 
    8370                 :             :         /*
    8371                 :             :          * Recurse to propagate the identity column to partitions.  Identity is
    8372                 :             :          * not inherited in regular inheritance children.
    8373                 :             :          */
    8374   [ +  -  +  + ]:          11 :         if (recurse && ispartitioned)
    8375                 :             :         {
    8376                 :           1 :                 List       *children;
    8377                 :           1 :                 ListCell   *lc;
    8378                 :             : 
    8379                 :           1 :                 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8380                 :             : 
    8381   [ +  -  +  +  :           2 :                 foreach(lc, children)
                   +  + ]
    8382                 :             :                 {
    8383                 :           1 :                         Relation        childrel;
    8384                 :             : 
    8385                 :           1 :                         childrel = table_open(lfirst_oid(lc), NoLock);
    8386                 :           1 :                         ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
    8387                 :           1 :                         table_close(childrel, NoLock);
    8388                 :           1 :                 }
    8389                 :           1 :         }
    8390                 :             : 
    8391                 :             :         return address;
    8392                 :          11 : }
    8393                 :             : 
    8394                 :             : /*
    8395                 :             :  * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
    8396                 :             :  *
    8397                 :             :  * Return the address of the affected column.
    8398                 :             :  */
    8399                 :             : static ObjectAddress
    8400                 :          12 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
    8401                 :             :                                   LOCKMODE lockmode, bool recurse, bool recursing)
    8402                 :             : {
    8403                 :          12 :         ListCell   *option;
    8404                 :          12 :         DefElem    *generatedEl = NULL;
    8405                 :          12 :         HeapTuple       tuple;
    8406                 :          12 :         Form_pg_attribute attTup;
    8407                 :          12 :         AttrNumber      attnum;
    8408                 :          12 :         Relation        attrelation;
    8409                 :             :         ObjectAddress address;
    8410                 :          12 :         bool            ispartitioned;
    8411                 :             : 
    8412                 :          12 :         ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8413   [ +  +  +  + ]:          12 :         if (ispartitioned && !recurse)
    8414   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    8415                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8416                 :             :                                  errmsg("cannot change identity column of only the partitioned table"),
    8417                 :             :                                  errhint("Do not specify the ONLY keyword.")));
    8418                 :             : 
    8419   [ +  +  +  + ]:          11 :         if (rel->rd_rel->relispartition && !recursing)
    8420   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    8421                 :             :                                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8422                 :             :                                 errmsg("cannot change identity column of a partition"));
    8423                 :             : 
    8424   [ +  +  +  +  :          16 :         foreach(option, castNode(List, def))
                   +  + ]
    8425                 :             :         {
    8426                 :           7 :                 DefElem    *defel = lfirst_node(DefElem, option);
    8427                 :             : 
    8428         [ +  - ]:           7 :                 if (strcmp(defel->defname, "generated") == 0)
    8429                 :             :                 {
    8430         [ +  - ]:           7 :                         if (generatedEl)
    8431   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
    8432                 :             :                                                 (errcode(ERRCODE_SYNTAX_ERROR),
    8433                 :             :                                                  errmsg("conflicting or redundant options")));
    8434                 :           7 :                         generatedEl = defel;
    8435                 :           7 :                 }
    8436                 :             :                 else
    8437   [ #  #  #  # ]:           0 :                         elog(ERROR, "option \"%s\" not recognized",
    8438                 :             :                                  defel->defname);
    8439                 :           7 :         }
    8440                 :             : 
    8441                 :             :         /*
    8442                 :             :          * Even if there is nothing to change here, we run all the checks.  There
    8443                 :             :          * will be a subsequent ALTER SEQUENCE that relies on everything being
    8444                 :             :          * there.
    8445                 :             :          */
    8446                 :             : 
    8447                 :           9 :         attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8448                 :           9 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8449         [ +  - ]:           9 :         if (!HeapTupleIsValid(tuple))
    8450   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    8451                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8452                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8453                 :             :                                                 colName, RelationGetRelationName(rel))));
    8454                 :             : 
    8455                 :           9 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8456                 :           9 :         attnum = attTup->attnum;
    8457                 :             : 
    8458         [ +  - ]:           9 :         if (attnum <= 0)
    8459   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    8460                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8461                 :             :                                  errmsg("cannot alter system column \"%s\"",
    8462                 :             :                                                 colName)));
    8463                 :             : 
    8464         [ +  + ]:           9 :         if (!attTup->attidentity)
    8465   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    8466                 :             :                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8467                 :             :                                  errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8468                 :             :                                                 colName, RelationGetRelationName(rel))));
    8469                 :             : 
    8470         [ +  + ]:           8 :         if (generatedEl)
    8471                 :             :         {
    8472                 :           7 :                 attTup->attidentity = defGetInt32(generatedEl);
    8473                 :           7 :                 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8474                 :             : 
    8475         [ +  - ]:           7 :                 InvokeObjectPostAlterHook(RelationRelationId,
    8476                 :             :                                                                   RelationGetRelid(rel),
    8477                 :             :                                                                   attTup->attnum);
    8478                 :           7 :                 ObjectAddressSubSet(address, RelationRelationId,
    8479                 :             :                                                         RelationGetRelid(rel), attnum);
    8480                 :           7 :         }
    8481                 :             :         else
    8482                 :           1 :                 address = InvalidObjectAddress;
    8483                 :             : 
    8484                 :           8 :         heap_freetuple(tuple);
    8485                 :           8 :         table_close(attrelation, RowExclusiveLock);
    8486                 :             : 
    8487                 :             :         /*
    8488                 :             :          * Recurse to propagate the identity change to partitions. Identity is not
    8489                 :             :          * inherited in regular inheritance children.
    8490                 :             :          */
    8491   [ +  +  +  -  :           8 :         if (generatedEl && recurse && ispartitioned)
                   +  + ]
    8492                 :             :         {
    8493                 :           1 :                 List       *children;
    8494                 :           1 :                 ListCell   *lc;
    8495                 :             : 
    8496                 :           1 :                 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8497                 :             : 
    8498   [ +  -  +  +  :           3 :                 foreach(lc, children)
                   +  + ]
    8499                 :             :                 {
    8500                 :           2 :                         Relation        childrel;
    8501                 :             : 
    8502                 :           2 :                         childrel = table_open(lfirst_oid(lc), NoLock);
    8503                 :           2 :                         ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
    8504                 :           2 :                         table_close(childrel, NoLock);
    8505                 :           2 :                 }
    8506                 :           1 :         }
    8507                 :             : 
    8508                 :             :         return address;
    8509                 :           8 : }
    8510                 :             : 
    8511                 :             : /*
    8512                 :             :  * ALTER TABLE ALTER COLUMN DROP IDENTITY
    8513                 :             :  *
    8514                 :             :  * Return the address of the affected column.
    8515                 :             :  */
    8516                 :             : static ObjectAddress
    8517                 :          15 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
    8518                 :             :                                    bool recurse, bool recursing)
    8519                 :             : {
    8520                 :          15 :         HeapTuple       tuple;
    8521                 :          15 :         Form_pg_attribute attTup;
    8522                 :          15 :         AttrNumber      attnum;
    8523                 :          15 :         Relation        attrelation;
    8524                 :          15 :         ObjectAddress address;
    8525                 :          15 :         Oid                     seqid;
    8526                 :          15 :         ObjectAddress seqaddress;
    8527                 :          15 :         bool            ispartitioned;
    8528                 :             : 
    8529                 :          15 :         ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8530   [ +  +  +  + ]:          15 :         if (ispartitioned && !recurse)
    8531   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    8532                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8533                 :             :                                  errmsg("cannot drop identity from a column of only the partitioned table"),
    8534                 :             :                                  errhint("Do not specify the ONLY keyword.")));
    8535                 :             : 
    8536   [ +  +  +  + ]:          14 :         if (rel->rd_rel->relispartition && !recursing)
    8537   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    8538                 :             :                                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8539                 :             :                                 errmsg("cannot drop identity from a column of a partition"));
    8540                 :             : 
    8541                 :          13 :         attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8542                 :          13 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8543         [ +  - ]:          13 :         if (!HeapTupleIsValid(tuple))
    8544   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    8545                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8546                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8547                 :             :                                                 colName, RelationGetRelationName(rel))));
    8548                 :             : 
    8549                 :          13 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8550                 :          13 :         attnum = attTup->attnum;
    8551                 :             : 
    8552         [ +  - ]:          13 :         if (attnum <= 0)
    8553   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    8554                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8555                 :             :                                  errmsg("cannot alter system column \"%s\"",
    8556                 :             :                                                 colName)));
    8557                 :             : 
    8558         [ +  + ]:          13 :         if (!attTup->attidentity)
    8559                 :             :         {
    8560         [ +  + ]:           2 :                 if (!missing_ok)
    8561   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    8562                 :             :                                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8563                 :             :                                          errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8564                 :             :                                                         colName, RelationGetRelationName(rel))));
    8565                 :             :                 else
    8566                 :             :                 {
    8567   [ -  +  +  - ]:           1 :                         ereport(NOTICE,
    8568                 :             :                                         (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
    8569                 :             :                                                         colName, RelationGetRelationName(rel))));
    8570                 :           1 :                         heap_freetuple(tuple);
    8571                 :           1 :                         table_close(attrelation, RowExclusiveLock);
    8572                 :           1 :                         return InvalidObjectAddress;
    8573                 :             :                 }
    8574                 :           0 :         }
    8575                 :             : 
    8576                 :          11 :         attTup->attidentity = '\0';
    8577                 :          11 :         CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8578                 :             : 
    8579         [ +  - ]:          11 :         InvokeObjectPostAlterHook(RelationRelationId,
    8580                 :             :                                                           RelationGetRelid(rel),
    8581                 :             :                                                           attTup->attnum);
    8582                 :          11 :         ObjectAddressSubSet(address, RelationRelationId,
    8583                 :             :                                                 RelationGetRelid(rel), attnum);
    8584                 :          11 :         heap_freetuple(tuple);
    8585                 :             : 
    8586                 :          11 :         table_close(attrelation, RowExclusiveLock);
    8587                 :             : 
    8588                 :             :         /*
    8589                 :             :          * Recurse to drop the identity from column in partitions.  Identity is
    8590                 :             :          * not inherited in regular inheritance children so ignore them.
    8591                 :             :          */
    8592   [ +  -  +  + ]:          11 :         if (recurse && ispartitioned)
    8593                 :             :         {
    8594                 :           1 :                 List       *children;
    8595                 :           1 :                 ListCell   *lc;
    8596                 :             : 
    8597                 :           1 :                 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8598                 :             : 
    8599   [ +  -  +  +  :           2 :                 foreach(lc, children)
                   +  + ]
    8600                 :             :                 {
    8601                 :           1 :                         Relation        childrel;
    8602                 :             : 
    8603                 :           1 :                         childrel = table_open(lfirst_oid(lc), NoLock);
    8604                 :           1 :                         ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
    8605                 :           1 :                         table_close(childrel, NoLock);
    8606                 :           1 :                 }
    8607                 :           1 :         }
    8608                 :             : 
    8609         [ +  + ]:          11 :         if (!recursing)
    8610                 :             :         {
    8611                 :             :                 /* drop the internal sequence */
    8612                 :           5 :                 seqid = getIdentitySequence(rel, attnum, false);
    8613                 :           5 :                 deleteDependencyRecordsForClass(RelationRelationId, seqid,
    8614                 :             :                                                                                 RelationRelationId, DEPENDENCY_INTERNAL);
    8615                 :           5 :                 CommandCounterIncrement();
    8616                 :           5 :                 seqaddress.classId = RelationRelationId;
    8617                 :           5 :                 seqaddress.objectId = seqid;
    8618                 :           5 :                 seqaddress.objectSubId = 0;
    8619                 :           5 :                 performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
    8620                 :           5 :         }
    8621                 :             : 
    8622                 :          11 :         return address;
    8623                 :          12 : }
    8624                 :             : 
    8625                 :             : /*
    8626                 :             :  * ALTER TABLE ALTER COLUMN SET EXPRESSION
    8627                 :             :  *
    8628                 :             :  * Return the address of the affected column.
    8629                 :             :  */
    8630                 :             : static ObjectAddress
    8631                 :          37 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
    8632                 :             :                                         Node *newExpr, LOCKMODE lockmode)
    8633                 :             : {
    8634                 :          37 :         HeapTuple       tuple;
    8635                 :          37 :         Form_pg_attribute attTup;
    8636                 :          37 :         AttrNumber      attnum;
    8637                 :          37 :         char            attgenerated;
    8638                 :          37 :         bool            rewrite;
    8639                 :          37 :         Oid                     attrdefoid;
    8640                 :             :         ObjectAddress address;
    8641                 :          37 :         Expr       *defval;
    8642                 :          37 :         NewColumnValue *newval;
    8643                 :          37 :         RawColumnDefault *rawEnt;
    8644                 :             : 
    8645                 :          37 :         tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8646         [ +  - ]:          37 :         if (!HeapTupleIsValid(tuple))
    8647   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    8648                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8649                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8650                 :             :                                                 colName, RelationGetRelationName(rel))));
    8651                 :             : 
    8652                 :          37 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8653                 :             : 
    8654                 :          37 :         attnum = attTup->attnum;
    8655         [ +  - ]:          37 :         if (attnum <= 0)
    8656   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    8657                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8658                 :             :                                  errmsg("cannot alter system column \"%s\"",
    8659                 :             :                                                 colName)));
    8660                 :             : 
    8661                 :          37 :         attgenerated = attTup->attgenerated;
    8662         [ +  + ]:          37 :         if (!attgenerated)
    8663   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    8664                 :             :                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8665                 :             :                                  errmsg("column \"%s\" of relation \"%s\" is not a generated column",
    8666                 :             :                                                 colName, RelationGetRelationName(rel))));
    8667                 :             : 
    8668                 :             :         /*
    8669                 :             :          * TODO: This could be done, just need to recheck any constraints
    8670                 :             :          * afterwards.
    8671                 :             :          */
    8672         [ +  + ]:          35 :         if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
    8673   [ +  -  +  + ]:          18 :                 rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
    8674   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    8675                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8676                 :             :                                  errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints"),
    8677                 :             :                                  errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
    8678                 :             :                                                    colName, RelationGetRelationName(rel))));
    8679                 :             : 
    8680   [ +  +  +  + ]:          33 :         if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
    8681                 :           4 :                 tab->verify_new_notnull = true;
    8682                 :             : 
    8683                 :             :         /*
    8684                 :             :          * We need to prevent this because a change of expression could affect a
    8685                 :             :          * row filter and inject expressions that are not permitted in a row
    8686                 :             :          * filter.  XXX We could try to have a more precise check to catch only
    8687                 :             :          * publications with row filters, or even re-verify the row filter
    8688                 :             :          * expressions.
    8689                 :             :          */
    8690   [ +  +  +  + ]:          33 :         if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
    8691                 :          16 :                 GetRelationPublications(RelationGetRelid(rel)) != NIL)
    8692   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    8693                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8694                 :             :                                  errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
    8695                 :             :                                  errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
    8696                 :             :                                                    colName, RelationGetRelationName(rel))));
    8697                 :             : 
    8698                 :          32 :         rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
    8699                 :             : 
    8700                 :          32 :         ReleaseSysCache(tuple);
    8701                 :             : 
    8702         [ +  + ]:          32 :         if (rewrite)
    8703                 :             :         {
    8704                 :             :                 /*
    8705                 :             :                  * Clear all the missing values if we're rewriting the table, since
    8706                 :             :                  * this renders them pointless.
    8707                 :             :                  */
    8708                 :          17 :                 RelationClearMissing(rel);
    8709                 :             : 
    8710                 :             :                 /* make sure we don't conflict with later attribute modifications */
    8711                 :          17 :                 CommandCounterIncrement();
    8712                 :             : 
    8713                 :             :                 /*
    8714                 :             :                  * Find everything that depends on the column (constraints, indexes,
    8715                 :             :                  * etc), and record enough information to let us recreate the objects
    8716                 :             :                  * after rewrite.
    8717                 :             :                  */
    8718                 :          17 :                 RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
    8719                 :          17 :         }
    8720                 :             : 
    8721                 :             :         /*
    8722                 :             :          * Drop the dependency records of the GENERATED expression, in particular
    8723                 :             :          * its INTERNAL dependency on the column, which would otherwise cause
    8724                 :             :          * dependency.c to refuse to perform the deletion.
    8725                 :             :          */
    8726                 :          32 :         attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    8727         [ +  - ]:          32 :         if (!OidIsValid(attrdefoid))
    8728   [ #  #  #  # ]:           0 :                 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    8729                 :             :                          RelationGetRelid(rel), attnum);
    8730                 :          32 :         (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    8731                 :             : 
    8732                 :             :         /* Make above changes visible */
    8733                 :          32 :         CommandCounterIncrement();
    8734                 :             : 
    8735                 :             :         /*
    8736                 :             :          * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    8737                 :             :          * safety, but at present we do not expect anything to depend on the
    8738                 :             :          * expression.
    8739                 :             :          */
    8740                 :          32 :         RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    8741                 :             :                                           false, false);
    8742                 :             : 
    8743                 :             :         /* Prepare to store the new expression, in the catalogs */
    8744                 :          32 :         rawEnt = palloc_object(RawColumnDefault);
    8745                 :          32 :         rawEnt->attnum = attnum;
    8746                 :          32 :         rawEnt->raw_default = newExpr;
    8747                 :          32 :         rawEnt->generated = attgenerated;
    8748                 :             : 
    8749                 :             :         /* Store the generated expression */
    8750                 :          32 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    8751                 :             :                                                           false, true, false, NULL);
    8752                 :             : 
    8753                 :             :         /* Make above new expression visible */
    8754                 :          32 :         CommandCounterIncrement();
    8755                 :             : 
    8756         [ +  + ]:          32 :         if (rewrite)
    8757                 :             :         {
    8758                 :             :                 /* Prepare for table rewrite */
    8759                 :          17 :                 defval = (Expr *) build_column_default(rel, attnum);
    8760                 :             : 
    8761                 :          17 :                 newval = palloc0_object(NewColumnValue);
    8762                 :          17 :                 newval->attnum = attnum;
    8763                 :          17 :                 newval->expr = expression_planner(defval);
    8764                 :          17 :                 newval->is_generated = true;
    8765                 :             : 
    8766                 :          17 :                 tab->newvals = lappend(tab->newvals, newval);
    8767                 :          17 :                 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    8768                 :          17 :         }
    8769                 :             : 
    8770                 :             :         /* Drop any pg_statistic entry for the column */
    8771                 :          32 :         RemoveStatistics(RelationGetRelid(rel), attnum);
    8772                 :             : 
    8773         [ +  - ]:          32 :         InvokeObjectPostAlterHook(RelationRelationId,
    8774                 :             :                                                           RelationGetRelid(rel), attnum);
    8775                 :             : 
    8776                 :          32 :         ObjectAddressSubSet(address, RelationRelationId,
    8777                 :             :                                                 RelationGetRelid(rel), attnum);
    8778                 :             :         return address;
    8779                 :          32 : }
    8780                 :             : 
    8781                 :             : /*
    8782                 :             :  * ALTER TABLE ALTER COLUMN DROP EXPRESSION
    8783                 :             :  */
    8784                 :             : static void
    8785                 :          14 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
    8786                 :             : {
    8787                 :             :         /*
    8788                 :             :          * Reject ONLY if there are child tables.  We could implement this, but it
    8789                 :             :          * is a bit complicated.  GENERATED clauses must be attached to the column
    8790                 :             :          * definition and cannot be added later like DEFAULT, so if a child table
    8791                 :             :          * has a generation expression that the parent does not have, the child
    8792                 :             :          * column will necessarily be an attislocal column.  So to implement ONLY
    8793                 :             :          * here, we'd need extra code to update attislocal of the direct child
    8794                 :             :          * tables, somewhat similar to how DROP COLUMN does it, so that the
    8795                 :             :          * resulting state can be properly dumped and restored.
    8796                 :             :          */
    8797   [ +  +  +  + ]:          14 :         if (!recurse &&
    8798                 :           4 :                 find_inheritance_children(RelationGetRelid(rel), lockmode))
    8799   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    8800                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8801                 :             :                                  errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
    8802                 :             : 
    8803                 :             :         /*
    8804                 :             :          * Cannot drop generation expression from inherited columns.
    8805                 :             :          */
    8806         [ +  + ]:          12 :         if (!recursing)
    8807                 :             :         {
    8808                 :          10 :                 HeapTuple       tuple;
    8809                 :          10 :                 Form_pg_attribute attTup;
    8810                 :             : 
    8811                 :          10 :                 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
    8812         [ +  - ]:          10 :                 if (!HeapTupleIsValid(tuple))
    8813   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    8814                 :             :                                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    8815                 :             :                                          errmsg("column \"%s\" of relation \"%s\" does not exist",
    8816                 :             :                                                         cmd->name, RelationGetRelationName(rel))));
    8817                 :             : 
    8818                 :          10 :                 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8819                 :             : 
    8820         [ +  + ]:          10 :                 if (attTup->attinhcount > 0)
    8821   [ +  -  +  - ]:           2 :                         ereport(ERROR,
    8822                 :             :                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8823                 :             :                                          errmsg("cannot drop generation expression from inherited column")));
    8824                 :           8 :         }
    8825                 :          10 : }
    8826                 :             : 
    8827                 :             : /*
    8828                 :             :  * Return the address of the affected column.
    8829                 :             :  */
    8830                 :             : static ObjectAddress
    8831                 :           9 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
    8832                 :             : {
    8833                 :           9 :         HeapTuple       tuple;
    8834                 :           9 :         Form_pg_attribute attTup;
    8835                 :           9 :         AttrNumber      attnum;
    8836                 :           9 :         Relation        attrelation;
    8837                 :           9 :         Oid                     attrdefoid;
    8838                 :           9 :         ObjectAddress address;
    8839                 :             : 
    8840                 :           9 :         attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8841                 :           9 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8842         [ +  - ]:           9 :         if (!HeapTupleIsValid(tuple))
    8843   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    8844                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8845                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8846                 :             :                                                 colName, RelationGetRelationName(rel))));
    8847                 :             : 
    8848                 :           9 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8849                 :           9 :         attnum = attTup->attnum;
    8850                 :             : 
    8851         [ +  - ]:           9 :         if (attnum <= 0)
    8852   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    8853                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8854                 :             :                                  errmsg("cannot alter system column \"%s\"",
    8855                 :             :                                                 colName)));
    8856                 :             : 
    8857                 :             :         /*
    8858                 :             :          * TODO: This could be done, but it would need a table rewrite to
    8859                 :             :          * materialize the generated values.  Note that for the time being, we
    8860                 :             :          * still error with missing_ok, so that we don't silently leave the column
    8861                 :             :          * as generated.
    8862                 :             :          */
    8863         [ +  + ]:           9 :         if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
    8864   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    8865                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8866                 :             :                                  errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
    8867                 :             :                                  errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
    8868                 :             :                                                    colName, RelationGetRelationName(rel))));
    8869                 :             : 
    8870         [ +  + ]:           7 :         if (!attTup->attgenerated)
    8871                 :             :         {
    8872         [ +  + ]:           4 :                 if (!missing_ok)
    8873   [ +  -  +  - ]:           2 :                         ereport(ERROR,
    8874                 :             :                                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8875                 :             :                                          errmsg("column \"%s\" of relation \"%s\" is not a generated column",
    8876                 :             :                                                         colName, RelationGetRelationName(rel))));
    8877                 :             :                 else
    8878                 :             :                 {
    8879   [ -  +  +  - ]:           2 :                         ereport(NOTICE,
    8880                 :             :                                         (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
    8881                 :             :                                                         colName, RelationGetRelationName(rel))));
    8882                 :           2 :                         heap_freetuple(tuple);
    8883                 :           2 :                         table_close(attrelation, RowExclusiveLock);
    8884                 :           2 :                         return InvalidObjectAddress;
    8885                 :             :                 }
    8886                 :           0 :         }
    8887                 :             : 
    8888                 :             :         /*
    8889                 :             :          * Mark the column as no longer generated.  (The atthasdef flag needs to
    8890                 :             :          * get cleared too, but RemoveAttrDefault will handle that.)
    8891                 :             :          */
    8892                 :           3 :         attTup->attgenerated = '\0';
    8893                 :           3 :         CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8894                 :             : 
    8895         [ +  - ]:           3 :         InvokeObjectPostAlterHook(RelationRelationId,
    8896                 :             :                                                           RelationGetRelid(rel),
    8897                 :             :                                                           attnum);
    8898                 :           3 :         heap_freetuple(tuple);
    8899                 :             : 
    8900                 :           3 :         table_close(attrelation, RowExclusiveLock);
    8901                 :             : 
    8902                 :             :         /*
    8903                 :             :          * Drop the dependency records of the GENERATED expression, in particular
    8904                 :             :          * its INTERNAL dependency on the column, which would otherwise cause
    8905                 :             :          * dependency.c to refuse to perform the deletion.
    8906                 :             :          */
    8907                 :           3 :         attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    8908         [ +  - ]:           3 :         if (!OidIsValid(attrdefoid))
    8909   [ #  #  #  # ]:           0 :                 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    8910                 :             :                          RelationGetRelid(rel), attnum);
    8911                 :           3 :         (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    8912                 :             : 
    8913                 :             :         /* Make above changes visible */
    8914                 :           3 :         CommandCounterIncrement();
    8915                 :             : 
    8916                 :             :         /*
    8917                 :             :          * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    8918                 :             :          * safety, but at present we do not expect anything to depend on the
    8919                 :             :          * default.
    8920                 :             :          */
    8921                 :           3 :         RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    8922                 :             :                                           false, false);
    8923                 :             : 
    8924                 :           3 :         ObjectAddressSubSet(address, RelationRelationId,
    8925                 :             :                                                 RelationGetRelid(rel), attnum);
    8926                 :           3 :         return address;
    8927                 :           5 : }
    8928                 :             : 
    8929                 :             : /*
    8930                 :             :  * ALTER TABLE ALTER COLUMN SET STATISTICS
    8931                 :             :  *
    8932                 :             :  * Return value is the address of the modified column
    8933                 :             :  */
    8934                 :             : static ObjectAddress
    8935                 :          25 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
    8936                 :             : {
    8937                 :          25 :         int                     newtarget = 0;
    8938                 :          25 :         bool            newtarget_default;
    8939                 :          25 :         Relation        attrelation;
    8940                 :          25 :         HeapTuple       tuple,
    8941                 :             :                                 newtuple;
    8942                 :          25 :         Form_pg_attribute attrtuple;
    8943                 :          25 :         AttrNumber      attnum;
    8944                 :             :         ObjectAddress address;
    8945                 :          25 :         Datum           repl_val[Natts_pg_attribute];
    8946                 :          25 :         bool            repl_null[Natts_pg_attribute];
    8947                 :          25 :         bool            repl_repl[Natts_pg_attribute];
    8948                 :             : 
    8949                 :             :         /*
    8950                 :             :          * We allow referencing columns by numbers only for indexes, since table
    8951                 :             :          * column numbers could contain gaps if columns are later dropped.
    8952                 :             :          */
    8953         [ +  + ]:          25 :         if (rel->rd_rel->relkind != RELKIND_INDEX &&
    8954   [ +  -  +  - ]:          15 :                 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    8955                 :          15 :                 !colName)
    8956   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    8957                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8958                 :             :                                  errmsg("cannot refer to non-index column by number")));
    8959                 :             : 
    8960                 :             :         /* -1 was used in previous versions for the default setting */
    8961   [ +  -  +  + ]:          25 :         if (newValue && intVal(newValue) != -1)
    8962                 :             :         {
    8963                 :          18 :                 newtarget = intVal(newValue);
    8964                 :          18 :                 newtarget_default = false;
    8965                 :          18 :         }
    8966                 :             :         else
    8967                 :           7 :                 newtarget_default = true;
    8968                 :             : 
    8969         [ +  + ]:          25 :         if (!newtarget_default)
    8970                 :             :         {
    8971                 :             :                 /*
    8972                 :             :                  * Limit target to a sane range
    8973                 :             :                  */
    8974         [ +  - ]:          18 :                 if (newtarget < 0)
    8975                 :             :                 {
    8976   [ #  #  #  # ]:           0 :                         ereport(ERROR,
    8977                 :             :                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    8978                 :             :                                          errmsg("statistics target %d is too low",
    8979                 :             :                                                         newtarget)));
    8980                 :           0 :                 }
    8981         [ +  - ]:          18 :                 else if (newtarget > MAX_STATISTICS_TARGET)
    8982                 :             :                 {
    8983                 :           0 :                         newtarget = MAX_STATISTICS_TARGET;
    8984   [ #  #  #  # ]:           0 :                         ereport(WARNING,
    8985                 :             :                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    8986                 :             :                                          errmsg("lowering statistics target to %d",
    8987                 :             :                                                         newtarget)));
    8988                 :           0 :                 }
    8989                 :          18 :         }
    8990                 :             : 
    8991                 :          25 :         attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8992                 :             : 
    8993         [ +  + ]:          25 :         if (colName)
    8994                 :             :         {
    8995                 :          15 :                 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8996                 :             : 
    8997         [ +  + ]:          15 :                 if (!HeapTupleIsValid(tuple))
    8998   [ +  -  +  - ]:           2 :                         ereport(ERROR,
    8999                 :             :                                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    9000                 :             :                                          errmsg("column \"%s\" of relation \"%s\" does not exist",
    9001                 :             :                                                         colName, RelationGetRelationName(rel))));
    9002                 :          13 :         }
    9003                 :             :         else
    9004                 :             :         {
    9005                 :          10 :                 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
    9006                 :             : 
    9007         [ +  + ]:          10 :                 if (!HeapTupleIsValid(tuple))
    9008   [ +  -  +  - ]:           2 :                         ereport(ERROR,
    9009                 :             :                                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    9010                 :             :                                          errmsg("column number %d of relation \"%s\" does not exist",
    9011                 :             :                                                         colNum, RelationGetRelationName(rel))));
    9012                 :             :         }
    9013                 :             : 
    9014                 :          21 :         attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9015                 :             : 
    9016                 :          21 :         attnum = attrtuple->attnum;
    9017         [ +  - ]:          21 :         if (attnum <= 0)
    9018   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    9019                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9020                 :             :                                  errmsg("cannot alter system column \"%s\"",
    9021                 :             :                                                 colName)));
    9022                 :             : 
    9023                 :             :         /*
    9024                 :             :          * Prevent this as long as the ANALYZE code skips virtual generated
    9025                 :             :          * columns.
    9026                 :             :          */
    9027         [ +  - ]:          21 :         if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
    9028   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    9029                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9030                 :             :                                  errmsg("cannot alter statistics on virtual generated column \"%s\"",
    9031                 :             :                                                 colName)));
    9032                 :             : 
    9033   [ +  +  -  + ]:          21 :         if (rel->rd_rel->relkind == RELKIND_INDEX ||
    9034                 :          13 :                 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    9035                 :             :         {
    9036         [ +  + ]:           8 :                 if (attnum > rel->rd_index->indnkeyatts)
    9037   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    9038                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9039                 :             :                                          errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
    9040                 :             :                                                         NameStr(attrtuple->attname), RelationGetRelationName(rel))));
    9041         [ +  + ]:           7 :                 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
    9042   [ +  -  +  - ]:           3 :                         ereport(ERROR,
    9043                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9044                 :             :                                          errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
    9045                 :             :                                                         NameStr(attrtuple->attname), RelationGetRelationName(rel)),
    9046                 :             :                                          errhint("Alter statistics on table column instead.")));
    9047                 :           4 :         }
    9048                 :             : 
    9049                 :             :         /* Build new tuple. */
    9050                 :          17 :         memset(repl_null, false, sizeof(repl_null));
    9051                 :          17 :         memset(repl_repl, false, sizeof(repl_repl));
    9052         [ +  + ]:          17 :         if (!newtarget_default)
    9053                 :          10 :                 repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
    9054                 :             :         else
    9055                 :           7 :                 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
    9056                 :          17 :         repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
    9057                 :          34 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    9058                 :          17 :                                                                  repl_val, repl_null, repl_repl);
    9059                 :          17 :         CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
    9060                 :             : 
    9061         [ +  - ]:          17 :         InvokeObjectPostAlterHook(RelationRelationId,
    9062                 :             :                                                           RelationGetRelid(rel),
    9063                 :             :                                                           attrtuple->attnum);
    9064                 :          17 :         ObjectAddressSubSet(address, RelationRelationId,
    9065                 :             :                                                 RelationGetRelid(rel), attnum);
    9066                 :             : 
    9067                 :          17 :         heap_freetuple(newtuple);
    9068                 :             : 
    9069                 :          17 :         ReleaseSysCache(tuple);
    9070                 :             : 
    9071                 :          17 :         table_close(attrelation, RowExclusiveLock);
    9072                 :             : 
    9073                 :             :         return address;
    9074                 :          17 : }
    9075                 :             : 
    9076                 :             : /*
    9077                 :             :  * Return value is the address of the modified column
    9078                 :             :  */
    9079                 :             : static ObjectAddress
    9080                 :           5 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
    9081                 :             :                                  bool isReset, LOCKMODE lockmode)
    9082                 :             : {
    9083                 :           5 :         Relation        attrelation;
    9084                 :           5 :         HeapTuple       tuple,
    9085                 :             :                                 newtuple;
    9086                 :           5 :         Form_pg_attribute attrtuple;
    9087                 :           5 :         AttrNumber      attnum;
    9088                 :           5 :         Datum           datum,
    9089                 :             :                                 newOptions;
    9090                 :           5 :         bool            isnull;
    9091                 :             :         ObjectAddress address;
    9092                 :           5 :         Datum           repl_val[Natts_pg_attribute];
    9093                 :           5 :         bool            repl_null[Natts_pg_attribute];
    9094                 :           5 :         bool            repl_repl[Natts_pg_attribute];
    9095                 :             : 
    9096                 :           5 :         attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    9097                 :             : 
    9098                 :           5 :         tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    9099                 :             : 
    9100         [ +  - ]:           5 :         if (!HeapTupleIsValid(tuple))
    9101   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    9102                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    9103                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    9104                 :             :                                                 colName, RelationGetRelationName(rel))));
    9105                 :           5 :         attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9106                 :             : 
    9107                 :           5 :         attnum = attrtuple->attnum;
    9108         [ +  - ]:           5 :         if (attnum <= 0)
    9109   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    9110                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9111                 :             :                                  errmsg("cannot alter system column \"%s\"",
    9112                 :             :                                                 colName)));
    9113                 :             : 
    9114                 :             :         /* Generate new proposed attoptions (text array) */
    9115                 :           5 :         datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
    9116                 :             :                                                         &isnull);
    9117         [ +  + ]:           5 :         newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
    9118                 :           5 :                                                                          castNode(List, options), NULL, NULL,
    9119                 :           5 :                                                                          false, isReset);
    9120                 :             :         /* Validate new options */
    9121                 :           5 :         (void) attribute_reloptions(newOptions, true);
    9122                 :             : 
    9123                 :             :         /* Build new tuple. */
    9124                 :           5 :         memset(repl_null, false, sizeof(repl_null));
    9125                 :           5 :         memset(repl_repl, false, sizeof(repl_repl));
    9126         [ +  - ]:           5 :         if (newOptions != (Datum) 0)
    9127                 :           5 :                 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
    9128                 :             :         else
    9129                 :           0 :                 repl_null[Anum_pg_attribute_attoptions - 1] = true;
    9130                 :           5 :         repl_repl[Anum_pg_attribute_attoptions - 1] = true;
    9131                 :          10 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    9132                 :           5 :                                                                  repl_val, repl_null, repl_repl);
    9133                 :             : 
    9134                 :             :         /* Update system catalog. */
    9135                 :           5 :         CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
    9136                 :             : 
    9137         [ +  - ]:           5 :         InvokeObjectPostAlterHook(RelationRelationId,
    9138                 :             :                                                           RelationGetRelid(rel),
    9139                 :             :                                                           attrtuple->attnum);
    9140                 :           5 :         ObjectAddressSubSet(address, RelationRelationId,
    9141                 :             :                                                 RelationGetRelid(rel), attnum);
    9142                 :             : 
    9143                 :           5 :         heap_freetuple(newtuple);
    9144                 :             : 
    9145                 :           5 :         ReleaseSysCache(tuple);
    9146                 :             : 
    9147                 :           5 :         table_close(attrelation, RowExclusiveLock);
    9148                 :             : 
    9149                 :             :         return address;
    9150                 :           5 : }
    9151                 :             : 
    9152                 :             : /*
    9153                 :             :  * Helper function for ATExecSetStorage and ATExecSetCompression
    9154                 :             :  *
    9155                 :             :  * Set the attstorage and/or attcompression fields for index columns
    9156                 :             :  * associated with the specified table column.
    9157                 :             :  */
    9158                 :             : static void
    9159                 :          36 : SetIndexStorageProperties(Relation rel, Relation attrelation,
    9160                 :             :                                                   AttrNumber attnum,
    9161                 :             :                                                   bool setstorage, char newstorage,
    9162                 :             :                                                   bool setcompression, char newcompression,
    9163                 :             :                                                   LOCKMODE lockmode)
    9164                 :             : {
    9165                 :          36 :         ListCell   *lc;
    9166                 :             : 
    9167   [ +  +  +  +  :          47 :         foreach(lc, RelationGetIndexList(rel))
                   +  + ]
    9168                 :             :         {
    9169                 :          11 :                 Oid                     indexoid = lfirst_oid(lc);
    9170                 :          11 :                 Relation        indrel;
    9171                 :          11 :                 AttrNumber      indattnum = 0;
    9172                 :          11 :                 HeapTuple       tuple;
    9173                 :             : 
    9174                 :          11 :                 indrel = index_open(indexoid, lockmode);
    9175                 :             : 
    9176         [ +  + ]:          23 :                 for (int i = 0; i < indrel->rd_index->indnatts; i++)
    9177                 :             :                 {
    9178         [ +  + ]:          12 :                         if (indrel->rd_index->indkey.values[i] == attnum)
    9179                 :             :                         {
    9180                 :           5 :                                 indattnum = i + 1;
    9181                 :           5 :                                 break;
    9182                 :             :                         }
    9183                 :           7 :                 }
    9184                 :             : 
    9185         [ +  + ]:          11 :                 if (indattnum == 0)
    9186                 :             :                 {
    9187                 :           6 :                         index_close(indrel, lockmode);
    9188                 :           6 :                         continue;
    9189                 :             :                 }
    9190                 :             : 
    9191                 :           5 :                 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
    9192                 :             : 
    9193         [ +  - ]:           5 :                 if (HeapTupleIsValid(tuple))
    9194                 :             :                 {
    9195                 :           5 :                         Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9196                 :             : 
    9197         [ +  + ]:           5 :                         if (setstorage)
    9198                 :           3 :                                 attrtuple->attstorage = newstorage;
    9199                 :             : 
    9200         [ +  + ]:           5 :                         if (setcompression)
    9201                 :           2 :                                 attrtuple->attcompression = newcompression;
    9202                 :             : 
    9203                 :           5 :                         CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    9204                 :             : 
    9205         [ +  - ]:           5 :                         InvokeObjectPostAlterHook(RelationRelationId,
    9206                 :             :                                                                           RelationGetRelid(rel),
    9207                 :             :                                                                           attrtuple->attnum);
    9208                 :             : 
    9209                 :           5 :                         heap_freetuple(tuple);
    9210                 :           5 :                 }
    9211                 :             : 
    9212                 :           5 :                 index_close(indrel, lockmode);
    9213      [ +  -  + ]:          11 :         }
    9214                 :          36 : }
    9215                 :             : 
    9216                 :             : /*
    9217                 :             :  * ALTER TABLE ALTER COLUMN SET STORAGE
    9218                 :             :  *
    9219                 :             :  * Return value is the address of the modified column
    9220                 :             :  */
    9221                 :             : static ObjectAddress
    9222                 :          31 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
    9223                 :             : {
    9224                 :          31 :         Relation        attrelation;
    9225                 :          31 :         HeapTuple       tuple;
    9226                 :          31 :         Form_pg_attribute attrtuple;
    9227                 :          31 :         AttrNumber      attnum;
    9228                 :             :         ObjectAddress address;
    9229                 :             : 
    9230                 :          31 :         attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    9231                 :             : 
    9232                 :          31 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    9233                 :             : 
    9234         [ +  + ]:          31 :         if (!HeapTupleIsValid(tuple))
    9235   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    9236                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    9237                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    9238                 :             :                                                 colName, RelationGetRelationName(rel))));
    9239                 :          29 :         attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9240                 :             : 
    9241                 :          29 :         attnum = attrtuple->attnum;
    9242         [ +  - ]:          29 :         if (attnum <= 0)
    9243   [ #  #  #  # ]:           0 :                 ereport(ERROR,
    9244                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9245                 :             :                                  errmsg("cannot alter system column \"%s\"",
    9246                 :             :                                                 colName)));
    9247                 :             : 
    9248                 :          29 :         attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
    9249                 :             : 
    9250                 :          29 :         CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    9251                 :             : 
    9252         [ +  - ]:          29 :         InvokeObjectPostAlterHook(RelationRelationId,
    9253                 :             :                                                           RelationGetRelid(rel),
    9254                 :             :                                                           attrtuple->attnum);
    9255                 :             : 
    9256                 :             :         /*
    9257                 :             :          * Apply the change to indexes as well (only for simple index columns,
    9258                 :             :          * matching behavior of index.c ConstructTupleDescriptor()).
    9259                 :             :          */
    9260                 :          58 :         SetIndexStorageProperties(rel, attrelation, attnum,
    9261                 :          29 :                                                           true, attrtuple->attstorage,
    9262                 :             :                                                           false, 0,
    9263                 :          29 :                                                           lockmode);
    9264                 :             : 
    9265                 :          29 :         heap_freetuple(tuple);
    9266                 :             : 
    9267                 :          29 :         table_close(attrelation, RowExclusiveLock);
    9268                 :             : 
    9269                 :          29 :         ObjectAddressSubSet(address, RelationRelationId,
    9270                 :             :                                                 RelationGetRelid(rel), attnum);
    9271                 :             :         return address;
    9272                 :          29 : }
    9273                 :             : 
    9274                 :             : 
    9275                 :             : /*
    9276                 :             :  * ALTER TABLE DROP COLUMN
    9277                 :             :  *
    9278                 :             :  * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
    9279                 :             :  * because we have to decide at runtime whether to recurse or not depending
    9280                 :             :  * on whether attinhcount goes to zero or not.  (We can't check this in a
    9281                 :             :  * static pre-pass because it won't handle multiple inheritance situations
    9282                 :             :  * correctly.)
    9283                 :             :  */
    9284                 :             : static void
    9285                 :         237 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    9286                 :             :                                  AlterTableCmd *cmd, LOCKMODE lockmode,
    9287                 :             :                                  AlterTableUtilityContext *context)
    9288                 :             : {
    9289   [ +  +  +  + ]:         237 :         if (rel->rd_rel->reloftype && !recursing)
    9290   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    9291                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9292                 :             :                                  errmsg("cannot drop column from typed table")));
    9293                 :             : 
    9294         [ +  + ]:         236 :         if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    9295                 :          12 :                 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    9296                 :             : 
    9297         [ +  + ]:         236 :         if (recurse)
    9298                 :         218 :                 cmd->recurse = true;
    9299                 :         236 : }
    9300                 :             : 
    9301                 :             : /*
    9302                 :             :  * Drops column 'colName' from relation 'rel' and returns the address of the
    9303                 :             :  * dropped column.  The column is also dropped (or marked as no longer
    9304                 :             :  * inherited from relation) from the relation's inheritance children, if any.
    9305                 :             :  *
    9306                 :             :  * In the recursive invocations for inheritance child relations, instead of
    9307                 :             :  * dropping the column directly (if to be dropped at all), its object address
    9308                 :             :  * is added to 'addrs', which must be non-NULL in such invocations.  All
    9309                 :             :  * columns are dropped at the same time after all the children have been
    9310                 :             :  * checked recursively.
    9311                 :             :  */
    9312                 :             : static ObjectAddress
    9313                 :         317 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
    9314                 :             :                                  DropBehavior behavior,
    9315                 :             :                                  bool recurse, bool recursing,
    9316                 :             :                                  bool missing_ok, LOCKMODE lockmode,
    9317                 :             :                                  ObjectAddresses *addrs)
    9318                 :             : {
    9319                 :         317 :         HeapTuple       tuple;
    9320                 :         317 :         Form_pg_attribute targetatt;
    9321                 :         317 :         AttrNumber      attnum;
    9322                 :         317 :         List       *children;
    9323                 :         317 :         ObjectAddress object;
    9324                 :         317 :         bool            is_expr;
    9325                 :             : 
    9326                 :             :         /* At top level, permission check was done in ATPrepCmd, else do it */
    9327         [ +  + ]:         317 :         if (recursing)
    9328                 :          92 :                 ATSimplePermissions(AT_DropColumn, rel,
    9329                 :             :                                                         ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    9330                 :             : 
    9331                 :             :         /* Initialize addrs on the first invocation */
    9332   [ +  +  +  - ]:         317 :         Assert(!recursing || addrs != NULL);
    9333                 :             : 
    9334                 :             :         /* since this function recurses, it could be driven to stack overflow */
    9335                 :         317 :         check_stack_depth();
    9336                 :             : 
    9337         [ +  + ]:         317 :         if (!recursing)
    9338                 :         235 :                 addrs = new_object_addresses();
    9339                 :             : 
    9340                 :             :         /*
    9341                 :             :          * get the number of the attribute
    9342                 :             :          */
    9343                 :         317 :         tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    9344         [ +  + ]:         317 :         if (!HeapTupleIsValid(tuple))
    9345                 :             :         {
    9346         [ +  + ]:           9 :                 if (!missing_ok)
    9347                 :             :                 {
    9348   [ +  -  +  - ]:           6 :                         ereport(ERROR,
    9349                 :             :                                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    9350                 :             :                                          errmsg("column \"%s\" of relation \"%s\" does not exist",
    9351                 :             :                                                         colName, RelationGetRelationName(rel))));
    9352                 :           0 :                 }
    9353                 :             :                 else
    9354                 :             :                 {
    9355   [ -  +  +  - ]:           3 :                         ereport(NOTICE,
    9356                 :             :                                         (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
    9357                 :             :                                                         colName, RelationGetRelationName(rel))));
    9358                 :           3 :                         return InvalidObjectAddress;
    9359                 :             :                 }
    9360                 :           0 :         }
    9361                 :         308 :         targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
    9362                 :             : 
    9363                 :         308 :         attnum = targetatt->attnum;
    9364                 :             : 
    9365                 :             :         /* Can't drop a system attribute */
    9366         [ +  + ]:         308 :         if (attnum <= 0)
    9367   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    9368                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9369                 :             :                                  errmsg("cannot drop system column \"%s\"",
    9370                 :             :                                                 colName)));
    9371                 :             : 
    9372                 :             :         /*
    9373                 :             :          * Don't drop inherited columns, unless recursing (presumably from a drop
    9374                 :             :          * of the parent column)
    9375                 :             :          */
    9376   [ +  +  +  + ]:         307 :         if (targetatt->attinhcount > 0 && !recursing)
    9377   [ +  -  +  - ]:           8 :                 ereport(ERROR,
    9378                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9379                 :             :                                  errmsg("cannot drop inherited column \"%s\"",
    9380                 :             :                                                 colName)));
    9381                 :             : 
    9382                 :             :         /*
    9383                 :             :          * Don't drop columns used in the partition key, either.  (If we let this
    9384                 :             :          * go through, the key column's dependencies would cause a cascaded drop
    9385                 :             :          * of the whole table, which is surely not what the user expected.)
    9386                 :             :          */
    9387   [ +  +  +  + ]:         598 :         if (has_partition_attrs(rel,
    9388                 :         299 :                                                         bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
    9389                 :             :                                                         &is_expr))
    9390   [ +  -  +  - ]:           5 :                 ereport(ERROR,
    9391                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9392                 :             :                                  errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
    9393                 :             :                                                 colName, RelationGetRelationName(rel))));
    9394                 :             : 
    9395                 :         294 :         ReleaseSysCache(tuple);
    9396                 :             : 
    9397                 :             :         /*
    9398                 :             :          * Propagate to children as appropriate.  Unlike most other ALTER
    9399                 :             :          * routines, we have to do this one level of recursion at a time; we can't
    9400                 :             :          * use find_all_inheritors to do it in one pass.
    9401                 :             :          */
    9402                 :         294 :         children =
    9403                 :         294 :                 find_inheritance_children(RelationGetRelid(rel), lockmode);
    9404                 :             : 
    9405         [ +  + ]:         294 :         if (children)
    9406                 :             :         {
    9407                 :          49 :                 Relation        attr_rel;
    9408                 :          49 :                 ListCell   *child;
    9409                 :             : 
    9410                 :             :                 /*
    9411                 :             :                  * In case of a partitioned table, the column must be dropped from the
    9412                 :             :                  * partitions as well.
    9413                 :             :                  */
    9414   [ +  +  +  + ]:          49 :                 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
    9415   [ +  -  +  - ]:           1 :                         ereport(ERROR,
    9416                 :             :                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9417                 :             :                                          errmsg("cannot drop column from only the partitioned table when partitions exist"),
    9418                 :             :                                          errhint("Do not specify the ONLY keyword.")));
    9419                 :             : 
    9420                 :          48 :                 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    9421   [ +  -  +  +  :         146 :                 foreach(child, children)
                   +  + ]
    9422                 :             :                 {
    9423                 :          98 :                         Oid                     childrelid = lfirst_oid(child);
    9424                 :          98 :                         Relation        childrel;
    9425                 :          98 :                         Form_pg_attribute childatt;
    9426                 :             : 
    9427                 :             :                         /* find_inheritance_children already got lock */
    9428                 :          98 :                         childrel = table_open(childrelid, NoLock);
    9429                 :          98 :                         CheckAlterTableIsSafe(childrel);
    9430                 :             : 
    9431                 :          98 :                         tuple = SearchSysCacheCopyAttName(childrelid, colName);
    9432         [ +  - ]:          98 :                         if (!HeapTupleIsValid(tuple))   /* shouldn't happen */
    9433   [ #  #  #  # ]:           0 :                                 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
    9434                 :             :                                          colName, childrelid);
    9435                 :          98 :                         childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    9436                 :             : 
    9437         [ +  - ]:          98 :                         if (childatt->attinhcount <= 0) /* shouldn't happen */
    9438   [ #  #  #  # ]:           0 :                                 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
    9439                 :             :                                          childrelid, colName);
    9440                 :             : 
    9441         [ +  + ]:          98 :                         if (recurse)
    9442                 :             :                         {
    9443                 :             :                                 /*
    9444                 :             :                                  * If the child column has other definition sources, just
    9445                 :             :                                  * decrement its inheritance count; if not, recurse to delete
    9446                 :             :                                  * it.
    9447                 :             :                                  */
    9448   [ +  -  +  + ]:          94 :                                 if (childatt->attinhcount == 1 && !childatt->attislocal)
    9449                 :             :                                 {
    9450                 :             :                                         /* Time to delete this child column, too */
    9451                 :         184 :                                         ATExecDropColumn(wqueue, childrel, colName,
    9452                 :          92 :                                                                          behavior, true, true,
    9453                 :          92 :                                                                          false, lockmode, addrs);
    9454                 :          92 :                                 }
    9455                 :             :                                 else
    9456                 :             :                                 {
    9457                 :             :                                         /* Child column must survive my deletion */
    9458                 :           2 :                                         childatt->attinhcount--;
    9459                 :             : 
    9460                 :           2 :                                         CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    9461                 :             : 
    9462                 :             :                                         /* Make update visible */
    9463                 :           2 :                                         CommandCounterIncrement();
    9464                 :             :                                 }
    9465                 :          94 :                         }
    9466                 :             :                         else
    9467                 :             :                         {
    9468                 :             :                                 /*
    9469                 :             :                                  * If we were told to drop ONLY in this table (no recursion),
    9470                 :             :                                  * we need to mark the inheritors' attributes as locally
    9471                 :             :                                  * defined rather than inherited.
    9472                 :             :                                  */
    9473                 :           4 :                                 childatt->attinhcount--;
    9474                 :           4 :                                 childatt->attislocal = true;
    9475                 :             : 
    9476                 :           4 :                                 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    9477                 :             : 
    9478                 :             :                                 /* Make update visible */
    9479                 :           4 :                                 CommandCounterIncrement();
    9480                 :             :                         }
    9481                 :             : 
    9482                 :          98 :                         heap_freetuple(tuple);
    9483                 :             : 
    9484                 :          98 :                         table_close(childrel, NoLock);
    9485                 :          98 :                 }
    9486                 :          48 :                 table_close(attr_rel, RowExclusiveLock);
    9487                 :          48 :         }
    9488                 :             : 
    9489                 :             :         /* Add object to delete */
    9490                 :         293 :         object.classId = RelationRelationId;
    9491                 :         293 :         object.objectId = RelationGetRelid(rel);
    9492                 :         293 :         object.objectSubId = attnum;
    9493                 :         293 :         add_exact_object_address(&object, addrs);
    9494                 :             : 
    9495         [ +  + ]:         293 :         if (!recursing)
    9496                 :             :         {
    9497                 :             :                 /* Recursion has ended, drop everything that was collected */
    9498                 :         211 :                 performMultipleDeletions(addrs, behavior, 0);
    9499                 :         211 :                 free_object_addresses(addrs);
    9500                 :         211 :         }
    9501                 :             : 
    9502                 :         293 :         return object;
    9503                 :         296 : }
    9504                 :             : 
    9505                 :             : /*
    9506                 :             :  * Prepare to add a primary key on a table, by adding not-null constraints
    9507                 :             :  * on all columns.
    9508                 :             :  *
    9509                 :             :  * The not-null constraints for a primary key must cover the whole inheritance
    9510                 :             :  * hierarchy (failing to ensure that leads to funny corner cases).  For the
    9511                 :             :  * normal case where we're asked to recurse, this routine checks if the
    9512                 :             :  * not-null constraints exist already, and if not queues a requirement for
    9513                 :             :  * them to be created by phase 2.
    9514                 :             :  *
    9515                 :             :  * For the case where we're asked not to recurse, we verify that a not-null
    9516                 :             :  * constraint exists on each column of each (direct) child table, throwing an
    9517                 :             :  * error if not.  Not throwing an error would also work, because a not-null
    9518                 :             :  * constraint would be created anyway, but it'd cause a silent scan of the
    9519                 :             :  * child table to verify absence of nulls.  We prefer to let the user know so
    9520                 :             :  * that they can add the constraint manually without having to hold
    9521                 :             :  * AccessExclusiveLock while at it.
    9522                 :             :  *
    9523                 :             :  * However, it's also important that we do not acquire locks on children if
    9524                 :             :  * the not-null constraints already exist on the parent, to avoid risking
    9525                 :             :  * deadlocks during parallel pg_restore of PKs on partitioned tables.
    9526                 :             :  */
    9527                 :             : static void
    9528                 :         830 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
    9529                 :             :                                         bool recurse, LOCKMODE lockmode,
    9530                 :             :                                         AlterTableUtilityContext *context)
    9531                 :             : {
    9532                 :         830 :         Constraint *pkconstr;
    9533                 :         830 :         List       *children = NIL;
    9534                 :         830 :         bool            got_children = false;
    9535                 :             : 
    9536                 :         830 :         pkconstr = castNode(Constraint, cmd->def);
    9537         [ +  + ]:         830 :         if (pkconstr->contype != CONSTR_PRIMARY)
    9538                 :         679 :                 return;
    9539                 :             : 
    9540                 :             :         /* Verify that columns are not-null, or request that they be made so */
    9541   [ +  +  +  +  :         394 :         foreach_node(String, column, pkconstr->keys)
             +  +  +  + ]
    9542                 :             :         {
    9543                 :         102 :                 AlterTableCmd *newcmd;
    9544                 :         102 :                 Constraint *nnconstr;
    9545                 :         102 :                 HeapTuple       tuple;
    9546                 :             : 
    9547                 :             :                 /*
    9548                 :             :                  * First check if a suitable constraint exists.  If it does, we don't
    9549                 :             :                  * need to request another one.  We do need to bail out if it's not
    9550                 :             :                  * valid, though.
    9551                 :             :                  */
    9552                 :         102 :                 tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
    9553         [ +  + ]:         102 :                 if (tuple != NULL)
    9554                 :             :                 {
    9555                 :          28 :                         verifyNotNullPKCompatible(tuple, strVal(column));
    9556                 :             : 
    9557                 :             :                         /* All good with this one; don't request another */
    9558                 :          28 :                         heap_freetuple(tuple);
    9559                 :          28 :                         continue;
    9560                 :             :                 }
    9561         [ +  + ]:          74 :                 else if (!recurse)
    9562                 :             :                 {
    9563                 :             :                         /*
    9564                 :             :                          * No constraint on this column.  Asked not to recurse, we won't
    9565                 :             :                          * create one here, but verify that all children have one.
    9566                 :             :                          */
    9567         [ +  + ]:           8 :                         if (!got_children)
    9568                 :             :                         {
    9569                 :          12 :                                 children = find_inheritance_children(RelationGetRelid(rel),
    9570                 :           6 :                                                                                                          lockmode);
    9571                 :             :                                 /* only search for children on the first time through */
    9572                 :           6 :                                 got_children = true;
    9573                 :           6 :                         }
    9574                 :             : 
    9575   [ +  +  +  +  :          14 :                         foreach_oid(childrelid, children)
             +  +  +  + ]
    9576                 :             :                         {
    9577                 :           4 :                                 HeapTuple       tup;
    9578                 :             : 
    9579                 :           4 :                                 tup = findNotNullConstraint(childrelid, strVal(column));
    9580         [ +  + ]:           4 :                                 if (!tup)
    9581   [ +  -  +  - ]:           1 :                                         ereport(ERROR,
    9582                 :             :                                                         errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
    9583                 :             :                                                                    strVal(column), get_rel_name(childrelid)));
    9584                 :             :                                 /* verify it's good enough */
    9585                 :           3 :                                 verifyNotNullPKCompatible(tup, strVal(column));
    9586                 :           6 :                         }
    9587                 :           3 :                 }
    9588                 :             : 
    9589                 :             :                 /* This column is not already not-null, so add it to the queue */
    9590                 :          69 :                 nnconstr = makeNotNullConstraint(column);
    9591                 :             : 
    9592                 :          69 :                 newcmd = makeNode(AlterTableCmd);
    9593                 :          69 :                 newcmd->subtype = AT_AddConstraint;
    9594                 :             :                 /* note we force recurse=true here; see above */
    9595                 :          69 :                 newcmd->recurse = true;
    9596                 :          69 :                 newcmd->def = (Node *) nnconstr;
    9597                 :             : 
    9598                 :          69 :                 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
    9599         [ +  + ]:         243 :         }
    9600                 :         825 : }
    9601                 :             : 
    9602                 :             : /*
    9603                 :             :  * Verify whether the given not-null constraint is compatible with a
    9604                 :             :  * primary key.  If not, an error is thrown.
    9605                 :             :  */
    9606                 :             : static void
    9607                 :          33 : verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
    9608                 :             : {
    9609                 :          33 :         Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
    9610                 :             : 
    9611         [ +  - ]:          33 :         if (conForm->contype != CONSTRAINT_NOTNULL)
    9612   [ #  #  #  # ]:           0 :                 elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
    9613                 :             : 
    9614                 :             :         /* a NO INHERIT constraint is no good */
    9615         [ +  + ]:          33 :         if (conForm->connoinherit)
    9616   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    9617                 :             :                                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    9618                 :             :                                 errmsg("cannot create primary key on column \"%s\"", colname),
    9619                 :             :                 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
    9620                 :             :                                 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
    9621                 :             :                                                   NameStr(conForm->conname), colname,
    9622                 :             :                                                   get_rel_name(conForm->conrelid), "NO INHERIT"),
    9623                 :             :                                 errhint("You might need to make the existing constraint inheritable using %s.",
    9624                 :             :                                                 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
    9625                 :             : 
    9626                 :             :         /* an unvalidated constraint is no good */
    9627         [ +  + ]:          31 :         if (!conForm->convalidated)
    9628   [ +  -  +  - ]:           2 :                 ereport(ERROR,
    9629                 :             :                                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    9630                 :             :                                 errmsg("cannot create primary key on column \"%s\"", colname),
    9631                 :             :                 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
    9632                 :             :                                 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
    9633                 :             :                                                   NameStr(conForm->conname), colname,
    9634                 :             :                                                   get_rel_name(conForm->conrelid), "NOT VALID"),
    9635                 :             :                                 errhint("You might need to validate it using %s.",
    9636                 :             :                                                 "ALTER TABLE ... VALIDATE CONSTRAINT"));
    9637                 :          29 : }
    9638                 :             : 
    9639                 :             : /*
    9640                 :             :  * ALTER TABLE ADD INDEX
    9641                 :             :  *
    9642                 :             :  * There is no such command in the grammar, but parse_utilcmd.c converts
    9643                 :             :  * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands.  This lets
    9644                 :             :  * us schedule creation of the index at the appropriate time during ALTER.
    9645                 :             :  *
    9646                 :             :  * Return value is the address of the new index.
    9647                 :             :  */
    9648                 :             : static ObjectAddress
    9649                 :         202 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
    9650                 :             :                            IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9651                 :             : {
    9652                 :         202 :         bool            check_rights;
    9653                 :         202 :         bool            skip_build;
    9654                 :         202 :         bool            quiet;
    9655                 :             :         ObjectAddress address;
    9656                 :             : 
    9657         [ +  - ]:         202 :         Assert(IsA(stmt, IndexStmt));
    9658         [ +  - ]:         202 :         Assert(!stmt->concurrent);
    9659                 :             : 
    9660                 :             :         /* The IndexStmt has already been through transformIndexStmt */
    9661         [ +  - ]:         202 :         Assert(stmt->transformed);
    9662                 :             : 
    9663                 :             :         /* suppress schema rights check when rebuilding existing index */
    9664                 :         202 :         check_rights = !is_rebuild;
    9665                 :             :         /* skip index build if phase 3 will do it or we're reusing an old one */
    9666         [ +  + ]:         202 :         skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
    9667                 :             :         /* suppress notices when rebuilding existing index */
    9668                 :         202 :         quiet = is_rebuild;
    9669                 :             : 
    9670                 :         404 :         address = DefineIndex(NULL,
    9671                 :         202 :                                                   RelationGetRelid(rel),
    9672                 :         202 :                                                   stmt,
    9673                 :             :                                                   InvalidOid,   /* no predefined OID */
    9674                 :             :                                                   InvalidOid,   /* no parent index */
    9675                 :             :                                                   InvalidOid,   /* no parent constraint */
    9676                 :             :                                                   -1,   /* total_parts unknown */
    9677                 :             :                                                   true, /* is_alter_table */
    9678                 :         202 :                                                   check_rights,
    9679                 :             :                                                   false,        /* check_not_in_use - we did it already */
    9680                 :         202 :                                                   skip_build,
    9681                 :         202 :                                                   quiet);
    9682                 :             : 
    9683                 :             :         /*
    9684                 :             :          * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
    9685                 :             :          * new index instead of building from scratch.  Restore associated fields.
    9686                 :             :          * This may store InvalidSubTransactionId in both fields, in which case
    9687                 :             :          * relcache.c will assume it can rebuild the relcache entry.  Hence, do
    9688                 :             :          * this after the CCI that made catalog rows visible to any rebuild.  The
    9689                 :             :          * DROP of the old edition of this index will have scheduled the storage
    9690                 :             :          * for deletion at commit, so cancel that pending deletion.
    9691                 :             :          */
    9692         [ +  + ]:         202 :         if (RelFileNumberIsValid(stmt->oldNumber))
    9693                 :             :         {
    9694                 :          12 :                 Relation        irel = index_open(address.objectId, NoLock);
    9695                 :             : 
    9696                 :          12 :                 irel->rd_createSubid = stmt->oldCreateSubid;
    9697                 :          12 :                 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
    9698                 :          12 :                 RelationPreserveStorage(irel->rd_locator, true);
    9699                 :          12 :                 index_close(irel, NoLock);
    9700                 :          12 :         }
    9701                 :             : 
    9702                 :             :         return address;
    9703                 :         202 : }
    9704                 :             : 
    9705                 :             : /*
    9706                 :             :  * ALTER TABLE ADD STATISTICS
    9707                 :             :  *
    9708                 :             :  * This is no such command in the grammar, but we use this internally to add
    9709                 :             :  * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
    9710                 :             :  * column type change.
    9711                 :             :  */
    9712                 :             : static ObjectAddress
    9713                 :          12 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
    9714                 :             :                                         CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9715                 :             : {
    9716                 :             :         ObjectAddress address;
    9717                 :             : 
    9718         [ +  - ]:          12 :         Assert(IsA(stmt, CreateStatsStmt));
    9719                 :             : 
    9720                 :             :         /* The CreateStatsStmt has already been through transformStatsStmt */
    9721         [ +  - ]:          12 :         Assert(stmt->transformed);
    9722                 :             : 
    9723                 :          12 :         address = CreateStatistics(stmt, !is_rebuild);
    9724                 :             : 
    9725                 :          12 :         return address;
    9726                 :             : }
    9727                 :             : 
    9728                 :             : /*
    9729                 :             :  * ALTER TABLE ADD CONSTRAINT USING INDEX
    9730                 :             :  *
    9731                 :             :  * Returns the address of the new constraint.
    9732                 :             :  */
    9733                 :             : static ObjectAddress
    9734                 :         122 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
    9735                 :             :                                                  IndexStmt *stmt, LOCKMODE lockmode)
    9736                 :             : {
    9737                 :         122 :         Oid                     index_oid = stmt->indexOid;
    9738                 :         122 :         Relation        indexRel;
    9739                 :         122 :         char       *indexName;
    9740                 :         122 :         IndexInfo  *indexInfo;
    9741                 :         122 :         char       *constraintName;
    9742                 :         122 :         char            constraintType;
    9743                 :             :         ObjectAddress address;
    9744                 :         122 :         bits16          flags;
    9745                 :             : 
    9746         [ +  - ]:         122 :         Assert(IsA(stmt, IndexStmt));
    9747         [ +  - ]:         122 :         Assert(OidIsValid(index_oid));
    9748         [ +  - ]:         122 :         Assert(stmt->isconstraint);
    9749                 :             : 
    9750                 :             :         /*
    9751                 :             :          * Doing this on partitioned tables is not a simple feature to implement,
    9752                 :             :          * so let's punt for now.
    9753                 :             :          */
    9754         [ +  + ]:         122 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    9755   [ +  -  +  - ]:           1 :                 ereport(ERROR,
    9756                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9757                 :             :                                  errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
    9758                 :             : 
    9759                 :         121 :         indexRel = index_open(index_oid, AccessShareLock);
    9760                 :             : 
    9761                 :         121 :         indexName = pstrdup(RelationGetRelationName(indexRel));
    9762                 :             : 
    9763                 :         121 :         indexInfo = BuildIndexInfo(indexRel);
    9764                 :             : 
    9765                 :             :         /* this should have been checked at parse time */
    9766         [ +  - ]:         121 :         if (!indexInfo->ii_Unique)
    9767   [ #  #  #  # ]:           0 :                 elog(ERROR, "index \"%s\" is not unique", indexName);
    9768                 :             : 
    9769                 :             :         /*
    9770                 :             :          * Determine name to assign to constraint.  We require a constraint to
    9771                 :             :          * have the same name as the underlying index; therefore, use the index's
    9772                 :             :          * existing name as the default constraint name, and if the user
    9773                 :             :          * explicitly gives some other name for the constraint, rename the index
    9774                 :             :          * to match.
    9775                 :             :          */
    9776                 :         121 :         constraintName = stmt->idxname;
    9777         [ +  + ]:         121 :         if (constraintName == NULL)
    9778                 :         118 :                 constraintName = indexName;
    9779         [ -  + ]:           3 :         else if (strcmp(constraintName, indexName) != 0)
    9780                 :             :         {
    9781   [ -  +  +  - ]:           3 :                 ereport(NOTICE,
    9782                 :             :                                 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
    9783                 :             :                                                 indexName, constraintName)));
    9784                 :           3 :                 RenameRelationInternal(index_oid, constraintName, false, true);
    9785                 :           3 :         }
    9786                 :             : 
    9787                 :             :         /* Extra checks needed if making primary key */
    9788         [ +  + ]:         121 :         if (stmt->primary)
    9789                 :          71 :                 index_check_primary_key(rel, indexInfo, true, stmt);
    9790                 :             : 
    9791                 :             :         /* Note we currently don't support EXCLUSION constraints here */
    9792         [ +  + ]:         121 :         if (stmt->primary)
    9793                 :          70 :                 constraintType = CONSTRAINT_PRIMARY;
    9794                 :             :         else
    9795                 :          51 :                 constraintType = CONSTRAINT_UNIQUE;
    9796                 :             : 
    9797                 :             :         /* Create the catalog entries for the constraint */
    9798                 :         121 :         flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
    9799                 :         121 :                 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
    9800                 :         242 :                 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
    9801                 :         242 :                 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
    9802                 :         121 :                 (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
    9803                 :             : 
    9804                 :         242 :         address = index_constraint_create(rel,
    9805                 :         121 :                                                                           index_oid,
    9806                 :             :                                                                           InvalidOid,
    9807                 :         121 :                                                                           indexInfo,
    9808                 :         121 :                                                                           constraintName,
    9809                 :         121 :                                                                           constraintType,
    9810                 :         121 :                                                                           flags,
    9811                 :         121 :                                                                           allowSystemTableMods,
    9812                 :             :                                                                           false);       /* is_internal */
    9813                 :             : 
    9814                 :         121 :         index_close(indexRel, NoLock);
    9815                 :             : 
    9816                 :             :         return address;
    9817                 :         121 : }
    9818                 :             : 
    9819                 :             : /*
    9820                 :             :  * ALTER TABLE ADD CONSTRAINT
    9821                 :             :  *
    9822                 :             :  * Return value is the address of the new constraint; if no constraint was
    9823                 :             :  * added, InvalidObjectAddress is returned.
    9824                 :             :  */
    9825                 :             : static ObjectAddress
    9826                 :         777 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9827                 :             :                                         Constraint *newConstraint, bool recurse, bool is_readd,
    9828                 :             :                                         LOCKMODE lockmode)
    9829                 :             : {
    9830                 :         777 :         ObjectAddress address = InvalidObjectAddress;
    9831                 :             : 
    9832         [ +  - ]:         777 :         Assert(IsA(newConstraint, Constraint));
    9833                 :             : 
    9834                 :             :         /*
    9835                 :             :          * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
    9836                 :             :          * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
    9837                 :             :          * parse_utilcmd.c).
    9838                 :             :          */
    9839      [ +  +  - ]:         777 :         switch (newConstraint->contype)
    9840                 :             :         {
    9841                 :             :                 case CONSTR_CHECK:
    9842                 :             :                 case CONSTR_NOTNULL:
    9843                 :             :                         address =
    9844                 :         900 :                                 ATAddCheckNNConstraint(wqueue, tab, rel,
    9845                 :         450 :                                                                            newConstraint, recurse, false, is_readd,
    9846                 :         450 :                                                                            lockmode);
    9847                 :         450 :                         break;
    9848                 :             : 
    9849                 :             :                 case CONSTR_FOREIGN:
    9850                 :             : 
    9851                 :             :                         /*
    9852                 :             :                          * Assign or validate constraint name
    9853                 :             :                          */
    9854         [ +  + ]:         327 :                         if (newConstraint->conname)
    9855                 :             :                         {
    9856         [ +  - ]:         130 :                                 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
    9857                 :         130 :                                                                                  RelationGetRelid(rel),
    9858                 :         130 :                                                                                  newConstraint->conname))
    9859   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
    9860                 :             :                                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
    9861                 :             :                                                          errmsg("constraint \"%s\" for relation \"%s\" already exists",
    9862                 :             :                                                                         newConstraint->conname,
    9863                 :             :                                                                         RelationGetRelationName(rel))));
    9864                 :         130 :                         }
    9865                 :             :                         else
    9866                 :         197 :                                 newConstraint->conname =
    9867                 :         394 :                                         ChooseConstraintName(RelationGetRelationName(rel),
    9868                 :         197 :                                                                                  ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
    9869                 :             :                                                                                  "fkey",
    9870                 :         197 :                                                                                  RelationGetNamespace(rel),
    9871                 :             :                                                                                  NIL);
    9872                 :             : 
    9873                 :         654 :                         address = ATAddForeignKeyConstraint(wqueue, tab, rel,
    9874                 :         327 :                                                                                                 newConstraint,
    9875                 :         327 :                                                                                                 recurse, false,
    9876                 :         327 :                                                                                                 lockmode);
    9877                 :         327 :                         break;
    9878                 :             : 
    9879                 :             :                 default:
    9880   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized constraint type: %d",
    9881                 :             :                                  (int) newConstraint->contype);
    9882                 :           0 :         }
    9883                 :             : 
    9884                 :         777 :         return address;
    9885                 :             : }
    9886                 :             : 
    9887                 :             : /*
    9888                 :             :  * Generate the column-name portion of the constraint name for a new foreign
    9889                 :             :  * key given the list of column names that reference the referenced
    9890                 :             :  * table.  This will be passed to ChooseConstraintName along with the parent
    9891                 :             :  * table name and the "fkey" suffix.
    9892                 :             :  *
    9893                 :             :  * We know that less than NAMEDATALEN characters will actually be used, so we
    9894                 :             :  * can truncate the result once we've generated that many.
    9895                 :             :  *
    9896                 :             :  * XXX see also ChooseExtendedStatisticNameAddition and
    9897                 :             :  * ChooseIndexNameAddition.
    9898                 :             :  */
    9899                 :             : static char *
    9900                 :         197 : ChooseForeignKeyConstraintNameAddition(List *colnames)
    9901                 :             : {
    9902                 :         197 :         char            buf[NAMEDATALEN * 2];
    9903                 :         197 :         int                     buflen = 0;
    9904                 :         197 :         ListCell   *lc;
    9905                 :             : 
    9906                 :         197 :         buf[0] = '\0';
    9907   [ +  -  +  +  :         458 :         foreach(lc, colnames)
                   +  + ]
    9908                 :             :         {
    9909                 :         261 :                 const char *name = strVal(lfirst(lc));
    9910                 :             : 
    9911         [ +  + ]:         261 :                 if (buflen > 0)
    9912                 :          64 :                         buf[buflen++] = '_';    /* insert _ between names */
    9913                 :             : 
    9914                 :             :                 /*
    9915                 :             :                  * At this point we have buflen <= NAMEDATALEN.  name should be less
    9916                 :             :                  * than NAMEDATALEN already, but use strlcpy for paranoia.
    9917                 :             :                  */
    9918                 :         261 :                 strlcpy(buf + buflen, name, NAMEDATALEN);
    9919                 :         261 :                 buflen += strlen(buf + buflen);
    9920         [ -  + ]:         261 :                 if (buflen >= NAMEDATALEN)
    9921                 :           0 :                         break;
    9922         [ -  + ]:         261 :         }
    9923                 :         394 :         return pstrdup(buf);
    9924                 :         197 : }
    9925                 :             : 
    9926                 :             : /*
    9927                 :             :  * Add a check or not-null constraint to a single table and its children.
    9928                 :             :  * Returns the address of the constraint added to the parent relation,
    9929                 :             :  * if one gets added, or InvalidObjectAddress otherwise.
    9930                 :             :  *
    9931                 :             :  * Subroutine for ATExecAddConstraint.
    9932                 :             :  *
    9933                 :             :  * We must recurse to child tables during execution, rather than using
    9934                 :             :  * ALTER TABLE's normal prep-time recursion.  The reason is that all the
    9935                 :             :  * constraints *must* be given the same name, else they won't be seen as
    9936                 :             :  * related later.  If the user didn't explicitly specify a name, then
    9937                 :             :  * AddRelationNewConstraints would normally assign different names to the
    9938                 :             :  * child constraints.  To fix that, we must capture the name assigned at
    9939                 :             :  * the parent table and pass that down.
    9940                 :             :  */
    9941                 :             : static ObjectAddress
    9942                 :         564 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9943                 :             :                                            Constraint *constr, bool recurse, bool recursing,
    9944                 :             :                                            bool is_readd, LOCKMODE lockmode)
    9945                 :             : {
    9946                 :         564 :         List       *newcons;
    9947                 :         564 :         ListCell   *lcon;
    9948                 :         564 :         List       *children;
    9949                 :         564 :         ListCell   *child;
    9950                 :         564 :         ObjectAddress address = InvalidObjectAddress;
    9951                 :             : 
    9952                 :             :         /* Guard against stack overflow due to overly deep inheritance tree. */
    9953                 :         564 :         check_stack_depth();
    9954                 :             : 
    9955                 :             :         /* At top level, permission check was done in ATPrepCmd, else do it */
    9956         [ +  + ]:         564 :         if (recursing)
    9957                 :         142 :                 ATSimplePermissions(AT_AddConstraint, rel,
    9958                 :             :                                                         ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    9959                 :             : 
    9960                 :             :         /*
    9961                 :             :          * Call AddRelationNewConstraints to do the work, making sure it works on
    9962                 :             :          * a copy of the Constraint so transformExpr can't modify the original. It
    9963                 :             :          * returns a list of cooked constraints.
    9964                 :             :          *
    9965                 :             :          * If the constraint ends up getting merged with a pre-existing one, it's
    9966                 :             :          * omitted from the returned list, which is what we want: we do not need
    9967                 :             :          * to do any validation work.  That can only happen at child tables,
    9968                 :             :          * though, since we disallow merging at the top level.
    9969                 :             :          */
    9970                 :        1014 :         newcons = AddRelationNewConstraints(rel, NIL,
    9971                 :         564 :                                                                                 list_make1(copyObject(constr)),
    9972         [ +  + ]:         564 :                                                                                 recursing || is_readd,  /* allow_merge */
    9973                 :         564 :                                                                                 !recursing, /* is_local */
    9974                 :         564 :                                                                                 is_readd,       /* is_internal */
    9975                 :             :                                                                                 NULL);  /* queryString not available
    9976                 :             :                                                                                                  * here */
    9977                 :             : 
    9978                 :             :         /* we don't expect more than one constraint here */
    9979         [ +  - ]:         564 :         Assert(list_length(newcons) <= 1);
    9980                 :             : 
    9981                 :             :         /* Add each to-be-validated constraint to Phase 3's queue */
    9982   [ +  +  +  +  :        1108 :         foreach(lcon, newcons)
                   +  + ]
    9983                 :             :         {
    9984                 :         544 :                 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
    9985                 :             : 
    9986   [ +  +  +  + ]:         544 :                 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
    9987                 :             :                 {
    9988                 :         160 :                         NewConstraint *newcon;
    9989                 :             : 
    9990                 :         160 :                         newcon = palloc0_object(NewConstraint);
    9991                 :         160 :                         newcon->name = ccon->name;
    9992                 :         160 :                         newcon->contype = ccon->contype;
    9993                 :         160 :                         newcon->qual = ccon->expr;
    9994                 :             : 
    9995                 :         160 :                         tab->constraints = lappend(tab->constraints, newcon);
    9996                 :         160 :                 }
    9997                 :             : 
    9998                 :             :                 /* Save the actually assigned name if it was defaulted */
    9999         [ +  + ]:         544 :                 if (constr->conname == NULL)
   10000                 :         238 :                         constr->conname = ccon->name;
   10001                 :             : 
   10002                 :             :                 /*
   10003                 :             :                  * If adding a valid not-null constraint, set the pg_attribute flag
   10004                 :             :                  * and tell phase 3 to verify existing rows, if needed.  For an
   10005                 :             :                  * invalid constraint, just set attnotnull, without queueing
   10006                 :             :                  * verification.
   10007                 :             :                  */
   10008         [ +  + ]:         544 :                 if (constr->contype == CONSTR_NOTNULL)
   10009                 :         612 :                         set_attnotnull(wqueue, rel, ccon->attnum,
   10010                 :         306 :                                                    !constr->skip_validation,
   10011                 :         306 :                                                    !constr->skip_validation);
   10012                 :             : 
   10013                 :         544 :                 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
   10014                 :         544 :         }
   10015                 :             : 
   10016                 :             :         /* At this point we must have a locked-down name to use */
   10017   [ +  +  +  - ]:         564 :         Assert(newcons == NIL || constr->conname != NULL);
   10018                 :             : 
   10019                 :             :         /* Advance command counter in case same table is visited multiple times */
   10020                 :         564 :         CommandCounterIncrement();
   10021                 :             : 
   10022                 :             :         /*
   10023                 :             :          * If the constraint got merged with an existing constraint, we're done.
   10024                 :             :          * We mustn't recurse to child tables in this case, because they've
   10025                 :             :          * already got the constraint, and visiting them again would lead to an
   10026                 :             :          * incorrect value for coninhcount.
   10027                 :             :          */
   10028         [ +  + ]:         564 :         if (newcons == NIL)
   10029                 :          30 :                 return address;
   10030                 :             : 
   10031                 :             :         /*
   10032                 :             :          * If adding a NO INHERIT constraint, no need to find our children.
   10033                 :             :          */
   10034         [ +  + ]:         534 :         if (constr->is_no_inherit)
   10035                 :          14 :                 return address;
   10036                 :             : 
   10037                 :             :         /*
   10038                 :             :          * Propagate to children as appropriate.  Unlike most other ALTER
   10039                 :             :          * routines, we have to do this one level of recursion at a time; we can't
   10040                 :             :          * use find_all_inheritors to do it in one pass.
   10041                 :             :          */
   10042                 :         520 :         children =
   10043                 :         520 :                 find_inheritance_children(RelationGetRelid(rel), lockmode);
   10044                 :             : 
   10045                 :             :         /*
   10046                 :             :          * Check if ONLY was specified with ALTER TABLE.  If so, allow the
   10047                 :             :          * constraint creation only if there are no children currently. Error out
   10048                 :             :          * otherwise.
   10049                 :             :          */
   10050   [ +  +  +  + ]:         520 :         if (!recurse && children != NIL)
   10051   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   10052                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10053                 :             :                                  errmsg("constraint must be added to child tables too")));
   10054                 :             : 
   10055                 :             :         /*
   10056                 :             :          * Recurse to create the constraint on each child.
   10057                 :             :          */
   10058   [ +  +  +  +  :         661 :         foreach(child, children)
                   +  + ]
   10059                 :             :         {
   10060                 :         142 :                 Oid                     childrelid = lfirst_oid(child);
   10061                 :         142 :                 Relation        childrel;
   10062                 :         142 :                 AlteredTableInfo *childtab;
   10063                 :             : 
   10064                 :             :                 /* find_inheritance_children already got lock */
   10065                 :         142 :                 childrel = table_open(childrelid, NoLock);
   10066                 :         142 :                 CheckAlterTableIsSafe(childrel);
   10067                 :             : 
   10068                 :             :                 /* Find or create work queue entry for this table */
   10069                 :         142 :                 childtab = ATGetQueueEntry(wqueue, childrel);
   10070                 :             : 
   10071                 :             :                 /* Recurse to this child */
   10072                 :         284 :                 ATAddCheckNNConstraint(wqueue, childtab, childrel,
   10073                 :         142 :                                                            constr, recurse, true, is_readd, lockmode);
   10074                 :             : 
   10075                 :         142 :                 table_close(childrel, NoLock);
   10076                 :         142 :         }
   10077                 :             : 
   10078                 :         519 :         return address;
   10079                 :         563 : }
   10080                 :             : 
   10081                 :             : /*
   10082                 :             :  * Add a foreign-key constraint to a single table; return the new constraint's
   10083                 :             :  * address.
   10084                 :             :  *
   10085                 :             :  * Subroutine for ATExecAddConstraint.  Must already hold exclusive
   10086                 :             :  * lock on the rel, and have done appropriate validity checks for it.
   10087                 :             :  * We do permissions checks here, however.
   10088                 :             :  *
   10089                 :             :  * When the referenced or referencing tables (or both) are partitioned,
   10090                 :             :  * multiple pg_constraint rows are required -- one for each partitioned table
   10091                 :             :  * and each partition on each side (fortunately, not one for every combination
   10092                 :             :  * thereof).  We also need action triggers on each leaf partition on the
   10093                 :             :  * referenced side, and check triggers on each leaf partition on the
   10094                 :             :  * referencing side.
   10095                 :             :  */
   10096                 :             : static ObjectAddress
   10097                 :         338 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
   10098                 :             :                                                   Constraint *fkconstraint,
   10099                 :             :                                                   bool recurse, bool recursing, LOCKMODE lockmode)
   10100                 :             : {
   10101                 :         338 :         Relation        pkrel;
   10102                 :         338 :         int16           pkattnum[INDEX_MAX_KEYS] = {0};
   10103                 :         338 :         int16           fkattnum[INDEX_MAX_KEYS] = {0};
   10104                 :         338 :         Oid                     pktypoid[INDEX_MAX_KEYS] = {0};
   10105                 :         338 :         Oid                     fktypoid[INDEX_MAX_KEYS] = {0};
   10106                 :         338 :         Oid                     pkcolloid[INDEX_MAX_KEYS] = {0};
   10107                 :         338 :         Oid                     fkcolloid[INDEX_MAX_KEYS] = {0};
   10108                 :         338 :         Oid                     opclasses[INDEX_MAX_KEYS] = {0};
   10109                 :         338 :         Oid                     pfeqoperators[INDEX_MAX_KEYS] = {0};
   10110                 :         338 :         Oid                     ppeqoperators[INDEX_MAX_KEYS] = {0};
   10111                 :         338 :         Oid                     ffeqoperators[INDEX_MAX_KEYS] = {0};
   10112                 :         338 :         int16           fkdelsetcols[INDEX_MAX_KEYS] = {0};
   10113                 :         338 :         bool            with_period;
   10114                 :         338 :         bool            pk_has_without_overlaps;
   10115                 :         338 :         int                     i;
   10116                 :         338 :         int                     numfks,
   10117                 :             :                                 numpks,
   10118                 :             :                                 numfkdelsetcols;
   10119                 :         338 :         Oid                     indexOid;
   10120                 :         338 :         bool            old_check_ok;
   10121                 :             :         ObjectAddress address;
   10122                 :         338 :         ListCell   *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
   10123                 :             : 
   10124                 :             :         /*
   10125                 :             :          * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
   10126                 :             :          * delete rows out from under us.
   10127                 :             :          */
   10128         [ +  + ]:         338 :         if (OidIsValid(fkconstraint->old_pktable_oid))
   10129                 :          23 :                 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
   10130                 :             :         else
   10131                 :         315 :                 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
   10132                 :             : 
   10133                 :             :         /*
   10134                 :             :          * Validity checks (permission checks wait till we have the column
   10135                 :             :          * numbers)
   10136                 :             :          */
   10137   [ +  +  -  + ]:         338 :         if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10138   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   10139                 :             :                                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10140                 :             :                                 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
   10141                 :             :                                            RelationGetRelationName(rel),
   10142                 :             :                                            RelationGetRelationName(pkrel)));
   10143                 :             : 
   10144   [ +  +  +  - ]:         337 :         if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
   10145                 :          42 :                 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
   10146   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   10147                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10148                 :             :                                  errmsg("referenced relation \"%s\" is not a table",
   10149                 :             :                                                 RelationGetRelationName(pkrel))));
   10150                 :             : 
   10151   [ +  +  +  - ]:         337 :         if (!allowSystemTableMods && IsSystemRelation(pkrel))
   10152   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   10153                 :             :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   10154                 :             :                                  errmsg("permission denied: \"%s\" is a system catalog",
   10155                 :             :                                                 RelationGetRelationName(pkrel))));
   10156                 :             : 
   10157                 :             :         /*
   10158                 :             :          * References from permanent or unlogged tables to temp tables, and from
   10159                 :             :          * permanent tables to unlogged tables, are disallowed because the
   10160                 :             :          * referenced data can vanish out from under us.  References from temp
   10161                 :             :          * tables to any other table type are also disallowed, because other
   10162                 :             :          * backends might need to run the RI triggers on the perm table, but they
   10163                 :             :          * can't reliably see tuples in the local buffers of other backends.
   10164                 :             :          */
   10165   [ +  +  +  + ]:         337 :         switch (rel->rd_rel->relpersistence)
   10166                 :             :         {
   10167                 :             :                 case RELPERSISTENCE_PERMANENT:
   10168         [ +  - ]:         277 :                         if (!RelationIsPermanent(pkrel))
   10169   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   10170                 :             :                                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10171                 :             :                                                  errmsg("constraints on permanent tables may reference only permanent tables")));
   10172                 :         277 :                         break;
   10173                 :             :                 case RELPERSISTENCE_UNLOGGED:
   10174                 :           2 :                         if (!RelationIsPermanent(pkrel)
   10175   [ +  -  +  - ]:           2 :                                 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
   10176   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   10177                 :             :                                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10178                 :             :                                                  errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
   10179                 :           2 :                         break;
   10180                 :             :                 case RELPERSISTENCE_TEMP:
   10181         [ +  - ]:          46 :                         if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   10182   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   10183                 :             :                                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10184                 :             :                                                  errmsg("constraints on temporary tables may reference only temporary tables")));
   10185         [ +  - ]:          46 :                         if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
   10186   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   10187                 :             :                                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10188                 :             :                                                  errmsg("constraints on temporary tables must involve temporary tables of this session")));
   10189                 :          46 :                         break;
   10190                 :             :         }
   10191                 :             : 
   10192                 :             :         /*
   10193                 :             :          * Look up the referencing attributes to make sure they exist, and record
   10194                 :             :          * their attnums and type and collation OIDs.
   10195                 :             :          */
   10196                 :         674 :         numfks = transformColumnNameList(RelationGetRelid(rel),
   10197                 :         337 :                                                                          fkconstraint->fk_attrs,
   10198                 :         337 :                                                                          fkattnum, fktypoid, fkcolloid);
   10199         [ +  + ]:         337 :         with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
   10200   [ +  +  +  + ]:         313 :         if (with_period && !fkconstraint->fk_with_period)
   10201   [ +  -  +  - ]:           4 :                 ereport(ERROR,
   10202                 :             :                                 errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10203                 :             :                                 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
   10204                 :             : 
   10205                 :         618 :         numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
   10206                 :         309 :                                                                                           fkconstraint->fk_del_set_cols,
   10207                 :         309 :                                                                                           fkdelsetcols, NULL, NULL);
   10208                 :         618 :         numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
   10209                 :         309 :                                                                                                    numfkdelsetcols,
   10210                 :         309 :                                                                                                    fkdelsetcols,
   10211                 :         309 :                                                                                                    fkconstraint->fk_del_set_cols);
   10212                 :             : 
   10213                 :             :         /*
   10214                 :             :          * If the attribute list for the referenced table was omitted, lookup the
   10215                 :             :          * definition of the primary key and use it.  Otherwise, validate the
   10216                 :             :          * supplied attribute list.  In either case, discover the index OID and
   10217                 :             :          * index opclasses, and the attnums and type and collation OIDs of the
   10218                 :             :          * attributes.
   10219                 :             :          */
   10220         [ +  + ]:         309 :         if (fkconstraint->pk_attrs == NIL)
   10221                 :             :         {
   10222                 :         338 :                 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
   10223                 :         169 :                                                                                         &fkconstraint->pk_attrs,
   10224                 :         169 :                                                                                         pkattnum, pktypoid, pkcolloid,
   10225                 :         169 :                                                                                         opclasses, &pk_has_without_overlaps);
   10226                 :             : 
   10227                 :             :                 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
   10228   [ +  +  +  + ]:         169 :                 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
   10229   [ +  -  +  - ]:           4 :                         ereport(ERROR,
   10230                 :             :                                         errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10231                 :             :                                         errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
   10232                 :         165 :         }
   10233                 :             :         else
   10234                 :             :         {
   10235                 :         280 :                 numpks = transformColumnNameList(RelationGetRelid(pkrel),
   10236                 :         140 :                                                                                  fkconstraint->pk_attrs,
   10237                 :         140 :                                                                                  pkattnum, pktypoid, pkcolloid);
   10238                 :             : 
   10239                 :             :                 /* Since we got pk_attrs, one should be a period. */
   10240   [ +  +  +  + ]:         140 :                 if (with_period && !fkconstraint->pk_with_period)
   10241   [ +  -  +  - ]:           4 :                         ereport(ERROR,
   10242                 :             :                                         errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10243                 :             :                                         errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
   10244                 :             : 
   10245                 :             :                 /* Look for an index matching the column list */
   10246                 :         272 :                 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
   10247                 :         136 :                                                                                    with_period, opclasses, &pk_has_without_overlaps);
   10248                 :             :         }
   10249                 :             : 
   10250                 :             :         /*
   10251                 :             :          * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
   10252                 :             :          * must use PERIOD.
   10253                 :             :          */
   10254   [ +  +  +  + ]:         301 :         if (pk_has_without_overlaps && !with_period)
   10255   [ +  -  +  - ]:           2 :                 ereport(ERROR,
   10256                 :             :                                 errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10257                 :             :                                 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
   10258                 :             : 
   10259                 :             :         /*
   10260                 :             :          * Now we can check permissions.
   10261                 :             :          */
   10262                 :         299 :         checkFkeyPermissions(pkrel, pkattnum, numpks);
   10263                 :             : 
   10264                 :             :         /*
   10265                 :             :          * Check some things for generated columns.
   10266                 :             :          */
   10267         [ +  + ]:         725 :         for (i = 0; i < numfks; i++)
   10268                 :             :         {
   10269                 :         431 :                 char            attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
   10270                 :             : 
   10271         [ +  + ]:         431 :                 if (attgenerated)
   10272                 :             :                 {
   10273                 :             :                         /*
   10274                 :             :                          * Check restrictions on UPDATE/DELETE actions, per SQL standard
   10275                 :             :                          */
   10276         [ +  + ]:           8 :                         if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
   10277                 :           6 :                                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
   10278                 :           6 :                                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
   10279   [ +  -  +  - ]:           2 :                                 ereport(ERROR,
   10280                 :             :                                                 (errcode(ERRCODE_SYNTAX_ERROR),
   10281                 :             :                                                  errmsg("invalid %s action for foreign key constraint containing generated column",
   10282                 :             :                                                                 "ON UPDATE")));
   10283         [ +  + ]:           6 :                         if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
   10284                 :           4 :                                 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
   10285   [ +  -  +  - ]:           2 :                                 ereport(ERROR,
   10286                 :             :                                                 (errcode(ERRCODE_SYNTAX_ERROR),
   10287                 :             :                                                  errmsg("invalid %s action for foreign key constraint containing generated column",
   10288                 :             :                                                                 "ON DELETE")));
   10289                 :           4 :                 }
   10290                 :             : 
   10291                 :             :                 /*
   10292                 :             :                  * FKs on virtual columns are not supported.  This would require
   10293                 :             :                  * various additional support in ri_triggers.c, including special
   10294                 :             :                  * handling in ri_NullCheck(), ri_KeysEqual(),
   10295                 :             :                  * RI_FKey_fk_upd_check_required() (since all virtual columns appear
   10296                 :             :                  * as NULL there).  Also not really practical as long as you can't
   10297                 :             :                  * index virtual columns.
   10298                 :             :                  */
   10299         [ +  + ]:         427 :                 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
   10300   [ +  -  +  - ]:           1 :                         ereport(ERROR,
   10301                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10302                 :             :                                          errmsg("foreign key constraints on virtual generated columns are not supported")));
   10303                 :         426 :         }
   10304                 :             : 
   10305                 :             :         /*
   10306                 :             :          * Some actions are currently unsupported for foreign keys using PERIOD.
   10307                 :             :          */
   10308         [ +  + ]:         294 :         if (fkconstraint->fk_with_period)
   10309                 :             :         {
   10310         [ +  + ]:          39 :                 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
   10311                 :          28 :                         fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
   10312                 :          28 :                         fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
   10313                 :          28 :                         fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
   10314   [ +  -  +  - ]:          11 :                         ereport(ERROR,
   10315                 :             :                                         errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10316                 :             :                                         errmsg("unsupported %s action for foreign key constraint using PERIOD",
   10317                 :             :                                                    "ON UPDATE"));
   10318                 :             : 
   10319         [ +  + ]:          28 :                 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
   10320                 :          27 :                         fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
   10321                 :          27 :                         fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
   10322                 :          27 :                         fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
   10323   [ +  -  +  - ]:           1 :                         ereport(ERROR,
   10324                 :             :                                         errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10325                 :             :                                         errmsg("unsupported %s action for foreign key constraint using PERIOD",
   10326                 :             :                                                    "ON DELETE"));
   10327                 :          27 :         }
   10328                 :             : 
   10329                 :             :         /*
   10330                 :             :          * Look up the equality operators to use in the constraint.
   10331                 :             :          *
   10332                 :             :          * Note that we have to be careful about the difference between the actual
   10333                 :             :          * PK column type and the opclass' declared input type, which might be
   10334                 :             :          * only binary-compatible with it.  The declared opcintype is the right
   10335                 :             :          * thing to probe pg_amop with.
   10336                 :             :          */
   10337         [ +  - ]:         282 :         if (numfks != numpks)
   10338   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   10339                 :             :                                 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10340                 :             :                                  errmsg("number of referencing and referenced columns for foreign key disagree")));
   10341                 :             : 
   10342                 :             :         /*
   10343                 :             :          * On the strength of a previous constraint, we might avoid scanning
   10344                 :             :          * tables to validate this one.  See below.
   10345                 :             :          */
   10346                 :         282 :         old_check_ok = (fkconstraint->old_conpfeqop != NIL);
   10347   [ +  +  +  - ]:         282 :         Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
   10348                 :             : 
   10349         [ +  + ]:         620 :         for (i = 0; i < numpks; i++)
   10350                 :             :         {
   10351                 :         378 :                 Oid                     pktype = pktypoid[i];
   10352                 :         378 :                 Oid                     fktype = fktypoid[i];
   10353                 :         378 :                 Oid                     fktyped;
   10354                 :         378 :                 Oid                     pkcoll = pkcolloid[i];
   10355                 :         378 :                 Oid                     fkcoll = fkcolloid[i];
   10356                 :         378 :                 HeapTuple       cla_ht;
   10357                 :         378 :                 Form_pg_opclass cla_tup;
   10358                 :         378 :                 Oid                     amid;
   10359                 :         378 :                 Oid                     opfamily;
   10360                 :         378 :                 Oid                     opcintype;
   10361                 :         378 :                 bool            for_overlaps;
   10362                 :         378 :                 CompareType cmptype;
   10363                 :         378 :                 Oid                     pfeqop;
   10364                 :         378 :                 Oid                     ppeqop;
   10365                 :         378 :                 Oid                     ffeqop;
   10366                 :         378 :                 int16           eqstrategy;
   10367                 :         378 :                 Oid                     pfeqop_right;
   10368                 :             : 
   10369                 :             :                 /* We need several fields out of the pg_opclass entry */
   10370                 :         378 :                 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
   10371         [ +  - ]:         378 :                 if (!HeapTupleIsValid(cla_ht))
   10372   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
   10373                 :         378 :                 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
   10374                 :         378 :                 amid = cla_tup->opcmethod;
   10375                 :         378 :                 opfamily = cla_tup->opcfamily;
   10376                 :         378 :                 opcintype = cla_tup->opcintype;
   10377                 :         378 :                 ReleaseSysCache(cla_ht);
   10378                 :             : 
   10379                 :             :                 /*
   10380                 :             :                  * Get strategy number from index AM.
   10381                 :             :                  *
   10382                 :             :                  * For a normal foreign-key constraint, this should not fail, since we
   10383                 :             :                  * already checked that the index is unique and should therefore have
   10384                 :             :                  * appropriate equal operators.  For a period foreign key, this could
   10385                 :             :                  * fail if we selected a non-matching exclusion constraint earlier.
   10386                 :             :                  * (XXX Maybe we should do these lookups earlier so we don't end up
   10387                 :             :                  * doing that.)
   10388                 :             :                  */
   10389         [ +  + ]:         378 :                 for_overlaps = with_period && i == numpks - 1;
   10390                 :         378 :                 cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
   10391                 :         378 :                 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
   10392         [ +  - ]:         378 :                 if (eqstrategy == InvalidStrategy)
   10393   [ #  #  #  #  :           0 :                         ereport(ERROR,
                   #  # ]
   10394                 :             :                                         errcode(ERRCODE_UNDEFINED_OBJECT),
   10395                 :             :                                         for_overlaps
   10396                 :             :                                         ? errmsg("could not identify an overlaps operator for foreign key")
   10397                 :             :                                         : errmsg("could not identify an equality operator for foreign key"),
   10398                 :             :                                         errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
   10399                 :             :                                                           cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
   10400                 :             : 
   10401                 :             :                 /*
   10402                 :             :                  * There had better be a primary equality operator for the index.
   10403                 :             :                  * We'll use it for PK = PK comparisons.
   10404                 :             :                  */
   10405                 :         756 :                 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
   10406                 :         378 :                                                                          eqstrategy);
   10407                 :             : 
   10408         [ +  - ]:         378 :                 if (!OidIsValid(ppeqop))
   10409   [ #  #  #  # ]:           0 :                         elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
   10410                 :             :                                  eqstrategy, opcintype, opcintype, opfamily);
   10411                 :             : 
   10412                 :             :                 /*
   10413                 :             :                  * Are there equality operators that take exactly the FK type? Assume
   10414                 :             :                  * we should look through any domain here.
   10415                 :             :                  */
   10416                 :         378 :                 fktyped = getBaseType(fktype);
   10417                 :             : 
   10418                 :         756 :                 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
   10419                 :         378 :                                                                          eqstrategy);
   10420         [ +  + ]:         378 :                 if (OidIsValid(pfeqop))
   10421                 :             :                 {
   10422                 :         277 :                         pfeqop_right = fktyped;
   10423                 :         554 :                         ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
   10424                 :         277 :                                                                                  eqstrategy);
   10425                 :         277 :                 }
   10426                 :             :                 else
   10427                 :             :                 {
   10428                 :             :                         /* keep compiler quiet */
   10429                 :         101 :                         pfeqop_right = InvalidOid;
   10430                 :         101 :                         ffeqop = InvalidOid;
   10431                 :             :                 }
   10432                 :             : 
   10433   [ +  +  +  - ]:         378 :                 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
   10434                 :             :                 {
   10435                 :             :                         /*
   10436                 :             :                          * Otherwise, look for an implicit cast from the FK type to the
   10437                 :             :                          * opcintype, and if found, use the primary equality operator.
   10438                 :             :                          * This is a bit tricky because opcintype might be a polymorphic
   10439                 :             :                          * type such as ANYARRAY or ANYENUM; so what we have to test is
   10440                 :             :                          * whether the two actual column types can be concurrently cast to
   10441                 :             :                          * that type.  (Otherwise, we'd fail to reject combinations such
   10442                 :             :                          * as int[] and point[].)
   10443                 :             :                          */
   10444                 :         101 :                         Oid                     input_typeids[2];
   10445                 :         101 :                         Oid                     target_typeids[2];
   10446                 :             : 
   10447                 :         101 :                         input_typeids[0] = pktype;
   10448                 :         101 :                         input_typeids[1] = fktype;
   10449                 :         101 :                         target_typeids[0] = opcintype;
   10450                 :         101 :                         target_typeids[1] = opcintype;
   10451         [ +  + ]:         101 :                         if (can_coerce_type(2, input_typeids, target_typeids,
   10452                 :             :                                                                 COERCION_IMPLICIT))
   10453                 :             :                         {
   10454                 :          63 :                                 pfeqop = ffeqop = ppeqop;
   10455                 :          63 :                                 pfeqop_right = opcintype;
   10456                 :          63 :                         }
   10457                 :         101 :                 }
   10458                 :             : 
   10459         [ +  + ]:         378 :                 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
   10460   [ +  -  +  - ]:          38 :                         ereport(ERROR,
   10461                 :             :                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   10462                 :             :                                          errmsg("foreign key constraint \"%s\" cannot be implemented",
   10463                 :             :                                                         fkconstraint->conname),
   10464                 :             :                                          errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
   10465                 :             :                                                            "are of incompatible types: %s and %s.",
   10466                 :             :                                                            strVal(list_nth(fkconstraint->fk_attrs, i)),
   10467                 :             :                                                            strVal(list_nth(fkconstraint->pk_attrs, i)),
   10468                 :             :                                                            format_type_be(fktype),
   10469                 :             :                                                            format_type_be(pktype))));
   10470                 :             : 
   10471                 :             :                 /*
   10472                 :             :                  * This shouldn't be possible, but better check to make sure we have a
   10473                 :             :                  * consistent state for the check below.
   10474                 :             :                  */
   10475   [ +  +  +  -  :         340 :                 if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
                   +  + ]
   10476   [ #  #  #  # ]:           0 :                         elog(ERROR, "key columns are not both collatable");
   10477                 :             : 
   10478   [ +  +  -  + ]:         340 :                 if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
   10479                 :             :                 {
   10480                 :          17 :                         bool            pkcolldet;
   10481                 :          17 :                         bool            fkcolldet;
   10482                 :             : 
   10483                 :          17 :                         pkcolldet = get_collation_isdeterministic(pkcoll);
   10484                 :          17 :                         fkcolldet = get_collation_isdeterministic(fkcoll);
   10485                 :             : 
   10486                 :             :                         /*
   10487                 :             :                          * SQL requires that both collations are the same.  This is
   10488                 :             :                          * because we need a consistent notion of equality on both
   10489                 :             :                          * columns.  We relax this by allowing different collations if
   10490                 :             :                          * they are both deterministic.  (This is also for backward
   10491                 :             :                          * compatibility, because PostgreSQL has always allowed this.)
   10492                 :             :                          */
   10493   [ +  +  +  + ]:          17 :                         if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
   10494   [ +  -  +  - ]:           2 :                                 ereport(ERROR,
   10495                 :             :                                                 (errcode(ERRCODE_COLLATION_MISMATCH),
   10496                 :             :                                                  errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
   10497                 :             :                                                  errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
   10498                 :             :                                                                    "have incompatible collations: \"%s\" and \"%s\".  "
   10499                 :             :                                                                    "If either collation is nondeterministic, then both collations have to be the same.",
   10500                 :             :                                                                    strVal(list_nth(fkconstraint->fk_attrs, i)),
   10501                 :             :                                                                    strVal(list_nth(fkconstraint->pk_attrs, i)),
   10502                 :             :                                                                    get_collation_name(fkcoll),
   10503                 :             :                                                                    get_collation_name(pkcoll))));
   10504                 :          15 :                 }
   10505                 :             : 
   10506         [ +  + ]:         338 :                 if (old_check_ok)
   10507                 :             :                 {
   10508                 :             :                         /*
   10509                 :             :                          * When a pfeqop changes, revalidate the constraint.  We could
   10510                 :             :                          * permit intra-opfamily changes, but that adds subtle complexity
   10511                 :             :                          * without any concrete benefit for core types.  We need not
   10512                 :             :                          * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
   10513                 :             :                          */
   10514                 :           1 :                         old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
   10515                 :           2 :                         old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
   10516                 :           1 :                                                                         old_pfeqop_item);
   10517                 :           1 :                 }
   10518         [ +  + ]:         338 :                 if (old_check_ok)
   10519                 :             :                 {
   10520                 :           1 :                         Oid                     old_fktype;
   10521                 :           1 :                         Oid                     new_fktype;
   10522                 :           1 :                         CoercionPathType old_pathtype;
   10523                 :           1 :                         CoercionPathType new_pathtype;
   10524                 :           1 :                         Oid                     old_castfunc;
   10525                 :           1 :                         Oid                     new_castfunc;
   10526                 :           1 :                         Oid                     old_fkcoll;
   10527                 :           1 :                         Oid                     new_fkcoll;
   10528                 :           2 :                         Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
   10529                 :           1 :                                                                                                    fkattnum[i] - 1);
   10530                 :             : 
   10531                 :             :                         /*
   10532                 :             :                          * Identify coercion pathways from each of the old and new FK-side
   10533                 :             :                          * column types to the right (foreign) operand type of the pfeqop.
   10534                 :             :                          * We may assume that pg_constraint.conkey is not changing.
   10535                 :             :                          */
   10536                 :           1 :                         old_fktype = attr->atttypid;
   10537                 :           1 :                         new_fktype = fktype;
   10538                 :           1 :                         old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
   10539                 :             :                                                                                 &old_castfunc);
   10540                 :           1 :                         new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
   10541                 :             :                                                                                 &new_castfunc);
   10542                 :             : 
   10543                 :           1 :                         old_fkcoll = attr->attcollation;
   10544                 :           1 :                         new_fkcoll = fkcoll;
   10545                 :             : 
   10546                 :             :                         /*
   10547                 :             :                          * Upon a change to the cast from the FK column to its pfeqop
   10548                 :             :                          * operand, revalidate the constraint.  For this evaluation, a
   10549                 :             :                          * binary coercion cast is equivalent to no cast at all.  While
   10550                 :             :                          * type implementors should design implicit casts with an eye
   10551                 :             :                          * toward consistency of operations like equality, we cannot
   10552                 :             :                          * assume here that they have done so.
   10553                 :             :                          *
   10554                 :             :                          * A function with a polymorphic argument could change behavior
   10555                 :             :                          * arbitrarily in response to get_fn_expr_argtype().  Therefore,
   10556                 :             :                          * when the cast destination is polymorphic, we only avoid
   10557                 :             :                          * revalidation if the input type has not changed at all.  Given
   10558                 :             :                          * just the core data types and operator classes, this requirement
   10559                 :             :                          * prevents no would-be optimizations.
   10560                 :             :                          *
   10561                 :             :                          * If the cast converts from a base type to a domain thereon, then
   10562                 :             :                          * that domain type must be the opcintype of the unique index.
   10563                 :             :                          * Necessarily, the primary key column must then be of the domain
   10564                 :             :                          * type.  Since the constraint was previously valid, all values on
   10565                 :             :                          * the foreign side necessarily exist on the primary side and in
   10566                 :             :                          * turn conform to the domain.  Consequently, we need not treat
   10567                 :             :                          * domains specially here.
   10568                 :             :                          *
   10569                 :             :                          * If the collation changes, revalidation is required, unless both
   10570                 :             :                          * collations are deterministic, because those share the same
   10571                 :             :                          * notion of equality (because texteq reduces to bitwise
   10572                 :             :                          * equality).
   10573                 :             :                          *
   10574                 :             :                          * We need not directly consider the PK type.  It's necessarily
   10575                 :             :                          * binary coercible to the opcintype of the unique index column,
   10576                 :             :                          * and ri_triggers.c will only deal with PK datums in terms of
   10577                 :             :                          * that opcintype.  Changing the opcintype also changes pfeqop.
   10578                 :             :                          */
   10579         [ +  - ]:           2 :                         old_check_ok = (new_pathtype == old_pathtype &&
   10580         [ +  - ]:           1 :                                                         new_castfunc == old_castfunc &&
   10581   [ +  -  +  -  :           1 :                                                         (!IsPolymorphicType(pfeqop_right) ||
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  +  
                      - ]
   10582         [ +  - ]:           1 :                                                          new_fktype == old_fktype) &&
   10583         [ +  - ]:           1 :                                                         (new_fkcoll == old_fkcoll ||
   10584         [ #  # ]:           0 :                                                          (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
   10585                 :           1 :                 }
   10586                 :             : 
   10587                 :         338 :                 pfeqoperators[i] = pfeqop;
   10588                 :         338 :                 ppeqoperators[i] = ppeqop;
   10589                 :         338 :                 ffeqoperators[i] = ffeqop;
   10590                 :         338 :         }
   10591                 :             : 
   10592                 :             :         /*
   10593                 :             :          * For FKs with PERIOD we need additional operators to check whether the
   10594                 :             :          * referencing row's range is contained by the aggregated ranges of the
   10595                 :             :          * referenced row(s). For rangetypes and multirangetypes this is
   10596                 :             :          * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
   10597                 :             :          * support for now. FKs will look these up at "runtime", but we should
   10598                 :             :          * make sure the lookup works here, even if we don't use the values.
   10599                 :             :          */
   10600         [ +  + ]:         242 :         if (with_period)
   10601                 :             :         {
   10602                 :          24 :                 Oid                     periodoperoid;
   10603                 :          24 :                 Oid                     aggedperiodoperoid;
   10604                 :          24 :                 Oid                     intersectoperoid;
   10605                 :             : 
   10606                 :          24 :                 FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
   10607                 :             :                                                   &intersectoperoid);
   10608                 :          24 :         }
   10609                 :             : 
   10610                 :             :         /* First, create the constraint catalog entry itself. */
   10611                 :         484 :         address = addFkConstraint(addFkBothSides,
   10612                 :         242 :                                                           fkconstraint->conname, fkconstraint, rel, pkrel,
   10613                 :         242 :                                                           indexOid,
   10614                 :             :                                                           InvalidOid,   /* no parent constraint */
   10615                 :         242 :                                                           numfks,
   10616                 :         242 :                                                           pkattnum,
   10617                 :         242 :                                                           fkattnum,
   10618                 :         242 :                                                           pfeqoperators,
   10619                 :         242 :                                                           ppeqoperators,
   10620                 :         242 :                                                           ffeqoperators,
   10621                 :         242 :                                                           numfkdelsetcols,
   10622                 :         242 :                                                           fkdelsetcols,
   10623                 :             :                                                           false,
   10624                 :         242 :                                                           with_period);
   10625                 :             : 
   10626                 :             :         /* Next process the action triggers at the referenced side and recurse */
   10627                 :         484 :         addFkRecurseReferenced(fkconstraint, rel, pkrel,
   10628                 :         242 :                                                    indexOid,
   10629                 :         242 :                                                    address.objectId,
   10630                 :         242 :                                                    numfks,
   10631                 :         242 :                                                    pkattnum,
   10632                 :         242 :                                                    fkattnum,
   10633                 :         242 :                                                    pfeqoperators,
   10634                 :         242 :                                                    ppeqoperators,
   10635                 :         242 :                                                    ffeqoperators,
   10636                 :         242 :                                                    numfkdelsetcols,
   10637                 :         242 :                                                    fkdelsetcols,
   10638                 :         242 :                                                    old_check_ok,
   10639                 :             :                                                    InvalidOid, InvalidOid,
   10640                 :         242 :                                                    with_period);
   10641                 :             : 
   10642                 :             :         /* Lastly create the check triggers at the referencing side and recurse */
   10643                 :         484 :         addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
   10644                 :         242 :                                                         indexOid,
   10645                 :         242 :                                                         address.objectId,
   10646                 :         242 :                                                         numfks,
   10647                 :         242 :                                                         pkattnum,
   10648                 :         242 :                                                         fkattnum,
   10649                 :         242 :                                                         pfeqoperators,
   10650                 :         242 :                                                         ppeqoperators,
   10651                 :         242 :                                                         ffeqoperators,
   10652                 :         242 :                                                         numfkdelsetcols,
   10653                 :         242 :                                                         fkdelsetcols,
   10654                 :         242 :                                                         old_check_ok,
   10655                 :         242 :                                                         lockmode,
   10656                 :             :                                                         InvalidOid, InvalidOid,
   10657                 :         242 :                                                         with_period);
   10658                 :             : 
   10659                 :             :         /*
   10660                 :             :          * Done.  Close pk table, but keep lock until we've committed.
   10661                 :             :          */
   10662                 :         242 :         table_close(pkrel, NoLock);
   10663                 :             : 
   10664                 :             :         return address;
   10665                 :         242 : }
   10666                 :             : 
   10667                 :             : /*
   10668                 :             :  * validateFkOnDeleteSetColumns
   10669                 :             :  *              Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
   10670                 :             :  *              column lists are valid.
   10671                 :             :  *
   10672                 :             :  * If there are duplicates in the fksetcolsattnums[] array, this silently
   10673                 :             :  * removes the dups.  The new count of numfksetcols is returned.
   10674                 :             :  */
   10675                 :             : static int
   10676                 :         315 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
   10677                 :             :                                                          int numfksetcols, int16 *fksetcolsattnums,
   10678                 :             :                                                          List *fksetcols)
   10679                 :             : {
   10680                 :         315 :         int                     numcolsout = 0;
   10681                 :             : 
   10682         [ +  + ]:         320 :         for (int i = 0; i < numfksetcols; i++)
   10683                 :             :         {
   10684                 :           6 :                 int16           setcol_attnum = fksetcolsattnums[i];
   10685                 :           6 :                 bool            seen = false;
   10686                 :             : 
   10687                 :             :                 /* Make sure it's in fkattnums[] */
   10688         [ +  + ]:          16 :                 for (int j = 0; j < numfks; j++)
   10689                 :             :                 {
   10690         [ +  + ]:          10 :                         if (fkattnums[j] == setcol_attnum)
   10691                 :             :                         {
   10692                 :           5 :                                 seen = true;
   10693                 :           5 :                                 break;
   10694                 :             :                         }
   10695                 :           5 :                 }
   10696                 :             : 
   10697         [ +  + ]:           6 :                 if (!seen)
   10698                 :             :                 {
   10699                 :           1 :                         char       *col = strVal(list_nth(fksetcols, i));
   10700                 :             : 
   10701   [ +  -  +  - ]:           1 :                         ereport(ERROR,
   10702                 :             :                                         (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   10703                 :             :                                          errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
   10704                 :           0 :                 }
   10705                 :             : 
   10706                 :             :                 /* Now check for dups */
   10707                 :           5 :                 seen = false;
   10708         [ +  + ]:           6 :                 for (int j = 0; j < numcolsout; j++)
   10709                 :             :                 {
   10710         [ +  - ]:           1 :                         if (fksetcolsattnums[j] == setcol_attnum)
   10711                 :             :                         {
   10712                 :           1 :                                 seen = true;
   10713                 :           1 :                                 break;
   10714                 :             :                         }
   10715                 :           0 :                 }
   10716         [ +  + ]:           5 :                 if (!seen)
   10717                 :           4 :                         fksetcolsattnums[numcolsout++] = setcol_attnum;
   10718                 :           5 :         }
   10719                 :         628 :         return numcolsout;
   10720                 :         314 : }
   10721                 :             : 
   10722                 :             : /*
   10723                 :             :  * addFkConstraint
   10724                 :             :  *              Install pg_constraint entries to implement a foreign key constraint.
   10725                 :             :  *              Caller must separately invoke addFkRecurseReferenced and
   10726                 :             :  *              addFkRecurseReferencing, as appropriate, to install pg_trigger entries
   10727                 :             :  *              and (for partitioned tables) recurse to partitions.
   10728                 :             :  *
   10729                 :             :  * fkside: the side of the FK (or both) to create.  Caller should
   10730                 :             :  *      call addFkRecurseReferenced if this is addFkReferencedSide,
   10731                 :             :  *      addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
   10732                 :             :  *      addFkBothSides.
   10733                 :             :  * constraintname: the base name for the constraint being added,
   10734                 :             :  *      copied to fkconstraint->conname if the latter is not set
   10735                 :             :  * fkconstraint: the constraint being added
   10736                 :             :  * rel: the root referencing relation
   10737                 :             :  * pkrel: the referenced relation; might be a partition, if recursing
   10738                 :             :  * indexOid: the OID of the index (on pkrel) implementing this constraint
   10739                 :             :  * parentConstr: the OID of a parent constraint; InvalidOid if this is a
   10740                 :             :  *      top-level constraint
   10741                 :             :  * numfks: the number of columns in the foreign key
   10742                 :             :  * pkattnum: the attnum array of referenced attributes
   10743                 :             :  * fkattnum: the attnum array of referencing attributes
   10744                 :             :  * pf/pp/ffeqoperators: OID array of operators between columns
   10745                 :             :  * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
   10746                 :             :  *      (...) clause
   10747                 :             :  * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
   10748                 :             :  *      NULL/DEFAULT clause
   10749                 :             :  * with_period: true if this is a temporal FK
   10750                 :             :  */
   10751                 :             : static ObjectAddress
   10752                 :         524 : addFkConstraint(addFkConstraintSides fkside,
   10753                 :             :                                 char *constraintname, Constraint *fkconstraint,
   10754                 :             :                                 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
   10755                 :             :                                 int numfks, int16 *pkattnum,
   10756                 :             :                                 int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
   10757                 :             :                                 Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
   10758                 :             :                                 bool is_internal, bool with_period)
   10759                 :             : {
   10760                 :             :         ObjectAddress address;
   10761                 :         524 :         Oid                     constrOid;
   10762                 :         524 :         char       *conname;
   10763                 :         524 :         bool            conislocal;
   10764                 :         524 :         int16           coninhcount;
   10765                 :         524 :         bool            connoinherit;
   10766                 :             : 
   10767                 :             :         /*
   10768                 :             :          * Verify relkind for each referenced partition.  At the top level, this
   10769                 :             :          * is redundant with a previous check, but we need it when recursing.
   10770                 :             :          */
   10771   [ +  +  +  - ]:         524 :         if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
   10772                 :         116 :                 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
   10773   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   10774                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10775                 :             :                                  errmsg("referenced relation \"%s\" is not a table",
   10776                 :             :                                                 RelationGetRelationName(pkrel))));
   10777                 :             : 
   10778                 :             :         /*
   10779                 :             :          * Caller supplies us with a constraint name; however, it may be used in
   10780                 :             :          * this partition, so come up with a different one in that case.  Unless
   10781                 :             :          * truncation to NAMEDATALEN dictates otherwise, the new name will be the
   10782                 :             :          * supplied name with an underscore and digit(s) appended.
   10783                 :             :          */
   10784         [ +  + ]:         524 :         if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
   10785                 :         524 :                                                          RelationGetRelid(rel),
   10786                 :         524 :                                                          constraintname))
   10787                 :         340 :                 conname = ChooseConstraintName(constraintname,
   10788                 :             :                                                                            NULL,
   10789                 :             :                                                                            "",
   10790                 :         170 :                                                                            RelationGetNamespace(rel), NIL);
   10791                 :             :         else
   10792                 :         354 :                 conname = constraintname;
   10793                 :             : 
   10794         [ +  + ]:         524 :         if (fkconstraint->conname == NULL)
   10795                 :          67 :                 fkconstraint->conname = pstrdup(conname);
   10796                 :             : 
   10797         [ +  + ]:         524 :         if (OidIsValid(parentConstr))
   10798                 :             :         {
   10799                 :         288 :                 conislocal = false;
   10800                 :         288 :                 coninhcount = 1;
   10801                 :         288 :                 connoinherit = false;
   10802                 :         288 :         }
   10803                 :             :         else
   10804                 :             :         {
   10805                 :         236 :                 conislocal = true;
   10806                 :         236 :                 coninhcount = 0;
   10807                 :             : 
   10808                 :             :                 /*
   10809                 :             :                  * always inherit for partitioned tables, never for legacy inheritance
   10810                 :             :                  */
   10811                 :         236 :                 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
   10812                 :             :         }
   10813                 :             : 
   10814                 :             :         /*
   10815                 :             :          * Record the FK constraint in pg_constraint.
   10816                 :             :          */
   10817                 :        1048 :         constrOid = CreateConstraintEntry(conname,
   10818                 :         524 :                                                                           RelationGetNamespace(rel),
   10819                 :             :                                                                           CONSTRAINT_FOREIGN,
   10820                 :         524 :                                                                           fkconstraint->deferrable,
   10821                 :         524 :                                                                           fkconstraint->initdeferred,
   10822                 :         524 :                                                                           fkconstraint->is_enforced,
   10823                 :         524 :                                                                           fkconstraint->initially_valid,
   10824                 :         524 :                                                                           parentConstr,
   10825                 :         524 :                                                                           RelationGetRelid(rel),
   10826                 :         524 :                                                                           fkattnum,
   10827                 :         524 :                                                                           numfks,
   10828                 :         524 :                                                                           numfks,
   10829                 :             :                                                                           InvalidOid,   /* not a domain constraint */
   10830                 :         524 :                                                                           indexOid,
   10831                 :         524 :                                                                           RelationGetRelid(pkrel),
   10832                 :         524 :                                                                           pkattnum,
   10833                 :         524 :                                                                           pfeqoperators,
   10834                 :         524 :                                                                           ppeqoperators,
   10835                 :         524 :                                                                           ffeqoperators,
   10836                 :         524 :                                                                           numfks,
   10837                 :         524 :                                                                           fkconstraint->fk_upd_action,
   10838                 :         524 :                                                                           fkconstraint->fk_del_action,
   10839                 :         524 :                                                                           fkdelsetcols,
   10840                 :         524 :                                                                           numfkdelsetcols,
   10841                 :         524 :                                                                           fkconstraint->fk_matchtype,
   10842                 :             :                                                                           NULL, /* no exclusion constraint */
   10843                 :             :                                                                           NULL, /* no check constraint */
   10844                 :             :                                                                           NULL,
   10845                 :         524 :                                                                           conislocal,   /* islocal */
   10846                 :         524 :                                                                           coninhcount,  /* inhcount */
   10847                 :         524 :                                                                           connoinherit, /* conNoInherit */
   10848                 :         524 :                                                                           with_period,  /* conPeriod */
   10849                 :         524 :                                                                           is_internal); /* is_internal */
   10850                 :             : 
   10851                 :         524 :         ObjectAddressSet(address, ConstraintRelationId, constrOid);
   10852                 :             : 
   10853                 :             :         /*
   10854                 :             :          * In partitioning cases, create the dependency entries for this
   10855                 :             :          * constraint.  (For non-partitioned cases, relevant entries were created
   10856                 :             :          * by CreateConstraintEntry.)
   10857                 :             :          *
   10858                 :             :          * On the referenced side, we need the constraint to have an internal
   10859                 :             :          * dependency on its parent constraint; this means that this constraint
   10860                 :             :          * cannot be dropped on its own -- only through the parent constraint. It
   10861                 :             :          * also means the containing partition cannot be dropped on its own, but
   10862                 :             :          * it can be detached, at which point this dependency is removed (after
   10863                 :             :          * verifying that no rows are referenced via this FK.)
   10864                 :             :          *
   10865                 :             :          * When processing the referencing side, we link the constraint via the
   10866                 :             :          * special partitioning dependencies: the parent constraint is the primary
   10867                 :             :          * dependent, and the partition on which the foreign key exists is the
   10868                 :             :          * secondary dependency.  That way, this constraint is dropped if either
   10869                 :             :          * of these objects is.
   10870                 :             :          *
   10871                 :             :          * Note that this is only necessary for the subsidiary pg_constraint rows
   10872                 :             :          * in partitions; the topmost row doesn't need any of this.
   10873                 :             :          */
   10874         [ +  + ]:         524 :         if (OidIsValid(parentConstr))
   10875                 :             :         {
   10876                 :         288 :                 ObjectAddress referenced;
   10877                 :             : 
   10878                 :         288 :                 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
   10879                 :             : 
   10880         [ +  - ]:         288 :                 Assert(fkside != addFkBothSides);
   10881         [ +  + ]:         288 :                 if (fkside == addFkReferencedSide)
   10882                 :         169 :                         recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
   10883                 :             :                 else
   10884                 :             :                 {
   10885                 :         119 :                         recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
   10886                 :         119 :                         ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
   10887                 :         119 :                         recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
   10888                 :             :                 }
   10889                 :         288 :         }
   10890                 :             : 
   10891                 :             :         /* make new constraint visible, in case we add more */
   10892                 :         524 :         CommandCounterIncrement();
   10893                 :             : 
   10894                 :             :         return address;
   10895                 :         524 : }
   10896                 :             : 
   10897                 :             : /*
   10898                 :             :  * addFkRecurseReferenced
   10899                 :             :  *              Recursive helper for the referenced side of foreign key creation,
   10900                 :             :  *              which creates the action triggers and recurses
   10901                 :             :  *
   10902                 :             :  * If the referenced relation is a plain relation, create the necessary action
   10903                 :             :  * triggers that implement the constraint.  If the referenced relation is a
   10904                 :             :  * partitioned table, then we create a pg_constraint row referencing the parent
   10905                 :             :  * of the referencing side for it and recurse on this routine for each
   10906                 :             :  * partition.
   10907                 :             :  *
   10908                 :             :  * fkconstraint: the constraint being added
   10909                 :             :  * rel: the root referencing relation
   10910                 :             :  * pkrel: the referenced relation; might be a partition, if recursing
   10911                 :             :  * indexOid: the OID of the index (on pkrel) implementing this constraint
   10912                 :             :  * parentConstr: the OID of a parent constraint; InvalidOid if this is a
   10913                 :             :  *      top-level constraint
   10914                 :             :  * numfks: the number of columns in the foreign key
   10915                 :             :  * pkattnum: the attnum array of referenced attributes
   10916                 :             :  * fkattnum: the attnum array of referencing attributes
   10917                 :             :  * numfkdelsetcols: the number of columns in the ON DELETE SET
   10918                 :             :  *      NULL/DEFAULT (...) clause
   10919                 :             :  * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
   10920                 :             :  *      NULL/DEFAULT clause
   10921                 :             :  * pf/pp/ffeqoperators: OID array of operators between columns
   10922                 :             :  * old_check_ok: true if this constraint replaces an existing one that
   10923                 :             :  *      was already validated (thus this one doesn't need validation)
   10924                 :             :  * parentDelTrigger and parentUpdTrigger: when recursively called on a
   10925                 :             :  *      partition, the OIDs of the parent action triggers for DELETE and
   10926                 :             :  *      UPDATE respectively.
   10927                 :             :  * with_period: true if this is a temporal FK
   10928                 :             :  */
   10929                 :             : static void
   10930                 :         423 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
   10931                 :             :                                            Relation pkrel, Oid indexOid, Oid parentConstr,
   10932                 :             :                                            int numfks,
   10933                 :             :                                            int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
   10934                 :             :                                            Oid *ppeqoperators, Oid *ffeqoperators,
   10935                 :             :                                            int numfkdelsetcols, int16 *fkdelsetcols,
   10936                 :             :                                            bool old_check_ok,
   10937                 :             :                                            Oid parentDelTrigger, Oid parentUpdTrigger,
   10938                 :             :                                            bool with_period)
   10939                 :             : {
   10940                 :         423 :         Oid                     deleteTriggerOid = InvalidOid,
   10941                 :         423 :                                 updateTriggerOid = InvalidOid;
   10942                 :             : 
   10943         [ +  - ]:         423 :         Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
   10944         [ +  - ]:         423 :         Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
   10945                 :             : 
   10946                 :             :         /*
   10947                 :             :          * Create action triggers to enforce the constraint, or skip them if the
   10948                 :             :          * constraint is NOT ENFORCED.
   10949                 :             :          */
   10950         [ +  + ]:         423 :         if (fkconstraint->is_enforced)
   10951                 :         822 :                 createForeignKeyActionTriggers(RelationGetRelid(rel),
   10952                 :         411 :                                                                            RelationGetRelid(pkrel),
   10953                 :         411 :                                                                            fkconstraint,
   10954                 :         411 :                                                                            parentConstr, indexOid,
   10955                 :         411 :                                                                            parentDelTrigger, parentUpdTrigger,
   10956                 :             :                                                                            &deleteTriggerOid, &updateTriggerOid);
   10957                 :             : 
   10958                 :             :         /*
   10959                 :             :          * If the referenced table is partitioned, recurse on ourselves to handle
   10960                 :             :          * each partition.  We need one pg_constraint row created for each
   10961                 :             :          * partition in addition to the pg_constraint row for the parent table.
   10962                 :             :          */
   10963         [ +  + ]:         423 :         if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10964                 :             :         {
   10965                 :          75 :                 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
   10966                 :             : 
   10967         [ +  + ]:         200 :                 for (int i = 0; i < pd->nparts; i++)
   10968                 :             :                 {
   10969                 :         125 :                         Relation        partRel;
   10970                 :         125 :                         AttrMap    *map;
   10971                 :         125 :                         AttrNumber *mapped_pkattnum;
   10972                 :         125 :                         Oid                     partIndexId;
   10973                 :         125 :                         ObjectAddress address;
   10974                 :             : 
   10975                 :             :                         /* XXX would it be better to acquire these locks beforehand? */
   10976                 :         125 :                         partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
   10977                 :             : 
   10978                 :             :                         /*
   10979                 :             :                          * Map the attribute numbers in the referenced side of the FK
   10980                 :             :                          * definition to match the partition's column layout.
   10981                 :             :                          */
   10982                 :         250 :                         map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
   10983                 :         125 :                                                                                            RelationGetDescr(pkrel),
   10984                 :             :                                                                                            false);
   10985         [ +  + ]:         125 :                         if (map)
   10986                 :             :                         {
   10987                 :          21 :                                 mapped_pkattnum = palloc_array(AttrNumber, numfks);
   10988         [ +  + ]:          44 :                                 for (int j = 0; j < numfks; j++)
   10989                 :          23 :                                         mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
   10990                 :          21 :                         }
   10991                 :             :                         else
   10992                 :         104 :                                 mapped_pkattnum = pkattnum;
   10993                 :             : 
   10994                 :             :                         /* Determine the index to use at this level */
   10995                 :         125 :                         partIndexId = index_get_partition(partRel, indexOid);
   10996         [ +  - ]:         125 :                         if (!OidIsValid(partIndexId))
   10997   [ #  #  #  # ]:           0 :                                 elog(ERROR, "index for %u not found in partition %s",
   10998                 :             :                                          indexOid, RelationGetRelationName(partRel));
   10999                 :             : 
   11000                 :             :                         /* Create entry at this level ... */
   11001                 :         250 :                         address = addFkConstraint(addFkReferencedSide,
   11002                 :         125 :                                                                           fkconstraint->conname, fkconstraint, rel,
   11003                 :         125 :                                                                           partRel, partIndexId, parentConstr,
   11004                 :         125 :                                                                           numfks, mapped_pkattnum,
   11005                 :         125 :                                                                           fkattnum, pfeqoperators, ppeqoperators,
   11006                 :         125 :                                                                           ffeqoperators, numfkdelsetcols,
   11007                 :         125 :                                                                           fkdelsetcols, true, with_period);
   11008                 :             :                         /* ... and recurse to our children */
   11009                 :         250 :                         addFkRecurseReferenced(fkconstraint, rel, partRel,
   11010                 :         125 :                                                                    partIndexId, address.objectId, numfks,
   11011                 :         125 :                                                                    mapped_pkattnum, fkattnum,
   11012                 :         125 :                                                                    pfeqoperators, ppeqoperators, ffeqoperators,
   11013                 :         125 :                                                                    numfkdelsetcols, fkdelsetcols,
   11014                 :         125 :                                                                    old_check_ok,
   11015                 :         125 :                                                                    deleteTriggerOid, updateTriggerOid,
   11016                 :         125 :                                                                    with_period);
   11017                 :             : 
   11018                 :             :                         /* Done -- clean up (but keep the lock) */
   11019                 :         125 :                         table_close(partRel, NoLock);
   11020         [ +  + ]:         125 :                         if (map)
   11021                 :             :                         {
   11022                 :          21 :                                 pfree(mapped_pkattnum);
   11023                 :          21 :                                 free_attrmap(map);
   11024                 :          21 :                         }
   11025                 :         125 :                 }
   11026                 :          75 :         }
   11027                 :         423 : }
   11028                 :             : 
   11029                 :             : /*
   11030                 :             :  * addFkRecurseReferencing
   11031                 :             :  *              Recursive helper for the referencing side of foreign key creation,
   11032                 :             :  *              which creates the check triggers and recurses
   11033                 :             :  *
   11034                 :             :  * If the referencing relation is a plain relation, create the necessary check
   11035                 :             :  * triggers that implement the constraint, and set up for Phase 3 constraint
   11036                 :             :  * verification.  If the referencing relation is a partitioned table, then
   11037                 :             :  * we create a pg_constraint row for it and recurse on this routine for each
   11038                 :             :  * partition.
   11039                 :             :  *
   11040                 :             :  * We assume that the referenced relation is locked against concurrent
   11041                 :             :  * deletions.  If it's a partitioned relation, every partition must be so
   11042                 :             :  * locked.
   11043                 :             :  *
   11044                 :             :  * wqueue: the ALTER TABLE work queue; NULL when not running as part
   11045                 :             :  *      of an ALTER TABLE sequence.
   11046                 :             :  * fkconstraint: the constraint being added
   11047                 :             :  * rel: the referencing relation; might be a partition, if recursing
   11048                 :             :  * pkrel: the root referenced relation
   11049                 :             :  * indexOid: the OID of the index (on pkrel) implementing this constraint
   11050                 :             :  * parentConstr: the OID of the parent constraint (there is always one)
   11051                 :             :  * numfks: the number of columns in the foreign key
   11052                 :             :  * pkattnum: the attnum array of referenced attributes
   11053                 :             :  * fkattnum: the attnum array of referencing attributes
   11054                 :             :  * pf/pp/ffeqoperators: OID array of operators between columns
   11055                 :             :  * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
   11056                 :             :  *      (...) clause
   11057                 :             :  * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
   11058                 :             :  *      NULL/DEFAULT clause
   11059                 :             :  * old_check_ok: true if this constraint replaces an existing one that
   11060                 :             :  *      was already validated (thus this one doesn't need validation)
   11061                 :             :  * lockmode: the lockmode to acquire on partitions when recursing
   11062                 :             :  * parentInsTrigger and parentUpdTrigger: when being recursively called on
   11063                 :             :  *      a partition, the OIDs of the parent check triggers for INSERT and
   11064                 :             :  *      UPDATE respectively.
   11065                 :             :  * with_period: true if this is a temporal FK
   11066                 :             :  */
   11067                 :             : static void
   11068                 :         354 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
   11069                 :             :                                                 Relation pkrel, Oid indexOid, Oid parentConstr,
   11070                 :             :                                                 int numfks, int16 *pkattnum, int16 *fkattnum,
   11071                 :             :                                                 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
   11072                 :             :                                                 int numfkdelsetcols, int16 *fkdelsetcols,
   11073                 :             :                                                 bool old_check_ok, LOCKMODE lockmode,
   11074                 :             :                                                 Oid parentInsTrigger, Oid parentUpdTrigger,
   11075                 :             :                                                 bool with_period)
   11076                 :             : {
   11077                 :         354 :         Oid                     insertTriggerOid = InvalidOid,
   11078                 :         354 :                                 updateTriggerOid = InvalidOid;
   11079                 :             : 
   11080         [ +  - ]:         354 :         Assert(OidIsValid(parentConstr));
   11081         [ +  - ]:         354 :         Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
   11082         [ +  - ]:         354 :         Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
   11083                 :             : 
   11084         [ +  - ]:         354 :         if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   11085   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   11086                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11087                 :             :                                  errmsg("foreign key constraints are not supported on foreign tables")));
   11088                 :             : 
   11089                 :             :         /*
   11090                 :             :          * Add check triggers if the constraint is ENFORCED, and if needed,
   11091                 :             :          * schedule them to be checked in Phase 3.
   11092                 :             :          *
   11093                 :             :          * If the relation is partitioned, drill down to do it to its partitions.
   11094                 :             :          */
   11095         [ +  + ]:         354 :         if (fkconstraint->is_enforced)
   11096                 :         694 :                 createForeignKeyCheckTriggers(RelationGetRelid(rel),
   11097                 :         347 :                                                                           RelationGetRelid(pkrel),
   11098                 :         347 :                                                                           fkconstraint,
   11099                 :         347 :                                                                           parentConstr,
   11100                 :         347 :                                                                           indexOid,
   11101                 :         347 :                                                                           parentInsTrigger, parentUpdTrigger,
   11102                 :             :                                                                           &insertTriggerOid, &updateTriggerOid);
   11103                 :             : 
   11104         [ +  + ]:         354 :         if (rel->rd_rel->relkind == RELKIND_RELATION)
   11105                 :             :         {
   11106                 :             :                 /*
   11107                 :             :                  * Tell Phase 3 to check that the constraint is satisfied by existing
   11108                 :             :                  * rows. We can skip this during table creation, when constraint is
   11109                 :             :                  * specified as NOT ENFORCED, or when requested explicitly by
   11110                 :             :                  * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
   11111                 :             :                  * recreating a constraint following a SET DATA TYPE operation that
   11112                 :             :                  * did not impugn its validity.
   11113                 :             :                  */
   11114   [ +  +  +  +  :         287 :                 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
             +  +  -  + ]
   11115                 :         109 :                         fkconstraint->is_enforced)
   11116                 :             :                 {
   11117                 :         109 :                         NewConstraint *newcon;
   11118                 :         109 :                         AlteredTableInfo *tab;
   11119                 :             : 
   11120                 :         109 :                         tab = ATGetQueueEntry(wqueue, rel);
   11121                 :             : 
   11122                 :         109 :                         newcon = palloc0_object(NewConstraint);
   11123                 :         109 :                         newcon->name = get_constraint_name(parentConstr);
   11124                 :         109 :                         newcon->contype = CONSTR_FOREIGN;
   11125                 :         109 :                         newcon->refrelid = RelationGetRelid(pkrel);
   11126                 :         109 :                         newcon->refindid = indexOid;
   11127                 :         109 :                         newcon->conid = parentConstr;
   11128                 :         109 :                         newcon->conwithperiod = fkconstraint->fk_with_period;
   11129                 :         109 :                         newcon->qual = (Node *) fkconstraint;
   11130                 :             : 
   11131                 :         109 :                         tab->constraints = lappend(tab->constraints, newcon);
   11132                 :         109 :                 }
   11133                 :         287 :         }
   11134         [ -  + ]:          67 :         else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11135                 :             :         {
   11136                 :          67 :                 PartitionDesc pd = RelationGetPartitionDesc(rel, true);
   11137                 :          67 :                 Relation        trigrel;
   11138                 :             : 
   11139                 :             :                 /*
   11140                 :             :                  * Triggers of the foreign keys will be manipulated a bunch of times
   11141                 :             :                  * in the loop below.  To avoid repeatedly opening/closing the trigger
   11142                 :             :                  * catalog relation, we open it here and pass it to the subroutines
   11143                 :             :                  * called below.
   11144                 :             :                  */
   11145                 :          67 :                 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   11146                 :             : 
   11147                 :             :                 /*
   11148                 :             :                  * Recurse to take appropriate action on each partition; either we
   11149                 :             :                  * find an existing constraint to reparent to ours, or we create a new
   11150                 :             :                  * one.
   11151                 :             :                  */
   11152         [ +  + ]:         121 :                 for (int i = 0; i < pd->nparts; i++)
   11153                 :             :                 {
   11154                 :          54 :                         Relation        partition = table_open(pd->oids[i], lockmode);
   11155                 :          54 :                         List       *partFKs;
   11156                 :          54 :                         AttrMap    *attmap;
   11157                 :          54 :                         AttrNumber      mapped_fkattnum[INDEX_MAX_KEYS];
   11158                 :          54 :                         bool            attached;
   11159                 :          54 :                         ObjectAddress address;
   11160                 :             : 
   11161                 :          54 :                         CheckAlterTableIsSafe(partition);
   11162                 :             : 
   11163                 :         108 :                         attmap = build_attrmap_by_name(RelationGetDescr(partition),
   11164                 :          54 :                                                                                    RelationGetDescr(rel),
   11165                 :             :                                                                                    false);
   11166         [ +  + ]:         141 :                         for (int j = 0; j < numfks; j++)
   11167                 :          87 :                                 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
   11168                 :             : 
   11169                 :             :                         /* Check whether an existing constraint can be repurposed */
   11170                 :          54 :                         partFKs = copyObject(RelationGetFKeyList(partition));
   11171                 :          54 :                         attached = false;
   11172   [ +  +  +  +  :         110 :                         foreach_node(ForeignKeyCacheInfo, fk, partFKs)
             -  +  +  + ]
   11173                 :             :                         {
   11174   [ +  -  +  - ]:           4 :                                 if (tryAttachPartitionForeignKey(wqueue,
   11175                 :           2 :                                                                                                  fk,
   11176                 :           2 :                                                                                                  partition,
   11177                 :           2 :                                                                                                  parentConstr,
   11178                 :           2 :                                                                                                  numfks,
   11179                 :           2 :                                                                                                  mapped_fkattnum,
   11180                 :           2 :                                                                                                  pkattnum,
   11181                 :           2 :                                                                                                  pfeqoperators,
   11182                 :           2 :                                                                                                  insertTriggerOid,
   11183                 :           2 :                                                                                                  updateTriggerOid,
   11184                 :           2 :                                                                                                  trigrel))
   11185                 :             :                                 {
   11186                 :           2 :                                         attached = true;
   11187                 :           2 :                                         break;
   11188                 :             :                                 }
   11189                 :          54 :                         }
   11190         [ +  + ]:          54 :                         if (attached)
   11191                 :             :                         {
   11192                 :           2 :                                 table_close(partition, NoLock);
   11193                 :           2 :                                 continue;
   11194                 :             :                         }
   11195                 :             : 
   11196                 :             :                         /*
   11197                 :             :                          * No luck finding a good constraint to reuse; create our own.
   11198                 :             :                          */
   11199                 :         104 :                         address = addFkConstraint(addFkReferencingSide,
   11200                 :          52 :                                                                           fkconstraint->conname, fkconstraint,
   11201                 :          52 :                                                                           partition, pkrel, indexOid, parentConstr,
   11202                 :          52 :                                                                           numfks, pkattnum,
   11203                 :          52 :                                                                           mapped_fkattnum, pfeqoperators,
   11204                 :          52 :                                                                           ppeqoperators, ffeqoperators,
   11205                 :          52 :                                                                           numfkdelsetcols, fkdelsetcols, true,
   11206                 :          52 :                                                                           with_period);
   11207                 :             : 
   11208                 :             :                         /* call ourselves to finalize the creation and we're done */
   11209                 :         104 :                         addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
   11210                 :          52 :                                                                         indexOid,
   11211                 :          52 :                                                                         address.objectId,
   11212                 :          52 :                                                                         numfks,
   11213                 :          52 :                                                                         pkattnum,
   11214                 :          52 :                                                                         mapped_fkattnum,
   11215                 :          52 :                                                                         pfeqoperators,
   11216                 :          52 :                                                                         ppeqoperators,
   11217                 :          52 :                                                                         ffeqoperators,
   11218                 :          52 :                                                                         numfkdelsetcols,
   11219                 :          52 :                                                                         fkdelsetcols,
   11220                 :          52 :                                                                         old_check_ok,
   11221                 :          52 :                                                                         lockmode,
   11222                 :          52 :                                                                         insertTriggerOid,
   11223                 :          52 :                                                                         updateTriggerOid,
   11224                 :          52 :                                                                         with_period);
   11225                 :             : 
   11226                 :          52 :                         table_close(partition, NoLock);
   11227      [ -  +  + ]:          54 :                 }
   11228                 :             : 
   11229                 :          67 :                 table_close(trigrel, RowExclusiveLock);
   11230                 :          67 :         }
   11231                 :         354 : }
   11232                 :             : 
   11233                 :             : /*
   11234                 :             :  * CloneForeignKeyConstraints
   11235                 :             :  *              Clone foreign keys from a partitioned table to a newly acquired
   11236                 :             :  *              partition.
   11237                 :             :  *
   11238                 :             :  * partitionRel is a partition of parentRel, so we can be certain that it has
   11239                 :             :  * the same columns with the same datatypes.  The columns may be in different
   11240                 :             :  * order, though.
   11241                 :             :  *
   11242                 :             :  * wqueue must be passed to set up phase 3 constraint checking, unless the
   11243                 :             :  * referencing-side partition is known to be empty (such as in CREATE TABLE /
   11244                 :             :  * PARTITION OF).
   11245                 :             :  */
   11246                 :             : static void
   11247                 :        1500 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
   11248                 :             :                                                    Relation partitionRel)
   11249                 :             : {
   11250                 :             :         /* This only works for declarative partitioning */
   11251         [ +  - ]:        1500 :         Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   11252                 :             : 
   11253                 :             :         /*
   11254                 :             :          * First, clone constraints where the parent is on the referencing side.
   11255                 :             :          */
   11256                 :        1500 :         CloneFkReferencing(wqueue, parentRel, partitionRel);
   11257                 :             : 
   11258                 :             :         /*
   11259                 :             :          * Clone constraints for which the parent is on the referenced side.
   11260                 :             :          */
   11261                 :        1500 :         CloneFkReferenced(parentRel, partitionRel);
   11262                 :        1500 : }
   11263                 :             : 
   11264                 :             : /*
   11265                 :             :  * CloneFkReferenced
   11266                 :             :  *              Subroutine for CloneForeignKeyConstraints
   11267                 :             :  *
   11268                 :             :  * Find all the FKs that have the parent relation on the referenced side;
   11269                 :             :  * clone those constraints to the given partition.  This is to be called
   11270                 :             :  * when the partition is being created or attached.
   11271                 :             :  *
   11272                 :             :  * This recurses to partitions, if the relation being attached is partitioned.
   11273                 :             :  * Recursion is done by calling addFkRecurseReferenced.
   11274                 :             :  */
   11275                 :             : static void
   11276                 :        1497 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
   11277                 :             : {
   11278                 :        1497 :         Relation        pg_constraint;
   11279                 :        1497 :         AttrMap    *attmap;
   11280                 :        1497 :         ListCell   *cell;
   11281                 :        1497 :         SysScanDesc scan;
   11282                 :        1497 :         ScanKeyData key[2];
   11283                 :        1497 :         HeapTuple       tuple;
   11284                 :        1497 :         List       *clone = NIL;
   11285                 :        1497 :         Relation        trigrel;
   11286                 :             : 
   11287                 :             :         /*
   11288                 :             :          * Search for any constraints where this partition's parent is in the
   11289                 :             :          * referenced side.  However, we must not clone any constraint whose
   11290                 :             :          * parent constraint is also going to be cloned, to avoid duplicates.  So
   11291                 :             :          * do it in two steps: first construct the list of constraints to clone,
   11292                 :             :          * then go over that list cloning those whose parents are not in the list.
   11293                 :             :          * (We must not rely on the parent being seen first, since the catalog
   11294                 :             :          * scan could return children first.)
   11295                 :             :          */
   11296                 :        1497 :         pg_constraint = table_open(ConstraintRelationId, RowShareLock);
   11297                 :        2994 :         ScanKeyInit(&key[0],
   11298                 :             :                                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   11299                 :        1497 :                                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
   11300                 :        2994 :         ScanKeyInit(&key[1],
   11301                 :             :                                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   11302                 :        1497 :                                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   11303                 :             :         /* This is a seqscan, as we don't have a usable index ... */
   11304                 :        2994 :         scan = systable_beginscan(pg_constraint, InvalidOid, true,
   11305                 :        1497 :                                                           NULL, 2, key);
   11306         [ +  + ]:        1578 :         while ((tuple = systable_getnext(scan)) != NULL)
   11307                 :             :         {
   11308                 :          81 :                 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   11309                 :             : 
   11310                 :          81 :                 clone = lappend_oid(clone, constrForm->oid);
   11311                 :          81 :         }
   11312                 :        1497 :         systable_endscan(scan);
   11313                 :        1497 :         table_close(pg_constraint, RowShareLock);
   11314                 :             : 
   11315                 :             :         /*
   11316                 :             :          * Triggers of the foreign keys will be manipulated a bunch of times in
   11317                 :             :          * the loop below.  To avoid repeatedly opening/closing the trigger
   11318                 :             :          * catalog relation, we open it here and pass it to the subroutines called
   11319                 :             :          * below.
   11320                 :             :          */
   11321                 :        1497 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   11322                 :             : 
   11323                 :        2994 :         attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
   11324                 :        1497 :                                                                    RelationGetDescr(parentRel),
   11325                 :             :                                                                    false);
   11326   [ +  +  +  +  :        1578 :         foreach(cell, clone)
                   +  + ]
   11327                 :             :         {
   11328                 :          81 :                 Oid                     constrOid = lfirst_oid(cell);
   11329                 :          81 :                 Form_pg_constraint constrForm;
   11330                 :          81 :                 Relation        fkRel;
   11331                 :          81 :                 Oid                     indexOid;
   11332                 :          81 :                 Oid                     partIndexId;
   11333                 :          81 :                 int                     numfks;
   11334                 :          81 :                 AttrNumber      conkey[INDEX_MAX_KEYS];
   11335                 :          81 :                 AttrNumber      mapped_confkey[INDEX_MAX_KEYS];
   11336                 :          81 :                 AttrNumber      confkey[INDEX_MAX_KEYS];
   11337                 :          81 :                 Oid                     conpfeqop[INDEX_MAX_KEYS];
   11338                 :          81 :                 Oid                     conppeqop[INDEX_MAX_KEYS];
   11339                 :          81 :                 Oid                     conffeqop[INDEX_MAX_KEYS];
   11340                 :          81 :                 int                     numfkdelsetcols;
   11341                 :          81 :                 AttrNumber      confdelsetcols[INDEX_MAX_KEYS];
   11342                 :          81 :                 Constraint *fkconstraint;
   11343                 :          81 :                 ObjectAddress address;
   11344                 :          81 :                 Oid                     deleteTriggerOid = InvalidOid,
   11345                 :          81 :                                         updateTriggerOid = InvalidOid;
   11346                 :             : 
   11347                 :          81 :                 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   11348         [ +  - ]:          81 :                 if (!HeapTupleIsValid(tuple))
   11349   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   11350                 :          81 :                 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   11351                 :             : 
   11352                 :             :                 /*
   11353                 :             :                  * As explained above: don't try to clone a constraint for which we're
   11354                 :             :                  * going to clone the parent.
   11355                 :             :                  */
   11356         [ +  + ]:          81 :                 if (list_member_oid(clone, constrForm->conparentid))
   11357                 :             :                 {
   11358                 :          37 :                         ReleaseSysCache(tuple);
   11359                 :          37 :                         continue;
   11360                 :             :                 }
   11361                 :             : 
   11362                 :             :                 /* We need the same lock level that CreateTrigger will acquire */
   11363                 :          44 :                 fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
   11364                 :             : 
   11365                 :          44 :                 indexOid = constrForm->conindid;
   11366                 :          88 :                 DeconstructFkConstraintRow(tuple,
   11367                 :             :                                                                    &numfks,
   11368                 :          44 :                                                                    conkey,
   11369                 :          44 :                                                                    confkey,
   11370                 :          44 :                                                                    conpfeqop,
   11371                 :          44 :                                                                    conppeqop,
   11372                 :          44 :                                                                    conffeqop,
   11373                 :             :                                                                    &numfkdelsetcols,
   11374                 :          44 :                                                                    confdelsetcols);
   11375                 :             : 
   11376         [ +  + ]:          95 :                 for (int i = 0; i < numfks; i++)
   11377                 :          51 :                         mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
   11378                 :             : 
   11379                 :          44 :                 fkconstraint = makeNode(Constraint);
   11380                 :          44 :                 fkconstraint->contype = CONSTRAINT_FOREIGN;
   11381                 :          44 :                 fkconstraint->conname = NameStr(constrForm->conname);
   11382                 :          44 :                 fkconstraint->deferrable = constrForm->condeferrable;
   11383                 :          44 :                 fkconstraint->initdeferred = constrForm->condeferred;
   11384                 :          44 :                 fkconstraint->location = -1;
   11385                 :          44 :                 fkconstraint->pktable = NULL;
   11386                 :             :                 /* ->fk_attrs determined below */
   11387                 :          44 :                 fkconstraint->pk_attrs = NIL;
   11388                 :          44 :                 fkconstraint->fk_matchtype = constrForm->confmatchtype;
   11389                 :          44 :                 fkconstraint->fk_upd_action = constrForm->confupdtype;
   11390                 :          44 :                 fkconstraint->fk_del_action = constrForm->confdeltype;
   11391                 :          44 :                 fkconstraint->fk_del_set_cols = NIL;
   11392                 :          44 :                 fkconstraint->old_conpfeqop = NIL;
   11393                 :          44 :                 fkconstraint->old_pktable_oid = InvalidOid;
   11394                 :          44 :                 fkconstraint->is_enforced = constrForm->conenforced;
   11395                 :          44 :                 fkconstraint->skip_validation = false;
   11396                 :          44 :                 fkconstraint->initially_valid = constrForm->convalidated;
   11397                 :             : 
   11398                 :             :                 /* set up colnames that are used to generate the constraint name */
   11399         [ +  + ]:          95 :                 for (int i = 0; i < numfks; i++)
   11400                 :             :                 {
   11401                 :          51 :                         Form_pg_attribute att;
   11402                 :             : 
   11403                 :         102 :                         att = TupleDescAttr(RelationGetDescr(fkRel),
   11404                 :          51 :                                                                 conkey[i] - 1);
   11405                 :         102 :                         fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   11406                 :          51 :                                                                                          makeString(NameStr(att->attname)));
   11407                 :          51 :                 }
   11408                 :             : 
   11409                 :             :                 /*
   11410                 :             :                  * Add the new foreign key constraint pointing to the new partition.
   11411                 :             :                  * Because this new partition appears in the referenced side of the
   11412                 :             :                  * constraint, we don't need to set up for Phase 3 check.
   11413                 :             :                  */
   11414                 :          44 :                 partIndexId = index_get_partition(partitionRel, indexOid);
   11415         [ +  - ]:          44 :                 if (!OidIsValid(partIndexId))
   11416   [ #  #  #  # ]:           0 :                         elog(ERROR, "index for %u not found in partition %s",
   11417                 :             :                                  indexOid, RelationGetRelationName(partitionRel));
   11418                 :             : 
   11419                 :             :                 /*
   11420                 :             :                  * Get the "action" triggers belonging to the constraint to pass as
   11421                 :             :                  * parent OIDs for similar triggers that will be created on the
   11422                 :             :                  * partition in addFkRecurseReferenced().
   11423                 :             :                  */
   11424         [ +  + ]:          44 :                 if (constrForm->conenforced)
   11425                 :          86 :                         GetForeignKeyActionTriggers(trigrel, constrOid,
   11426                 :          43 :                                                                                 constrForm->confrelid, constrForm->conrelid,
   11427                 :             :                                                                                 &deleteTriggerOid, &updateTriggerOid);
   11428                 :             : 
   11429                 :             :                 /* Add this constraint ... */
   11430                 :          88 :                 address = addFkConstraint(addFkReferencedSide,
   11431                 :          44 :                                                                   fkconstraint->conname, fkconstraint, fkRel,
   11432                 :          44 :                                                                   partitionRel, partIndexId, constrOid,
   11433                 :          44 :                                                                   numfks, mapped_confkey,
   11434                 :          44 :                                                                   conkey, conpfeqop, conppeqop, conffeqop,
   11435                 :          44 :                                                                   numfkdelsetcols, confdelsetcols, false,
   11436                 :          44 :                                                                   constrForm->conperiod);
   11437                 :             :                 /* ... and recurse */
   11438                 :          88 :                 addFkRecurseReferenced(fkconstraint,
   11439                 :          44 :                                                            fkRel,
   11440                 :          44 :                                                            partitionRel,
   11441                 :          44 :                                                            partIndexId,
   11442                 :          44 :                                                            address.objectId,
   11443                 :          44 :                                                            numfks,
   11444                 :          44 :                                                            mapped_confkey,
   11445                 :          44 :                                                            conkey,
   11446                 :          44 :                                                            conpfeqop,
   11447                 :          44 :                                                            conppeqop,
   11448                 :          44 :                                                            conffeqop,
   11449                 :          44 :                                                            numfkdelsetcols,
   11450                 :          44 :                                                            confdelsetcols,
   11451                 :             :                                                            true,
   11452                 :          44 :                                                            deleteTriggerOid,
   11453                 :          44 :                                                            updateTriggerOid,
   11454                 :          44 :                                                            constrForm->conperiod);
   11455                 :             : 
   11456                 :          44 :                 table_close(fkRel, NoLock);
   11457                 :          44 :                 ReleaseSysCache(tuple);
   11458      [ -  +  + ]:          81 :         }
   11459                 :             : 
   11460                 :        1497 :         table_close(trigrel, RowExclusiveLock);
   11461                 :        1497 : }
   11462                 :             : 
   11463                 :             : /*
   11464                 :             :  * CloneFkReferencing
   11465                 :             :  *              Subroutine for CloneForeignKeyConstraints
   11466                 :             :  *
   11467                 :             :  * For each FK constraint of the parent relation in the given list, find an
   11468                 :             :  * equivalent constraint in its partition relation that can be reparented;
   11469                 :             :  * if one cannot be found, create a new constraint in the partition as its
   11470                 :             :  * child.
   11471                 :             :  *
   11472                 :             :  * If wqueue is given, it is used to set up phase-3 verification for each
   11473                 :             :  * cloned constraint; omit it if such verification is not needed
   11474                 :             :  * (example: the partition is being created anew).
   11475                 :             :  */
   11476                 :             : static void
   11477                 :        1500 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
   11478                 :             : {
   11479                 :        1500 :         AttrMap    *attmap;
   11480                 :        1500 :         List       *partFKs;
   11481                 :        1500 :         List       *clone = NIL;
   11482                 :        1500 :         ListCell   *cell;
   11483                 :        1500 :         Relation        trigrel;
   11484                 :             : 
   11485                 :             :         /* obtain a list of constraints that we need to clone */
   11486   [ +  +  +  +  :        1707 :         foreach(cell, RelationGetFKeyList(parentRel))
                   +  + ]
   11487                 :             :         {
   11488                 :         208 :                 ForeignKeyCacheInfo *fk = lfirst(cell);
   11489                 :             : 
   11490                 :             :                 /*
   11491                 :             :                  * Refuse to attach a table as partition that this partitioned table
   11492                 :             :                  * already has a foreign key to.  This isn't useful schema, which is
   11493                 :             :                  * proven by the fact that there have been no user complaints that
   11494                 :             :                  * it's already impossible to achieve this in the opposite direction,
   11495                 :             :                  * i.e., creating a foreign key that references a partition.  This
   11496                 :             :                  * restriction allows us to dodge some complexities around
   11497                 :             :                  * pg_constraint and pg_trigger row creations that would be needed
   11498                 :             :                  * during ATTACH/DETACH for this kind of relationship.
   11499                 :             :                  */
   11500         [ +  + ]:         208 :                 if (fk->confrelid == RelationGetRelid(partRel))
   11501   [ +  -  +  - ]:           1 :                         ereport(ERROR,
   11502                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   11503                 :             :                                          errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
   11504                 :             :                                                         RelationGetRelationName(partRel),
   11505                 :             :                                                         get_constraint_name(fk->conoid))));
   11506                 :             : 
   11507                 :         207 :                 clone = lappend_oid(clone, fk->conoid);
   11508                 :         207 :         }
   11509                 :             : 
   11510                 :             :         /*
   11511                 :             :          * Silently do nothing if there's nothing to do.  In particular, this
   11512                 :             :          * avoids throwing a spurious error for foreign tables.
   11513                 :             :          */
   11514         [ +  + ]:        1499 :         if (clone == NIL)
   11515                 :        1412 :                 return;
   11516                 :             : 
   11517         [ +  - ]:          87 :         if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   11518   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   11519                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11520                 :             :                                  errmsg("foreign key constraints are not supported on foreign tables")));
   11521                 :             : 
   11522                 :             :         /*
   11523                 :             :          * Triggers of the foreign keys will be manipulated a bunch of times in
   11524                 :             :          * the loop below.  To avoid repeatedly opening/closing the trigger
   11525                 :             :          * catalog relation, we open it here and pass it to the subroutines called
   11526                 :             :          * below.
   11527                 :             :          */
   11528                 :          87 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   11529                 :             : 
   11530                 :             :         /*
   11531                 :             :          * The constraint key may differ, if the columns in the partition are
   11532                 :             :          * different.  This map is used to convert them.
   11533                 :             :          */
   11534                 :         174 :         attmap = build_attrmap_by_name(RelationGetDescr(partRel),
   11535                 :          87 :                                                                    RelationGetDescr(parentRel),
   11536                 :             :                                                                    false);
   11537                 :             : 
   11538                 :          87 :         partFKs = copyObject(RelationGetFKeyList(partRel));
   11539                 :             : 
   11540   [ +  +  +  +  :         293 :         foreach(cell, clone)
                   +  + ]
   11541                 :             :         {
   11542                 :         206 :                 Oid                     parentConstrOid = lfirst_oid(cell);
   11543                 :         206 :                 Form_pg_constraint constrForm;
   11544                 :         206 :                 Relation        pkrel;
   11545                 :         206 :                 HeapTuple       tuple;
   11546                 :         206 :                 int                     numfks;
   11547                 :         206 :                 AttrNumber      conkey[INDEX_MAX_KEYS];
   11548                 :         206 :                 AttrNumber      mapped_conkey[INDEX_MAX_KEYS];
   11549                 :         206 :                 AttrNumber      confkey[INDEX_MAX_KEYS];
   11550                 :         206 :                 Oid                     conpfeqop[INDEX_MAX_KEYS];
   11551                 :         206 :                 Oid                     conppeqop[INDEX_MAX_KEYS];
   11552                 :         206 :                 Oid                     conffeqop[INDEX_MAX_KEYS];
   11553                 :         206 :                 int                     numfkdelsetcols;
   11554                 :         206 :                 AttrNumber      confdelsetcols[INDEX_MAX_KEYS];
   11555                 :         206 :                 Constraint *fkconstraint;
   11556                 :         206 :                 bool            attached;
   11557                 :         206 :                 Oid                     indexOid;
   11558                 :         206 :                 ObjectAddress address;
   11559                 :         206 :                 ListCell   *lc;
   11560                 :         206 :                 Oid                     insertTriggerOid = InvalidOid,
   11561                 :         206 :                                         updateTriggerOid = InvalidOid;
   11562                 :         206 :                 bool            with_period;
   11563                 :             : 
   11564                 :         206 :                 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
   11565         [ +  - ]:         206 :                 if (!HeapTupleIsValid(tuple))
   11566   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for constraint %u",
   11567                 :             :                                  parentConstrOid);
   11568                 :         206 :                 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   11569                 :             : 
   11570                 :             :                 /* Don't clone constraints whose parents are being cloned */
   11571         [ +  + ]:         206 :                 if (list_member_oid(clone, constrForm->conparentid))
   11572                 :             :                 {
   11573                 :         114 :                         ReleaseSysCache(tuple);
   11574                 :         114 :                         continue;
   11575                 :             :                 }
   11576                 :             : 
   11577                 :             :                 /*
   11578                 :             :                  * Need to prevent concurrent deletions.  If pkrel is a partitioned
   11579                 :             :                  * relation, that means to lock all partitions.
   11580                 :             :                  */
   11581                 :          92 :                 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
   11582         [ +  + ]:          92 :                 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11583                 :          35 :                         (void) find_all_inheritors(RelationGetRelid(pkrel),
   11584                 :             :                                                                            ShareRowExclusiveLock, NULL);
   11585                 :             : 
   11586                 :         184 :                 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
   11587                 :          92 :                                                                    conpfeqop, conppeqop, conffeqop,
   11588                 :          92 :                                                                    &numfkdelsetcols, confdelsetcols);
   11589         [ +  + ]:         224 :                 for (int i = 0; i < numfks; i++)
   11590                 :         132 :                         mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
   11591                 :             : 
   11592                 :             :                 /*
   11593                 :             :                  * Get the "check" triggers belonging to the constraint, if it is
   11594                 :             :                  * ENFORCED, to pass as parent OIDs for similar triggers that will be
   11595                 :             :                  * created on the partition in addFkRecurseReferencing().  They are
   11596                 :             :                  * also passed to tryAttachPartitionForeignKey() below to simply
   11597                 :             :                  * assign as parents to the partition's existing "check" triggers,
   11598                 :             :                  * that is, if the corresponding constraints is deemed attachable to
   11599                 :             :                  * the parent constraint.
   11600                 :             :                  */
   11601         [ +  + ]:          92 :                 if (constrForm->conenforced)
   11602                 :         182 :                         GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
   11603                 :          91 :                                                                            constrForm->confrelid, constrForm->conrelid,
   11604                 :             :                                                                            &insertTriggerOid, &updateTriggerOid);
   11605                 :             : 
   11606                 :             :                 /*
   11607                 :             :                  * Before creating a new constraint, see whether any existing FKs are
   11608                 :             :                  * fit for the purpose.  If one is, attach the parent constraint to
   11609                 :             :                  * it, and don't clone anything.  This way we avoid the expensive
   11610                 :             :                  * verification step and don't end up with a duplicate FK, and we
   11611                 :             :                  * don't need to recurse to partitions for this constraint.
   11612                 :             :                  */
   11613                 :          92 :                 attached = false;
   11614   [ +  +  +  +  :         132 :                 foreach(lc, partFKs)
                   +  + ]
   11615                 :             :                 {
   11616                 :          40 :                         ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
   11617                 :             : 
   11618   [ +  +  +  + ]:          80 :                         if (tryAttachPartitionForeignKey(wqueue,
   11619                 :          40 :                                                                                          fk,
   11620                 :          40 :                                                                                          partRel,
   11621                 :          40 :                                                                                          parentConstrOid,
   11622                 :          40 :                                                                                          numfks,
   11623                 :          40 :                                                                                          mapped_conkey,
   11624                 :          40 :                                                                                          confkey,
   11625                 :          40 :                                                                                          conpfeqop,
   11626                 :          40 :                                                                                          insertTriggerOid,
   11627                 :          40 :                                                                                          updateTriggerOid,
   11628                 :          40 :                                                                                          trigrel))
   11629                 :             :                         {
   11630                 :          25 :                                 attached = true;
   11631                 :          25 :                                 table_close(pkrel, NoLock);
   11632                 :          25 :                                 break;
   11633                 :             :                         }
   11634         [ +  + ]:          40 :                 }
   11635         [ +  + ]:          92 :                 if (attached)
   11636                 :             :                 {
   11637                 :          25 :                         ReleaseSysCache(tuple);
   11638                 :          25 :                         continue;
   11639                 :             :                 }
   11640                 :             : 
   11641                 :             :                 /* No dice.  Set up to create our own constraint */
   11642                 :          67 :                 fkconstraint = makeNode(Constraint);
   11643                 :          67 :                 fkconstraint->contype = CONSTRAINT_FOREIGN;
   11644                 :             :                 /* ->conname determined below */
   11645                 :          67 :                 fkconstraint->deferrable = constrForm->condeferrable;
   11646                 :          67 :                 fkconstraint->initdeferred = constrForm->condeferred;
   11647                 :          67 :                 fkconstraint->location = -1;
   11648                 :          67 :                 fkconstraint->pktable = NULL;
   11649                 :             :                 /* ->fk_attrs determined below */
   11650                 :          67 :                 fkconstraint->pk_attrs = NIL;
   11651                 :          67 :                 fkconstraint->fk_matchtype = constrForm->confmatchtype;
   11652                 :          67 :                 fkconstraint->fk_upd_action = constrForm->confupdtype;
   11653                 :          67 :                 fkconstraint->fk_del_action = constrForm->confdeltype;
   11654                 :          67 :                 fkconstraint->fk_del_set_cols = NIL;
   11655                 :          67 :                 fkconstraint->old_conpfeqop = NIL;
   11656                 :          67 :                 fkconstraint->old_pktable_oid = InvalidOid;
   11657                 :          67 :                 fkconstraint->is_enforced = constrForm->conenforced;
   11658                 :          67 :                 fkconstraint->skip_validation = false;
   11659                 :          67 :                 fkconstraint->initially_valid = constrForm->convalidated;
   11660         [ +  + ]:         154 :                 for (int i = 0; i < numfks; i++)
   11661                 :             :                 {
   11662                 :          87 :                         Form_pg_attribute att;
   11663                 :             : 
   11664                 :         174 :                         att = TupleDescAttr(RelationGetDescr(partRel),
   11665                 :          87 :                                                                 mapped_conkey[i] - 1);
   11666                 :         174 :                         fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   11667                 :          87 :                                                                                          makeString(NameStr(att->attname)));
   11668                 :          87 :                 }
   11669                 :             : 
   11670                 :          67 :                 indexOid = constrForm->conindid;
   11671                 :          67 :                 with_period = constrForm->conperiod;
   11672                 :             : 
   11673                 :             :                 /* Create the pg_constraint entry at this level */
   11674                 :         134 :                 address = addFkConstraint(addFkReferencingSide,
   11675                 :          67 :                                                                   NameStr(constrForm->conname), fkconstraint,
   11676                 :          67 :                                                                   partRel, pkrel, indexOid, parentConstrOid,
   11677                 :          67 :                                                                   numfks, confkey,
   11678                 :          67 :                                                                   mapped_conkey, conpfeqop,
   11679                 :          67 :                                                                   conppeqop, conffeqop,
   11680                 :          67 :                                                                   numfkdelsetcols, confdelsetcols,
   11681                 :          67 :                                                                   false, with_period);
   11682                 :             : 
   11683                 :             :                 /* Done with the cloned constraint's tuple */
   11684                 :          67 :                 ReleaseSysCache(tuple);
   11685                 :             : 
   11686                 :             :                 /* Create the check triggers, and recurse to partitions, if any */
   11687                 :         134 :                 addFkRecurseReferencing(wqueue,
   11688                 :          67 :                                                                 fkconstraint,
   11689                 :          67 :                                                                 partRel,
   11690                 :          67 :                                                                 pkrel,
   11691                 :          67 :                                                                 indexOid,
   11692                 :          67 :                                                                 address.objectId,
   11693                 :          67 :                                                                 numfks,
   11694                 :          67 :                                                                 confkey,
   11695                 :          67 :                                                                 mapped_conkey,
   11696                 :          67 :                                                                 conpfeqop,
   11697                 :          67 :                                                                 conppeqop,
   11698                 :          67 :                                                                 conffeqop,
   11699                 :          67 :                                                                 numfkdelsetcols,
   11700                 :          67 :                                                                 confdelsetcols,
   11701                 :             :                                                                 false,  /* no old check exists */
   11702                 :             :                                                                 AccessExclusiveLock,
   11703                 :          67 :                                                                 insertTriggerOid,
   11704                 :          67 :                                                                 updateTriggerOid,
   11705                 :          67 :                                                                 with_period);
   11706                 :          67 :                 table_close(pkrel, NoLock);
   11707         [ +  + ]:         206 :         }
   11708                 :             : 
   11709                 :          85 :         table_close(trigrel, RowExclusiveLock);
   11710                 :        1497 : }
   11711                 :             : 
   11712                 :             : /*
   11713                 :             :  * When the parent of a partition receives [the referencing side of] a foreign
   11714                 :             :  * key, we must propagate that foreign key to the partition.  However, the
   11715                 :             :  * partition might already have an equivalent foreign key; this routine
   11716                 :             :  * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
   11717                 :             :  * by the other parameters.  If they are equivalent, create the link between
   11718                 :             :  * the two constraints and return true.
   11719                 :             :  *
   11720                 :             :  * If the given FK does not match the one defined by rest of the params,
   11721                 :             :  * return false.
   11722                 :             :  */
   11723                 :             : static bool
   11724                 :          43 : tryAttachPartitionForeignKey(List **wqueue,
   11725                 :             :                                                          ForeignKeyCacheInfo *fk,
   11726                 :             :                                                          Relation partition,
   11727                 :             :                                                          Oid parentConstrOid,
   11728                 :             :                                                          int numfks,
   11729                 :             :                                                          AttrNumber *mapped_conkey,
   11730                 :             :                                                          AttrNumber *confkey,
   11731                 :             :                                                          Oid *conpfeqop,
   11732                 :             :                                                          Oid parentInsTrigger,
   11733                 :             :                                                          Oid parentUpdTrigger,
   11734                 :             :                                                          Relation trigrel)
   11735                 :             : {
   11736                 :          43 :         HeapTuple       parentConstrTup;
   11737                 :          43 :         Form_pg_constraint parentConstr;
   11738                 :          43 :         HeapTuple       partcontup;
   11739                 :          43 :         Form_pg_constraint partConstr;
   11740                 :             : 
   11741                 :          43 :         parentConstrTup = SearchSysCache1(CONSTROID,
   11742                 :          43 :                                                                           ObjectIdGetDatum(parentConstrOid));
   11743         [ +  - ]:          43 :         if (!HeapTupleIsValid(parentConstrTup))
   11744   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
   11745                 :          43 :         parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
   11746                 :             : 
   11747                 :             :         /*
   11748                 :             :          * Do some quick & easy initial checks.  If any of these fail, we cannot
   11749                 :             :          * use this constraint.
   11750                 :             :          */
   11751   [ +  -  -  + ]:          43 :         if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
   11752                 :             :         {
   11753                 :           0 :                 ReleaseSysCache(parentConstrTup);
   11754                 :           0 :                 return false;
   11755                 :             :         }
   11756   [ +  +  -  + ]:         119 :         for (int i = 0; i < numfks; i++)
   11757                 :             :         {
   11758         [ +  - ]:          76 :                 if (fk->conkey[i] != mapped_conkey[i] ||
   11759   [ +  -  -  + ]:          76 :                         fk->confkey[i] != confkey[i] ||
   11760                 :          76 :                         fk->conpfeqop[i] != conpfeqop[i])
   11761                 :             :                 {
   11762                 :           0 :                         ReleaseSysCache(parentConstrTup);
   11763                 :           0 :                         return false;
   11764                 :             :                 }
   11765                 :          76 :         }
   11766                 :             : 
   11767                 :             :         /* Looks good so far; perform more extensive checks. */
   11768                 :          43 :         partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
   11769         [ +  - ]:          43 :         if (!HeapTupleIsValid(partcontup))
   11770   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   11771                 :          43 :         partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
   11772                 :             : 
   11773                 :             :         /*
   11774                 :             :          * An error should be raised if the constraint enforceability is
   11775                 :             :          * different. Returning false without raising an error, as we do for other
   11776                 :             :          * attributes, could lead to a duplicate constraint with the same
   11777                 :             :          * enforceability as the parent. While this may be acceptable, it may not
   11778                 :             :          * be ideal. Therefore, it's better to raise an error and allow the user
   11779                 :             :          * to correct the enforceability before proceeding.
   11780                 :             :          */
   11781         [ +  + ]:          43 :         if (partConstr->conenforced != parentConstr->conenforced)
   11782   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   11783                 :             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   11784                 :             :                                  errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
   11785                 :             :                                                 NameStr(parentConstr->conname),
   11786                 :             :                                                 NameStr(partConstr->conname),
   11787                 :             :                                                 RelationGetRelationName(partition))));
   11788                 :             : 
   11789         [ +  + ]:          42 :         if (OidIsValid(partConstr->conparentid) ||
   11790         [ +  + ]:          38 :                 partConstr->condeferrable != parentConstr->condeferrable ||
   11791         [ +  - ]:          34 :                 partConstr->condeferred != parentConstr->condeferred ||
   11792         [ +  + ]:          34 :                 partConstr->confupdtype != parentConstr->confupdtype ||
   11793   [ +  -  +  + ]:          29 :                 partConstr->confdeltype != parentConstr->confdeltype ||
   11794                 :          29 :                 partConstr->confmatchtype != parentConstr->confmatchtype)
   11795                 :             :         {
   11796                 :          15 :                 ReleaseSysCache(parentConstrTup);
   11797                 :          15 :                 ReleaseSysCache(partcontup);
   11798                 :          15 :                 return false;
   11799                 :             :         }
   11800                 :             : 
   11801                 :          27 :         ReleaseSysCache(parentConstrTup);
   11802                 :          27 :         ReleaseSysCache(partcontup);
   11803                 :             : 
   11804                 :             :         /* Looks good!  Attach this constraint. */
   11805                 :          54 :         AttachPartitionForeignKey(wqueue, partition, fk->conoid,
   11806                 :          27 :                                                           parentConstrOid, parentInsTrigger,
   11807                 :          27 :                                                           parentUpdTrigger, trigrel);
   11808                 :             : 
   11809                 :          27 :         return true;
   11810                 :          42 : }
   11811                 :             : 
   11812                 :             : /*
   11813                 :             :  * AttachPartitionForeignKey
   11814                 :             :  *
   11815                 :             :  * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
   11816                 :             :  * attaching the constraint, removing redundant triggers and entries from
   11817                 :             :  * pg_constraint, and setting the constraint's parent.
   11818                 :             :  */
   11819                 :             : static void
   11820                 :          27 : AttachPartitionForeignKey(List **wqueue,
   11821                 :             :                                                   Relation partition,
   11822                 :             :                                                   Oid partConstrOid,
   11823                 :             :                                                   Oid parentConstrOid,
   11824                 :             :                                                   Oid parentInsTrigger,
   11825                 :             :                                                   Oid parentUpdTrigger,
   11826                 :             :                                                   Relation trigrel)
   11827                 :             : {
   11828                 :          27 :         HeapTuple       parentConstrTup;
   11829                 :          27 :         Form_pg_constraint parentConstr;
   11830                 :          27 :         HeapTuple       partcontup;
   11831                 :          27 :         Form_pg_constraint partConstr;
   11832                 :          27 :         bool            queueValidation;
   11833                 :          27 :         Oid                     partConstrFrelid;
   11834                 :          27 :         Oid                     partConstrRelid;
   11835                 :          27 :         bool            parentConstrIsEnforced;
   11836                 :             : 
   11837                 :             :         /* Fetch the parent constraint tuple */
   11838                 :          27 :         parentConstrTup = SearchSysCache1(CONSTROID,
   11839                 :          27 :                                                                           ObjectIdGetDatum(parentConstrOid));
   11840         [ +  - ]:          27 :         if (!HeapTupleIsValid(parentConstrTup))
   11841   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
   11842                 :          27 :         parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
   11843                 :          27 :         parentConstrIsEnforced = parentConstr->conenforced;
   11844                 :             : 
   11845                 :             :         /* Fetch the child constraint tuple */
   11846                 :          27 :         partcontup = SearchSysCache1(CONSTROID,
   11847                 :          27 :                                                                  ObjectIdGetDatum(partConstrOid));
   11848         [ +  - ]:          27 :         if (!HeapTupleIsValid(partcontup))
   11849   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
   11850                 :          27 :         partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
   11851                 :          27 :         partConstrFrelid = partConstr->confrelid;
   11852                 :          27 :         partConstrRelid = partConstr->conrelid;
   11853                 :             : 
   11854                 :             :         /*
   11855                 :             :          * If the referenced table is partitioned, then the partition we're
   11856                 :             :          * attaching now has extra pg_constraint rows and action triggers that are
   11857                 :             :          * no longer needed.  Remove those.
   11858                 :             :          */
   11859         [ +  + ]:          27 :         if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
   11860                 :             :         {
   11861                 :           6 :                 Relation        pg_constraint = table_open(ConstraintRelationId, RowShareLock);
   11862                 :             : 
   11863                 :          12 :                 RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
   11864                 :           6 :                                                                   partConstrRelid);
   11865                 :             : 
   11866                 :           6 :                 table_close(pg_constraint, RowShareLock);
   11867                 :           6 :         }
   11868                 :             : 
   11869                 :             :         /*
   11870                 :             :          * Will we need to validate this constraint?   A valid parent constraint
   11871                 :             :          * implies that all child constraints have been validated, so if this one
   11872                 :             :          * isn't, we must trigger phase 3 validation.
   11873                 :             :          */
   11874         [ +  + ]:          27 :         queueValidation = parentConstr->convalidated && !partConstr->convalidated;
   11875                 :             : 
   11876                 :          27 :         ReleaseSysCache(partcontup);
   11877                 :          27 :         ReleaseSysCache(parentConstrTup);
   11878                 :             : 
   11879                 :             :         /*
   11880                 :             :          * The action triggers in the new partition become redundant -- the parent
   11881                 :             :          * table already has equivalent ones, and those will be able to reach the
   11882                 :             :          * partition.  Remove the ones in the partition.  We identify them because
   11883                 :             :          * they have our constraint OID, as well as being on the referenced rel.
   11884                 :             :          */
   11885                 :          54 :         DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
   11886                 :          27 :                                                                          partConstrRelid);
   11887                 :             : 
   11888                 :          54 :         ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
   11889                 :          27 :                                                                   RelationGetRelid(partition));
   11890                 :             : 
   11891                 :             :         /*
   11892                 :             :          * Like the constraint, attach partition's "check" triggers to the
   11893                 :             :          * corresponding parent triggers if the constraint is ENFORCED. NOT
   11894                 :             :          * ENFORCED constraints do not have these triggers.
   11895                 :             :          */
   11896         [ +  + ]:          27 :         if (parentConstrIsEnforced)
   11897                 :             :         {
   11898                 :          25 :                 Oid                     insertTriggerOid,
   11899                 :             :                                         updateTriggerOid;
   11900                 :             : 
   11901                 :          50 :                 GetForeignKeyCheckTriggers(trigrel,
   11902                 :          25 :                                                                    partConstrOid, partConstrFrelid, partConstrRelid,
   11903                 :             :                                                                    &insertTriggerOid, &updateTriggerOid);
   11904         [ +  - ]:          25 :                 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
   11905                 :          50 :                 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
   11906                 :          25 :                                                                 RelationGetRelid(partition));
   11907         [ +  - ]:          25 :                 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
   11908                 :          50 :                 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
   11909                 :          25 :                                                                 RelationGetRelid(partition));
   11910                 :          25 :         }
   11911                 :             : 
   11912                 :             :         /*
   11913                 :             :          * We updated this pg_constraint row above to set its parent; validating
   11914                 :             :          * it will cause its convalidated flag to change, so we need CCI here.  In
   11915                 :             :          * addition, we need it unconditionally for the rare case where the parent
   11916                 :             :          * table has *two* identical constraints; when reaching this function for
   11917                 :             :          * the second one, we must have made our changes visible, otherwise we
   11918                 :             :          * would try to attach both to this one.
   11919                 :             :          */
   11920                 :          27 :         CommandCounterIncrement();
   11921                 :             : 
   11922                 :             :         /* If validation is needed, put it in the queue now. */
   11923         [ +  + ]:          27 :         if (queueValidation)
   11924                 :             :         {
   11925                 :           3 :                 Relation        conrel;
   11926                 :           3 :                 Oid                     confrelid;
   11927                 :             : 
   11928                 :           3 :                 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   11929                 :             : 
   11930                 :           3 :                 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
   11931         [ +  - ]:           3 :                 if (!HeapTupleIsValid(partcontup))
   11932   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
   11933                 :             : 
   11934                 :           3 :                 confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
   11935                 :             : 
   11936                 :             :                 /* Use the same lock as for AT_ValidateConstraint */
   11937                 :           6 :                 QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
   11938                 :           3 :                                                                         partcontup, ShareUpdateExclusiveLock);
   11939                 :           3 :                 ReleaseSysCache(partcontup);
   11940                 :           3 :                 table_close(conrel, RowExclusiveLock);
   11941                 :           3 :         }
   11942                 :          27 : }
   11943                 :             : 
   11944                 :             : /*
   11945                 :             :  * RemoveInheritedConstraint
   11946                 :             :  *
   11947                 :             :  * Removes the constraint and its associated trigger from the specified
   11948                 :             :  * relation, which inherited the given constraint.
   11949                 :             :  */
   11950                 :             : static void
   11951                 :           6 : RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
   11952                 :             :                                                   Oid conrelid)
   11953                 :             : {
   11954                 :           6 :         ObjectAddresses *objs;
   11955                 :           6 :         HeapTuple       consttup;
   11956                 :           6 :         ScanKeyData key;
   11957                 :           6 :         SysScanDesc scan;
   11958                 :           6 :         HeapTuple       trigtup;
   11959                 :             : 
   11960                 :           6 :         ScanKeyInit(&key,
   11961                 :             :                                 Anum_pg_constraint_conrelid,
   11962                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   11963                 :           6 :                                 ObjectIdGetDatum(conrelid));
   11964                 :             : 
   11965                 :           6 :         scan = systable_beginscan(conrel,
   11966                 :             :                                                           ConstraintRelidTypidNameIndexId,
   11967                 :             :                                                           true, NULL, 1, &key);
   11968                 :           6 :         objs = new_object_addresses();
   11969         [ +  + ]:          54 :         while ((consttup = systable_getnext(scan)) != NULL)
   11970                 :             :         {
   11971                 :          48 :                 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
   11972                 :             : 
   11973         [ +  + ]:          48 :                 if (conform->conparentid != conoid)
   11974                 :          35 :                         continue;
   11975                 :             :                 else
   11976                 :             :                 {
   11977                 :          13 :                         ObjectAddress addr;
   11978                 :          13 :                         SysScanDesc scan2;
   11979                 :          13 :                         ScanKeyData key2;
   11980                 :          13 :                         int                     n PG_USED_FOR_ASSERTS_ONLY;
   11981                 :             : 
   11982                 :          13 :                         ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
   11983                 :          13 :                         add_exact_object_address(&addr, objs);
   11984                 :             : 
   11985                 :             :                         /*
   11986                 :             :                          * First we must delete the dependency record that binds the
   11987                 :             :                          * constraint records together.
   11988                 :             :                          */
   11989                 :          13 :                         n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
   11990                 :          13 :                                                                                                    conform->oid,
   11991                 :             :                                                                                                    DEPENDENCY_INTERNAL,
   11992                 :             :                                                                                                    ConstraintRelationId,
   11993                 :          13 :                                                                                                    conoid);
   11994         [ -  + ]:          13 :                         Assert(n == 1);         /* actually only one is expected */
   11995                 :             : 
   11996                 :             :                         /*
   11997                 :             :                          * Now search for the triggers for this constraint and set them up
   11998                 :             :                          * for deletion too
   11999                 :             :                          */
   12000                 :          13 :                         ScanKeyInit(&key2,
   12001                 :             :                                                 Anum_pg_trigger_tgconstraint,
   12002                 :             :                                                 BTEqualStrategyNumber, F_OIDEQ,
   12003                 :          13 :                                                 ObjectIdGetDatum(conform->oid));
   12004                 :          13 :                         scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
   12005                 :             :                                                                            true, NULL, 1, &key2);
   12006         [ +  + ]:          39 :                         while ((trigtup = systable_getnext(scan2)) != NULL)
   12007                 :             :                         {
   12008                 :          26 :                                 ObjectAddressSet(addr, TriggerRelationId,
   12009                 :             :                                                                  ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
   12010                 :          26 :                                 add_exact_object_address(&addr, objs);
   12011                 :             :                         }
   12012                 :          13 :                         systable_endscan(scan2);
   12013                 :          13 :                 }
   12014      [ -  +  + ]:          48 :         }
   12015                 :             :         /* make the dependency deletions visible */
   12016                 :           6 :         CommandCounterIncrement();
   12017                 :           6 :         performMultipleDeletions(objs, DROP_RESTRICT,
   12018                 :             :                                                          PERFORM_DELETION_INTERNAL);
   12019                 :           6 :         systable_endscan(scan);
   12020                 :           6 : }
   12021                 :             : 
   12022                 :             : /*
   12023                 :             :  * DropForeignKeyConstraintTriggers
   12024                 :             :  *
   12025                 :             :  * The subroutine for tryAttachPartitionForeignKey handles the deletion of
   12026                 :             :  * action triggers for the foreign key constraint.
   12027                 :             :  *
   12028                 :             :  * If valid confrelid and conrelid values are not provided, the respective
   12029                 :             :  * trigger check will be skipped, and the trigger will be considered for
   12030                 :             :  * removal.
   12031                 :             :  */
   12032                 :             : static void
   12033                 :          39 : DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
   12034                 :             :                                                                  Oid conrelid)
   12035                 :             : {
   12036                 :          39 :         ScanKeyData key;
   12037                 :          39 :         SysScanDesc scan;
   12038                 :          39 :         HeapTuple       trigtup;
   12039                 :             : 
   12040                 :          39 :         ScanKeyInit(&key,
   12041                 :             :                                 Anum_pg_trigger_tgconstraint,
   12042                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   12043                 :          39 :                                 ObjectIdGetDatum(conoid));
   12044                 :          39 :         scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   12045                 :             :                                                           NULL, 1, &key);
   12046         [ +  + ]:         169 :         while ((trigtup = systable_getnext(scan)) != NULL)
   12047                 :             :         {
   12048                 :         130 :                 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   12049                 :         130 :                 ObjectAddress trigger;
   12050                 :             : 
   12051                 :             :                 /* Invalid if trigger is not for a referential integrity constraint */
   12052         [ +  - ]:         130 :                 if (!OidIsValid(trgform->tgconstrrelid))
   12053                 :           0 :                         continue;
   12054   [ +  +  +  + ]:         130 :                 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
   12055                 :          50 :                         continue;
   12056   [ +  +  +  - ]:          80 :                 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
   12057                 :           0 :                         continue;
   12058                 :             : 
   12059                 :             :                 /* We should be dropping trigger related to foreign key constraint */
   12060   [ +  +  +  +  :          80 :                 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
          +  +  +  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
             -  +  +  -  
                      + ]
   12061                 :             :                            trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
   12062                 :             :                            trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
   12063                 :             :                            trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
   12064                 :             :                            trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
   12065                 :             :                            trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
   12066                 :             :                            trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
   12067                 :             :                            trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
   12068                 :             :                            trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
   12069                 :             :                            trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
   12070                 :             :                            trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
   12071                 :             :                            trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
   12072                 :             : 
   12073                 :             :                 /*
   12074                 :             :                  * The constraint is originally set up to contain this trigger as an
   12075                 :             :                  * implementation object, so there's a dependency record that links
   12076                 :             :                  * the two; however, since the trigger is no longer needed, we remove
   12077                 :             :                  * the dependency link in order to be able to drop the trigger while
   12078                 :             :                  * keeping the constraint intact.
   12079                 :             :                  */
   12080                 :          80 :                 deleteDependencyRecordsFor(TriggerRelationId,
   12081                 :          80 :                                                                    trgform->oid,
   12082                 :             :                                                                    false);
   12083                 :             :                 /* make dependency deletion visible to performDeletion */
   12084                 :          80 :                 CommandCounterIncrement();
   12085                 :          80 :                 ObjectAddressSet(trigger, TriggerRelationId,
   12086                 :             :                                                  trgform->oid);
   12087                 :          80 :                 performDeletion(&trigger, DROP_RESTRICT, 0);
   12088                 :             :                 /* make trigger drop visible, in case the loop iterates */
   12089                 :          80 :                 CommandCounterIncrement();
   12090      [ -  +  + ]:         130 :         }
   12091                 :             : 
   12092                 :          39 :         systable_endscan(scan);
   12093                 :          39 : }
   12094                 :             : 
   12095                 :             : /*
   12096                 :             :  * GetForeignKeyActionTriggers
   12097                 :             :  *              Returns delete and update "action" triggers of the given relation
   12098                 :             :  *              belonging to the given constraint
   12099                 :             :  */
   12100                 :             : static void
   12101                 :          43 : GetForeignKeyActionTriggers(Relation trigrel,
   12102                 :             :                                                         Oid conoid, Oid confrelid, Oid conrelid,
   12103                 :             :                                                         Oid *deleteTriggerOid,
   12104                 :             :                                                         Oid *updateTriggerOid)
   12105                 :             : {
   12106                 :          43 :         ScanKeyData key;
   12107                 :          43 :         SysScanDesc scan;
   12108                 :          43 :         HeapTuple       trigtup;
   12109                 :             : 
   12110                 :          43 :         *deleteTriggerOid = *updateTriggerOid = InvalidOid;
   12111                 :          43 :         ScanKeyInit(&key,
   12112                 :             :                                 Anum_pg_trigger_tgconstraint,
   12113                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   12114                 :          43 :                                 ObjectIdGetDatum(conoid));
   12115                 :             : 
   12116                 :          43 :         scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   12117                 :             :                                                           NULL, 1, &key);
   12118         [ +  + ]:         193 :         while ((trigtup = systable_getnext(scan)) != NULL)
   12119                 :             :         {
   12120                 :         150 :                 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   12121                 :             : 
   12122         [ +  + ]:         150 :                 if (trgform->tgconstrrelid != conrelid)
   12123                 :          46 :                         continue;
   12124         [ -  + ]:         104 :                 if (trgform->tgrelid != confrelid)
   12125                 :           0 :                         continue;
   12126                 :             :                 /* Only ever look at "action" triggers on the PK side. */
   12127         [ +  + ]:         104 :                 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
   12128                 :          18 :                         continue;
   12129         [ +  + ]:          86 :                 if (TRIGGER_FOR_DELETE(trgform->tgtype))
   12130                 :             :                 {
   12131         [ -  + ]:          43 :                         Assert(*deleteTriggerOid == InvalidOid);
   12132                 :          43 :                         *deleteTriggerOid = trgform->oid;
   12133                 :          43 :                 }
   12134         [ -  + ]:          43 :                 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   12135                 :             :                 {
   12136         [ -  + ]:          43 :                         Assert(*updateTriggerOid == InvalidOid);
   12137                 :          43 :                         *updateTriggerOid = trgform->oid;
   12138                 :          43 :                 }
   12139                 :             : #ifndef USE_ASSERT_CHECKING
   12140                 :             :                 /* In an assert-enabled build, continue looking to find duplicates */
   12141                 :             :                 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
   12142                 :             :                         break;
   12143                 :             : #endif
   12144      [ -  +  + ]:         150 :         }
   12145                 :             : 
   12146         [ +  - ]:          43 :         if (!OidIsValid(*deleteTriggerOid))
   12147   [ #  #  #  # ]:           0 :                 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
   12148                 :             :                          conoid);
   12149         [ +  - ]:          43 :         if (!OidIsValid(*updateTriggerOid))
   12150   [ #  #  #  # ]:           0 :                 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
   12151                 :             :                          conoid);
   12152                 :             : 
   12153                 :          43 :         systable_endscan(scan);
   12154                 :          43 : }
   12155                 :             : 
   12156                 :             : /*
   12157                 :             :  * GetForeignKeyCheckTriggers
   12158                 :             :  *              Returns insert and update "check" triggers of the given relation
   12159                 :             :  *              belonging to the given constraint
   12160                 :             :  */
   12161                 :             : static void
   12162                 :         134 : GetForeignKeyCheckTriggers(Relation trigrel,
   12163                 :             :                                                    Oid conoid, Oid confrelid, Oid conrelid,
   12164                 :             :                                                    Oid *insertTriggerOid,
   12165                 :             :                                                    Oid *updateTriggerOid)
   12166                 :             : {
   12167                 :         134 :         ScanKeyData key;
   12168                 :         134 :         SysScanDesc scan;
   12169                 :         134 :         HeapTuple       trigtup;
   12170                 :             : 
   12171                 :         134 :         *insertTriggerOid = *updateTriggerOid = InvalidOid;
   12172                 :         134 :         ScanKeyInit(&key,
   12173                 :             :                                 Anum_pg_trigger_tgconstraint,
   12174                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   12175                 :         134 :                                 ObjectIdGetDatum(conoid));
   12176                 :             : 
   12177                 :         134 :         scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   12178                 :             :                                                           NULL, 1, &key);
   12179         [ +  + ]:         564 :         while ((trigtup = systable_getnext(scan)) != NULL)
   12180                 :             :         {
   12181                 :         430 :                 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   12182                 :             : 
   12183         [ +  + ]:         430 :                 if (trgform->tgconstrrelid != confrelid)
   12184                 :         144 :                         continue;
   12185         [ -  + ]:         286 :                 if (trgform->tgrelid != conrelid)
   12186                 :           0 :                         continue;
   12187                 :             :                 /* Only ever look at "check" triggers on the FK side. */
   12188         [ +  + ]:         286 :                 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
   12189                 :          18 :                         continue;
   12190         [ +  + ]:         268 :                 if (TRIGGER_FOR_INSERT(trgform->tgtype))
   12191                 :             :                 {
   12192         [ -  + ]:         134 :                         Assert(*insertTriggerOid == InvalidOid);
   12193                 :         134 :                         *insertTriggerOid = trgform->oid;
   12194                 :         134 :                 }
   12195         [ -  + ]:         134 :                 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   12196                 :             :                 {
   12197         [ -  + ]:         134 :                         Assert(*updateTriggerOid == InvalidOid);
   12198                 :         134 :                         *updateTriggerOid = trgform->oid;
   12199                 :         134 :                 }
   12200                 :             : #ifndef USE_ASSERT_CHECKING
   12201                 :             :                 /* In an assert-enabled build, continue looking to find duplicates. */
   12202                 :             :                 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
   12203                 :             :                         break;
   12204                 :             : #endif
   12205      [ -  +  + ]:         430 :         }
   12206                 :             : 
   12207         [ +  - ]:         134 :         if (!OidIsValid(*insertTriggerOid))
   12208   [ #  #  #  # ]:           0 :                 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
   12209                 :             :                          conoid);
   12210         [ +  - ]:         134 :         if (!OidIsValid(*updateTriggerOid))
   12211   [ #  #  #  # ]:           0 :                 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
   12212                 :             :                          conoid);
   12213                 :             : 
   12214                 :         134 :         systable_endscan(scan);
   12215                 :         134 : }
   12216                 :             : 
   12217                 :             : /*
   12218                 :             :  * ALTER TABLE ALTER CONSTRAINT
   12219                 :             :  *
   12220                 :             :  * Update the attributes of a constraint.
   12221                 :             :  *
   12222                 :             :  * Currently only works for Foreign Key and not null constraints.
   12223                 :             :  *
   12224                 :             :  * If the constraint is modified, returns its address; otherwise, return
   12225                 :             :  * InvalidObjectAddress.
   12226                 :             :  */
   12227                 :             : static ObjectAddress
   12228                 :          49 : ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
   12229                 :             :                                           bool recurse, LOCKMODE lockmode)
   12230                 :             : {
   12231                 :          49 :         Relation        conrel;
   12232                 :          49 :         Relation        tgrel;
   12233                 :          49 :         SysScanDesc scan;
   12234                 :          49 :         ScanKeyData skey[3];
   12235                 :          49 :         HeapTuple       contuple;
   12236                 :          49 :         Form_pg_constraint currcon;
   12237                 :             :         ObjectAddress address;
   12238                 :             : 
   12239                 :             :         /*
   12240                 :             :          * Disallow altering ONLY a partitioned table, as it would make no sense.
   12241                 :             :          * This is okay for legacy inheritance.
   12242                 :             :          */
   12243   [ +  +  +  - ]:          49 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
   12244   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   12245                 :             :                                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12246                 :             :                                 errmsg("constraint must be altered in child tables too"),
   12247                 :             :                                 errhint("Do not specify the ONLY keyword."));
   12248                 :             : 
   12249                 :             : 
   12250                 :          49 :         conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12251                 :          49 :         tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   12252                 :             : 
   12253                 :             :         /*
   12254                 :             :          * Find and check the target constraint
   12255                 :             :          */
   12256                 :          98 :         ScanKeyInit(&skey[0],
   12257                 :             :                                 Anum_pg_constraint_conrelid,
   12258                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   12259                 :          49 :                                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12260                 :          98 :         ScanKeyInit(&skey[1],
   12261                 :             :                                 Anum_pg_constraint_contypid,
   12262                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   12263                 :          49 :                                 ObjectIdGetDatum(InvalidOid));
   12264                 :          98 :         ScanKeyInit(&skey[2],
   12265                 :             :                                 Anum_pg_constraint_conname,
   12266                 :             :                                 BTEqualStrategyNumber, F_NAMEEQ,
   12267                 :          49 :                                 CStringGetDatum(cmdcon->conname));
   12268                 :          98 :         scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12269                 :          49 :                                                           true, NULL, 3, skey);
   12270                 :             : 
   12271                 :             :         /* There can be at most one matching row */
   12272         [ +  + ]:          49 :         if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
   12273   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   12274                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   12275                 :             :                                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12276                 :             :                                                 cmdcon->conname, RelationGetRelationName(rel))));
   12277                 :             : 
   12278                 :          48 :         currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12279   [ +  +  +  - ]:          48 :         if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
   12280   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   12281                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12282                 :             :                                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
   12283                 :             :                                                 cmdcon->conname, RelationGetRelationName(rel))));
   12284   [ +  +  +  + ]:          48 :         if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
   12285   [ +  -  +  - ]:           2 :                 ereport(ERROR,
   12286                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12287                 :             :                                  errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
   12288                 :             :                                                 cmdcon->conname, RelationGetRelationName(rel))));
   12289   [ +  +  +  + ]:          46 :         if (cmdcon->alterInheritability &&
   12290                 :          15 :                 currcon->contype != CONSTRAINT_NOTNULL)
   12291   [ +  -  +  - ]:           4 :                 ereport(ERROR,
   12292                 :             :                                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12293                 :             :                                 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
   12294                 :             :                                            cmdcon->conname, RelationGetRelationName(rel)));
   12295                 :             : 
   12296                 :             :         /* Refuse to modify inheritability of inherited constraints */
   12297         [ +  + ]:          42 :         if (cmdcon->alterInheritability &&
   12298   [ +  +  +  + ]:          11 :                 cmdcon->noinherit && currcon->coninhcount > 0)
   12299   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   12300                 :             :                                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12301                 :             :                                 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
   12302                 :             :                                            NameStr(currcon->conname),
   12303                 :             :                                            RelationGetRelationName(rel)));
   12304                 :             : 
   12305                 :             :         /*
   12306                 :             :          * If it's not the topmost constraint, raise an error.
   12307                 :             :          *
   12308                 :             :          * Altering a non-topmost constraint leaves some triggers untouched, since
   12309                 :             :          * they are not directly connected to this constraint; also, pg_dump would
   12310                 :             :          * ignore the deferrability status of the individual constraint, since it
   12311                 :             :          * only dumps topmost constraints.  Avoid these problems by refusing this
   12312                 :             :          * operation and telling the user to alter the parent constraint instead.
   12313                 :             :          */
   12314         [ +  + ]:          41 :         if (OidIsValid(currcon->conparentid))
   12315                 :             :         {
   12316                 :           2 :                 HeapTuple       tp;
   12317                 :           2 :                 Oid                     parent = currcon->conparentid;
   12318                 :           2 :                 char       *ancestorname = NULL;
   12319                 :           2 :                 char       *ancestortable = NULL;
   12320                 :             : 
   12321                 :             :                 /* Loop to find the topmost constraint */
   12322         [ +  + ]:           6 :                 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
   12323                 :             :                 {
   12324                 :           4 :                         Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
   12325                 :             : 
   12326                 :             :                         /* If no parent, this is the constraint we want */
   12327         [ +  + ]:           4 :                         if (!OidIsValid(contup->conparentid))
   12328                 :             :                         {
   12329                 :           2 :                                 ancestorname = pstrdup(NameStr(contup->conname));
   12330                 :           2 :                                 ancestortable = get_rel_name(contup->conrelid);
   12331                 :           2 :                                 ReleaseSysCache(tp);
   12332                 :           2 :                                 break;
   12333                 :             :                         }
   12334                 :             : 
   12335                 :           2 :                         parent = contup->conparentid;
   12336                 :           2 :                         ReleaseSysCache(tp);
   12337         [ -  + ]:           4 :                 }
   12338                 :             : 
   12339   [ +  -  +  -  :           2 :                 ereport(ERROR,
             +  -  -  + ]
   12340                 :             :                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12341                 :             :                                  errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
   12342                 :             :                                                 cmdcon->conname, RelationGetRelationName(rel)),
   12343                 :             :                                  ancestorname && ancestortable ?
   12344                 :             :                                  errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
   12345                 :             :                                                    cmdcon->conname, ancestorname, ancestortable) : 0,
   12346                 :             :                                  errhint("You may alter the constraint it derives from instead.")));
   12347                 :           0 :         }
   12348                 :             : 
   12349                 :          39 :         address = InvalidObjectAddress;
   12350                 :             : 
   12351                 :             :         /*
   12352                 :             :          * Do the actual catalog work, and recurse if necessary.
   12353                 :             :          */
   12354   [ +  +  +  + ]:          78 :         if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
   12355                 :          39 :                                                                           contuple, recurse, lockmode))
   12356                 :          37 :                 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
   12357                 :             : 
   12358                 :          39 :         systable_endscan(scan);
   12359                 :             : 
   12360                 :          39 :         table_close(tgrel, RowExclusiveLock);
   12361                 :          39 :         table_close(conrel, RowExclusiveLock);
   12362                 :             : 
   12363                 :             :         return address;
   12364                 :          39 : }
   12365                 :             : 
   12366                 :             : /*
   12367                 :             :  * A subroutine of ATExecAlterConstraint that calls the respective routines for
   12368                 :             :  * altering constraint's enforceability, deferrability or inheritability.
   12369                 :             :  */
   12370                 :             : static bool
   12371                 :          39 : ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
   12372                 :             :                                                           Relation conrel, Relation tgrel, Relation rel,
   12373                 :             :                                                           HeapTuple contuple, bool recurse,
   12374                 :             :                                                           LOCKMODE lockmode)
   12375                 :             : {
   12376                 :          39 :         Form_pg_constraint currcon;
   12377                 :          39 :         bool            changed = false;
   12378                 :          39 :         List       *otherrelids = NIL;
   12379                 :             : 
   12380                 :          39 :         currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12381                 :             : 
   12382                 :             :         /*
   12383                 :             :          * Do the catalog work for the enforceability or deferrability change,
   12384                 :             :          * recurse if necessary.
   12385                 :             :          *
   12386                 :             :          * Note that even if deferrability is requested to be altered along with
   12387                 :             :          * enforceability, we don't need to explicitly update multiple entries in
   12388                 :             :          * pg_trigger related to deferrability.
   12389                 :             :          *
   12390                 :             :          * Modifying enforceability involves either creating or dropping the
   12391                 :             :          * trigger, during which the deferrability setting will be adjusted
   12392                 :             :          * automatically.
   12393                 :             :          */
   12394   [ +  +  +  + ]:          39 :         if (cmdcon->alterEnforceability &&
   12395                 :          26 :                 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
   12396                 :          13 :                                                                                 currcon->conrelid, currcon->confrelid,
   12397                 :          13 :                                                                                 contuple, lockmode, InvalidOid,
   12398                 :             :                                                                                 InvalidOid, InvalidOid, InvalidOid))
   12399                 :          12 :                 changed = true;
   12400                 :             : 
   12401   [ +  +  -  + ]:          27 :         else if (cmdcon->alterDeferrability &&
   12402                 :          32 :                          ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
   12403                 :          16 :                                                                                         contuple, recurse, &otherrelids,
   12404                 :          16 :                                                                                         lockmode))
   12405                 :             :         {
   12406                 :             :                 /*
   12407                 :             :                  * AlterConstrUpdateConstraintEntry already invalidated relcache for
   12408                 :             :                  * the relations having the constraint itself; here we also invalidate
   12409                 :             :                  * for relations that have any triggers that are part of the
   12410                 :             :                  * constraint.
   12411                 :             :                  */
   12412   [ +  +  +  +  :          51 :                 foreach_oid(relid, otherrelids)
             +  +  +  + ]
   12413                 :          35 :                         CacheInvalidateRelcacheByRelid(relid);
   12414                 :             : 
   12415                 :          16 :                 changed = true;
   12416                 :          16 :         }
   12417                 :             : 
   12418                 :             :         /*
   12419                 :             :          * Do the catalog work for the inheritability change.
   12420                 :             :          */
   12421   [ +  +  +  + ]:          39 :         if (cmdcon->alterInheritability &&
   12422                 :          20 :                 ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
   12423                 :          10 :                                                                                 lockmode))
   12424                 :           9 :                 changed = true;
   12425                 :             : 
   12426                 :          78 :         return changed;
   12427                 :          39 : }
   12428                 :             : 
   12429                 :             : /*
   12430                 :             :  * Returns true if the constraint's enforceability is altered.
   12431                 :             :  *
   12432                 :             :  * Depending on whether the constraint is being set to ENFORCED or NOT
   12433                 :             :  * ENFORCED, it creates or drops the trigger accordingly.
   12434                 :             :  *
   12435                 :             :  * Note that we must recurse even when trying to change a constraint to not
   12436                 :             :  * enforced if it is already not enforced, in case descendant constraints
   12437                 :             :  * might be enforced and need to be changed to not enforced. Conversely, we
   12438                 :             :  * should do nothing if a constraint is being set to enforced and is already
   12439                 :             :  * enforced, as descendant constraints cannot be different in that case.
   12440                 :             :  */
   12441                 :             : static bool
   12442                 :          30 : ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
   12443                 :             :                                                                 Relation conrel, Relation tgrel,
   12444                 :             :                                                                 Oid fkrelid, Oid pkrelid,
   12445                 :             :                                                                 HeapTuple contuple, LOCKMODE lockmode,
   12446                 :             :                                                                 Oid ReferencedParentDelTrigger,
   12447                 :             :                                                                 Oid ReferencedParentUpdTrigger,
   12448                 :             :                                                                 Oid ReferencingParentInsTrigger,
   12449                 :             :                                                                 Oid ReferencingParentUpdTrigger)
   12450                 :             : {
   12451                 :          30 :         Form_pg_constraint currcon;
   12452                 :          30 :         Oid                     conoid;
   12453                 :          30 :         Relation        rel;
   12454                 :          30 :         bool            changed = false;
   12455                 :             : 
   12456                 :             :         /* Since this function recurses, it could be driven to stack overflow */
   12457                 :          30 :         check_stack_depth();
   12458                 :             : 
   12459         [ +  - ]:          30 :         Assert(cmdcon->alterEnforceability);
   12460                 :             : 
   12461                 :          30 :         currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12462                 :          30 :         conoid = currcon->oid;
   12463                 :             : 
   12464                 :             :         /* Should be foreign key constraint */
   12465         [ +  - ]:          30 :         Assert(currcon->contype == CONSTRAINT_FOREIGN);
   12466                 :             : 
   12467                 :          30 :         rel = table_open(currcon->conrelid, lockmode);
   12468                 :             : 
   12469         [ +  + ]:          30 :         if (currcon->conenforced != cmdcon->is_enforced)
   12470                 :             :         {
   12471                 :          29 :                 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
   12472                 :          29 :                 changed = true;
   12473                 :          29 :         }
   12474                 :             : 
   12475                 :             :         /* Drop triggers */
   12476         [ +  + ]:          30 :         if (!cmdcon->is_enforced)
   12477                 :             :         {
   12478                 :             :                 /*
   12479                 :             :                  * When setting a constraint to NOT ENFORCED, the constraint triggers
   12480                 :             :                  * need to be dropped. Therefore, we must process the child relations
   12481                 :             :                  * first, followed by the parent, to account for dependencies.
   12482                 :             :                  */
   12483   [ +  +  -  + ]:          12 :                 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   12484                 :           9 :                         get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
   12485                 :           6 :                         AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
   12486                 :           3 :                                                                                          fkrelid, pkrelid, contuple,
   12487                 :           3 :                                                                                          lockmode, InvalidOid, InvalidOid,
   12488                 :             :                                                                                          InvalidOid, InvalidOid);
   12489                 :             : 
   12490                 :             :                 /* Drop all the triggers */
   12491                 :          12 :                 DropForeignKeyConstraintTriggers(tgrel, conoid, InvalidOid, InvalidOid);
   12492                 :          12 :         }
   12493         [ -  + ]:          18 :         else if (changed)                       /* Create triggers */
   12494                 :             :         {
   12495                 :          54 :                 Oid                     ReferencedDelTriggerOid = InvalidOid,
   12496                 :          18 :                                         ReferencedUpdTriggerOid = InvalidOid,
   12497                 :          18 :                                         ReferencingInsTriggerOid = InvalidOid,
   12498                 :          18 :                                         ReferencingUpdTriggerOid = InvalidOid;
   12499                 :             : 
   12500                 :             :                 /* Prepare the minimal information required for trigger creation. */
   12501                 :          18 :                 Constraint *fkconstraint = makeNode(Constraint);
   12502                 :             : 
   12503                 :          18 :                 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
   12504                 :          18 :                 fkconstraint->fk_matchtype = currcon->confmatchtype;
   12505                 :          18 :                 fkconstraint->fk_upd_action = currcon->confupdtype;
   12506                 :          18 :                 fkconstraint->fk_del_action = currcon->confdeltype;
   12507                 :             : 
   12508                 :             :                 /* Create referenced triggers */
   12509         [ +  + ]:          18 :                 if (currcon->conrelid == fkrelid)
   12510                 :          22 :                         createForeignKeyActionTriggers(currcon->conrelid,
   12511                 :          11 :                                                                                    currcon->confrelid,
   12512                 :          11 :                                                                                    fkconstraint,
   12513                 :          11 :                                                                                    conoid,
   12514                 :          11 :                                                                                    currcon->conindid,
   12515                 :          11 :                                                                                    ReferencedParentDelTrigger,
   12516                 :          11 :                                                                                    ReferencedParentUpdTrigger,
   12517                 :             :                                                                                    &ReferencedDelTriggerOid,
   12518                 :             :                                                                                    &ReferencedUpdTriggerOid);
   12519                 :             : 
   12520                 :             :                 /* Create referencing triggers */
   12521         [ +  + ]:          18 :                 if (currcon->confrelid == pkrelid)
   12522                 :          30 :                         createForeignKeyCheckTriggers(currcon->conrelid,
   12523                 :          15 :                                                                                   pkrelid,
   12524                 :          15 :                                                                                   fkconstraint,
   12525                 :          15 :                                                                                   conoid,
   12526                 :          15 :                                                                                   currcon->conindid,
   12527                 :          15 :                                                                                   ReferencingParentInsTrigger,
   12528                 :          15 :                                                                                   ReferencingParentUpdTrigger,
   12529                 :             :                                                                                   &ReferencingInsTriggerOid,
   12530                 :             :                                                                                   &ReferencingUpdTriggerOid);
   12531                 :             : 
   12532                 :             :                 /*
   12533                 :             :                  * Tell Phase 3 to check that the constraint is satisfied by existing
   12534                 :             :                  * rows.  Only applies to leaf partitions, and (for constraints that
   12535                 :             :                  * reference a partitioned table) only if this is not one of the
   12536                 :             :                  * pg_constraint rows that exist solely to support action triggers.
   12537                 :             :                  */
   12538   [ +  +  +  + ]:          18 :                 if (rel->rd_rel->relkind == RELKIND_RELATION &&
   12539                 :          15 :                         currcon->confrelid == pkrelid)
   12540                 :             :                 {
   12541                 :          12 :                         AlteredTableInfo *tab;
   12542                 :          12 :                         NewConstraint *newcon;
   12543                 :             : 
   12544                 :          12 :                         newcon = palloc0_object(NewConstraint);
   12545                 :          12 :                         newcon->name = fkconstraint->conname;
   12546                 :          12 :                         newcon->contype = CONSTR_FOREIGN;
   12547                 :          12 :                         newcon->refrelid = currcon->confrelid;
   12548                 :          12 :                         newcon->refindid = currcon->conindid;
   12549                 :          12 :                         newcon->conid = currcon->oid;
   12550                 :          12 :                         newcon->qual = (Node *) fkconstraint;
   12551                 :             : 
   12552                 :             :                         /* Find or create work queue entry for this table */
   12553                 :          12 :                         tab = ATGetQueueEntry(wqueue, rel);
   12554                 :          12 :                         tab->constraints = lappend(tab->constraints, newcon);
   12555                 :          12 :                 }
   12556                 :             : 
   12557                 :             :                 /*
   12558                 :             :                  * If the table at either end of the constraint is partitioned, we
   12559                 :             :                  * need to recurse and create triggers for each constraint that is a
   12560                 :             :                  * child of this one.
   12561                 :             :                  */
   12562   [ +  +  +  + ]:          18 :                 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   12563                 :          15 :                         get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
   12564                 :          10 :                         AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
   12565                 :           5 :                                                                                          fkrelid, pkrelid, contuple,
   12566                 :           5 :                                                                                          lockmode, ReferencedDelTriggerOid,
   12567                 :           5 :                                                                                          ReferencedUpdTriggerOid,
   12568                 :           5 :                                                                                          ReferencingInsTriggerOid,
   12569                 :           5 :                                                                                          ReferencingUpdTriggerOid);
   12570                 :          18 :         }
   12571                 :             : 
   12572                 :          30 :         table_close(rel, NoLock);
   12573                 :             : 
   12574                 :          60 :         return changed;
   12575                 :          30 : }
   12576                 :             : 
   12577                 :             : /*
   12578                 :             :  * Returns true if the constraint's deferrability is altered.
   12579                 :             :  *
   12580                 :             :  * *otherrelids is appended OIDs of relations containing affected triggers.
   12581                 :             :  *
   12582                 :             :  * Note that we must recurse even when the values are correct, in case
   12583                 :             :  * indirect descendants have had their constraints altered locally.
   12584                 :             :  * (This could be avoided if we forbade altering constraints in partitions
   12585                 :             :  * but existing releases don't do that.)
   12586                 :             :  */
   12587                 :             : static bool
   12588                 :          27 : ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
   12589                 :             :                                                            Relation conrel, Relation tgrel, Relation rel,
   12590                 :             :                                                            HeapTuple contuple, bool recurse,
   12591                 :             :                                                            List **otherrelids, LOCKMODE lockmode)
   12592                 :             : {
   12593                 :          27 :         Form_pg_constraint currcon;
   12594                 :          27 :         Oid                     refrelid;
   12595                 :          27 :         bool            changed = false;
   12596                 :             : 
   12597                 :             :         /* since this function recurses, it could be driven to stack overflow */
   12598                 :          27 :         check_stack_depth();
   12599                 :             : 
   12600         [ +  - ]:          27 :         Assert(cmdcon->alterDeferrability);
   12601                 :             : 
   12602                 :          27 :         currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12603                 :          27 :         refrelid = currcon->confrelid;
   12604                 :             : 
   12605                 :             :         /* Should be foreign key constraint */
   12606         [ +  - ]:          27 :         Assert(currcon->contype == CONSTRAINT_FOREIGN);
   12607                 :             : 
   12608                 :             :         /*
   12609                 :             :          * If called to modify a constraint that's already in the desired state,
   12610                 :             :          * silently do nothing.
   12611                 :             :          */
   12612   [ +  +  +  - ]:          27 :         if (currcon->condeferrable != cmdcon->deferrable ||
   12613                 :           1 :                 currcon->condeferred != cmdcon->initdeferred)
   12614                 :             :         {
   12615                 :          27 :                 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
   12616                 :          27 :                 changed = true;
   12617                 :             : 
   12618                 :             :                 /*
   12619                 :             :                  * Now we need to update the multiple entries in pg_trigger that
   12620                 :             :                  * implement the constraint.
   12621                 :             :                  */
   12622                 :          54 :                 AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
   12623                 :          27 :                                                                                 cmdcon->deferrable,
   12624                 :          27 :                                                                                 cmdcon->initdeferred, otherrelids);
   12625                 :          27 :         }
   12626                 :             : 
   12627                 :             :         /*
   12628                 :             :          * If the table at either end of the constraint is partitioned, we need to
   12629                 :             :          * handle every constraint that is a child of this one.
   12630                 :             :          */
   12631   [ +  -  +  -  :          50 :         if (recurse && changed &&
                   +  + ]
   12632         [ +  + ]:          27 :                 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   12633                 :          23 :                  get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
   12634                 :          14 :                 AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
   12635                 :           7 :                                                                                 contuple, recurse, otherrelids,
   12636                 :           7 :                                                                                 lockmode);
   12637                 :             : 
   12638                 :          54 :         return changed;
   12639                 :          27 : }
   12640                 :             : 
   12641                 :             : /*
   12642                 :             :  * Returns true if the constraint's inheritability is altered.
   12643                 :             :  */
   12644                 :             : static bool
   12645                 :           9 : ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
   12646                 :             :                                                                 Relation conrel, Relation rel,
   12647                 :             :                                                                 HeapTuple contuple, LOCKMODE lockmode)
   12648                 :             : {
   12649                 :           9 :         Form_pg_constraint currcon;
   12650                 :           9 :         AttrNumber      colNum;
   12651                 :           9 :         char       *colName;
   12652                 :           9 :         List       *children;
   12653                 :             : 
   12654         [ +  - ]:           9 :         Assert(cmdcon->alterInheritability);
   12655                 :             : 
   12656                 :           9 :         currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12657                 :             : 
   12658                 :             :         /* The current implementation only works for NOT NULL constraints */
   12659         [ +  - ]:           9 :         Assert(currcon->contype == CONSTRAINT_NOTNULL);
   12660                 :             : 
   12661                 :             :         /*
   12662                 :             :          * If called to modify a constraint that's already in the desired state,
   12663                 :             :          * silently do nothing.
   12664                 :             :          */
   12665         [ -  + ]:           9 :         if (cmdcon->noinherit == currcon->connoinherit)
   12666                 :           0 :                 return false;
   12667                 :             : 
   12668                 :           9 :         AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
   12669                 :           9 :         CommandCounterIncrement();
   12670                 :             : 
   12671                 :             :         /* Fetch the column number and name */
   12672                 :           9 :         colNum = extractNotNullColumn(contuple);
   12673                 :           9 :         colName = get_attname(currcon->conrelid, colNum, false);
   12674                 :             : 
   12675                 :             :         /*
   12676                 :             :          * Propagate the change to children.  For this subcommand type we don't
   12677                 :             :          * recursively affect children, just the immediate level.
   12678                 :             :          */
   12679                 :          18 :         children = find_inheritance_children(RelationGetRelid(rel),
   12680                 :           9 :                                                                                  lockmode);
   12681   [ +  +  +  -  :          31 :         foreach_oid(childoid, children)
             +  +  +  + ]
   12682                 :             :         {
   12683                 :          13 :                 ObjectAddress addr;
   12684                 :             : 
   12685         [ +  + ]:          13 :                 if (cmdcon->noinherit)
   12686                 :             :                 {
   12687                 :           5 :                         HeapTuple       childtup;
   12688                 :           5 :                         Form_pg_constraint childcon;
   12689                 :             : 
   12690                 :           5 :                         childtup = findNotNullConstraint(childoid, colName);
   12691         [ +  - ]:           5 :                         if (!childtup)
   12692   [ #  #  #  # ]:           0 :                                 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
   12693                 :             :                                          colName, childoid);
   12694                 :           5 :                         childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   12695         [ -  + ]:           5 :                         Assert(childcon->coninhcount > 0);
   12696                 :           5 :                         childcon->coninhcount--;
   12697                 :           5 :                         childcon->conislocal = true;
   12698                 :           5 :                         CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
   12699                 :           5 :                         heap_freetuple(childtup);
   12700                 :           5 :                 }
   12701                 :             :                 else
   12702                 :             :                 {
   12703                 :           8 :                         Relation        childrel = table_open(childoid, NoLock);
   12704                 :             : 
   12705                 :          16 :                         addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
   12706                 :           8 :                                                                         colName, true, true, lockmode);
   12707         [ -  + ]:           8 :                         if (OidIsValid(addr.objectId))
   12708                 :           8 :                                 CommandCounterIncrement();
   12709                 :           8 :                         table_close(childrel, NoLock);
   12710                 :           8 :                 }
   12711                 :          22 :         }
   12712                 :             : 
   12713                 :           9 :         return true;
   12714                 :           9 : }
   12715                 :             : 
   12716                 :             : /*
   12717                 :             :  * A subroutine of ATExecAlterConstrDeferrability that updated constraint
   12718                 :             :  * trigger's deferrability.
   12719                 :             :  *
   12720                 :             :  * The arguments to this function have the same meaning as the arguments to
   12721                 :             :  * ATExecAlterConstrDeferrability.
   12722                 :             :  */
   12723                 :             : static void
   12724                 :          27 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
   12725                 :             :                                                                 bool deferrable, bool initdeferred,
   12726                 :             :                                                                 List **otherrelids)
   12727                 :             : {
   12728                 :          27 :         HeapTuple       tgtuple;
   12729                 :          27 :         ScanKeyData tgkey;
   12730                 :          27 :         SysScanDesc tgscan;
   12731                 :             : 
   12732                 :          27 :         ScanKeyInit(&tgkey,
   12733                 :             :                                 Anum_pg_trigger_tgconstraint,
   12734                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   12735                 :          27 :                                 ObjectIdGetDatum(conoid));
   12736                 :          27 :         tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
   12737                 :             :                                                                 NULL, 1, &tgkey);
   12738         [ +  + ]:         105 :         while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
   12739                 :             :         {
   12740                 :          78 :                 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
   12741                 :          78 :                 Form_pg_trigger copy_tg;
   12742                 :          78 :                 HeapTuple       tgCopyTuple;
   12743                 :             : 
   12744                 :             :                 /*
   12745                 :             :                  * Remember OIDs of other relation(s) involved in FK constraint.
   12746                 :             :                  * (Note: it's likely that we could skip forcing a relcache inval for
   12747                 :             :                  * other rels that don't have a trigger whose properties change, but
   12748                 :             :                  * let's be conservative.)
   12749                 :             :                  */
   12750         [ +  + ]:          78 :                 if (tgform->tgrelid != RelationGetRelid(rel))
   12751                 :          76 :                         *otherrelids = list_append_unique_oid(*otherrelids,
   12752                 :          38 :                                                                                                   tgform->tgrelid);
   12753                 :             : 
   12754                 :             :                 /*
   12755                 :             :                  * Update enable status and deferrability of RI_FKey_noaction_del,
   12756                 :             :                  * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
   12757                 :             :                  * triggers, but not others; see createForeignKeyActionTriggers and
   12758                 :             :                  * CreateFKCheckTrigger.
   12759                 :             :                  */
   12760         [ +  + ]:          78 :                 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
   12761         [ +  + ]:          62 :                         tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
   12762   [ +  +  +  + ]:          43 :                         tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
   12763                 :          23 :                         tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
   12764                 :           3 :                         continue;
   12765                 :             : 
   12766                 :          75 :                 tgCopyTuple = heap_copytuple(tgtuple);
   12767                 :          75 :                 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
   12768                 :             : 
   12769                 :          75 :                 copy_tg->tgdeferrable = deferrable;
   12770                 :          75 :                 copy_tg->tginitdeferred = initdeferred;
   12771                 :          75 :                 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
   12772                 :             : 
   12773         [ +  - ]:          75 :                 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
   12774                 :             : 
   12775                 :          75 :                 heap_freetuple(tgCopyTuple);
   12776      [ -  +  + ]:          78 :         }
   12777                 :             : 
   12778                 :          27 :         systable_endscan(tgscan);
   12779                 :          27 : }
   12780                 :             : 
   12781                 :             : /*
   12782                 :             :  * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
   12783                 :             :  * the specified constraint.
   12784                 :             :  *
   12785                 :             :  * Note that this doesn't handle recursion the normal way, viz. by scanning the
   12786                 :             :  * list of child relations and recursing; instead it uses the conparentid
   12787                 :             :  * relationships.  This may need to be reconsidered.
   12788                 :             :  *
   12789                 :             :  * The arguments to this function have the same meaning as the arguments to
   12790                 :             :  * ATExecAlterConstrEnforceability.
   12791                 :             :  */
   12792                 :             : static void
   12793                 :           8 : AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
   12794                 :             :                                                                  Relation conrel, Relation tgrel,
   12795                 :             :                                                                  Oid fkrelid, Oid pkrelid,
   12796                 :             :                                                                  HeapTuple contuple, LOCKMODE lockmode,
   12797                 :             :                                                                  Oid ReferencedParentDelTrigger,
   12798                 :             :                                                                  Oid ReferencedParentUpdTrigger,
   12799                 :             :                                                                  Oid ReferencingParentInsTrigger,
   12800                 :             :                                                                  Oid ReferencingParentUpdTrigger)
   12801                 :             : {
   12802                 :           8 :         Form_pg_constraint currcon;
   12803                 :           8 :         Oid                     conoid;
   12804                 :           8 :         ScanKeyData pkey;
   12805                 :           8 :         SysScanDesc pscan;
   12806                 :           8 :         HeapTuple       childtup;
   12807                 :             : 
   12808                 :           8 :         currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12809                 :           8 :         conoid = currcon->oid;
   12810                 :             : 
   12811                 :           8 :         ScanKeyInit(&pkey,
   12812                 :             :                                 Anum_pg_constraint_conparentid,
   12813                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   12814                 :           8 :                                 ObjectIdGetDatum(conoid));
   12815                 :             : 
   12816                 :           8 :         pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   12817                 :             :                                                            true, NULL, 1, &pkey);
   12818                 :             : 
   12819         [ +  + ]:          25 :         while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   12820                 :          34 :                 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
   12821                 :          17 :                                                                                 pkrelid, childtup, lockmode,
   12822                 :          17 :                                                                                 ReferencedParentDelTrigger,
   12823                 :          17 :                                                                                 ReferencedParentUpdTrigger,
   12824                 :          17 :                                                                                 ReferencingParentInsTrigger,
   12825                 :          17 :                                                                                 ReferencingParentUpdTrigger);
   12826                 :             : 
   12827                 :           8 :         systable_endscan(pscan);
   12828                 :           8 : }
   12829                 :             : 
   12830                 :             : /*
   12831                 :             :  * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
   12832                 :             :  * the specified constraint.
   12833                 :             :  *
   12834                 :             :  * Note that this doesn't handle recursion the normal way, viz. by scanning the
   12835                 :             :  * list of child relations and recursing; instead it uses the conparentid
   12836                 :             :  * relationships.  This may need to be reconsidered.
   12837                 :             :  *
   12838                 :             :  * The arguments to this function have the same meaning as the arguments to
   12839                 :             :  * ATExecAlterConstrDeferrability.
   12840                 :             :  */
   12841                 :             : static void
   12842                 :           7 : AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
   12843                 :             :                                                                 Relation conrel, Relation tgrel, Relation rel,
   12844                 :             :                                                                 HeapTuple contuple, bool recurse,
   12845                 :             :                                                                 List **otherrelids, LOCKMODE lockmode)
   12846                 :             : {
   12847                 :           7 :         Form_pg_constraint currcon;
   12848                 :           7 :         Oid                     conoid;
   12849                 :           7 :         ScanKeyData pkey;
   12850                 :           7 :         SysScanDesc pscan;
   12851                 :           7 :         HeapTuple       childtup;
   12852                 :             : 
   12853                 :           7 :         currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12854                 :           7 :         conoid = currcon->oid;
   12855                 :             : 
   12856                 :           7 :         ScanKeyInit(&pkey,
   12857                 :             :                                 Anum_pg_constraint_conparentid,
   12858                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   12859                 :           7 :                                 ObjectIdGetDatum(conoid));
   12860                 :             : 
   12861                 :           7 :         pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   12862                 :             :                                                            true, NULL, 1, &pkey);
   12863                 :             : 
   12864         [ +  + ]:          18 :         while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   12865                 :             :         {
   12866                 :          11 :                 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   12867                 :          11 :                 Relation        childrel;
   12868                 :             : 
   12869                 :          11 :                 childrel = table_open(childcon->conrelid, lockmode);
   12870                 :             : 
   12871                 :          22 :                 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
   12872                 :          11 :                                                                            childtup, recurse, otherrelids, lockmode);
   12873                 :          11 :                 table_close(childrel, NoLock);
   12874                 :          11 :         }
   12875                 :             : 
   12876                 :           7 :         systable_endscan(pscan);
   12877                 :           7 : }
   12878                 :             : 
   12879                 :             : /*
   12880                 :             :  * Update the constraint entry for the given ATAlterConstraint command, and
   12881                 :             :  * invoke the appropriate hooks.
   12882                 :             :  */
   12883                 :             : static void
   12884                 :          66 : AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
   12885                 :             :                                                                  HeapTuple contuple)
   12886                 :             : {
   12887                 :          66 :         HeapTuple       copyTuple;
   12888                 :          66 :         Form_pg_constraint copy_con;
   12889                 :             : 
   12890   [ +  +  +  +  :          66 :         Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
                   +  - ]
   12891                 :             :                    cmdcon->alterInheritability);
   12892                 :             : 
   12893                 :          66 :         copyTuple = heap_copytuple(contuple);
   12894                 :          66 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   12895                 :             : 
   12896         [ +  + ]:          66 :         if (cmdcon->alterEnforceability)
   12897                 :             :         {
   12898                 :          29 :                 copy_con->conenforced = cmdcon->is_enforced;
   12899                 :             : 
   12900                 :             :                 /*
   12901                 :             :                  * NB: The convalidated status is irrelevant when the constraint is
   12902                 :             :                  * set to NOT ENFORCED, but for consistency, it should still be set
   12903                 :             :                  * appropriately. Similarly, if the constraint is later changed to
   12904                 :             :                  * ENFORCED, validation will be performed during phase 3, so it makes
   12905                 :             :                  * sense to mark it as valid in that case.
   12906                 :             :                  */
   12907                 :          29 :                 copy_con->convalidated = cmdcon->is_enforced;
   12908                 :          29 :         }
   12909         [ +  + ]:          66 :         if (cmdcon->alterDeferrability)
   12910                 :             :         {
   12911                 :          28 :                 copy_con->condeferrable = cmdcon->deferrable;
   12912                 :          28 :                 copy_con->condeferred = cmdcon->initdeferred;
   12913                 :          28 :         }
   12914         [ +  + ]:          66 :         if (cmdcon->alterInheritability)
   12915                 :          10 :                 copy_con->connoinherit = cmdcon->noinherit;
   12916                 :             : 
   12917                 :          66 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   12918         [ +  - ]:          66 :         InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
   12919                 :             : 
   12920                 :             :         /* Make new constraint flags visible to others */
   12921                 :          66 :         CacheInvalidateRelcacheByRelid(copy_con->conrelid);
   12922                 :             : 
   12923                 :          66 :         heap_freetuple(copyTuple);
   12924                 :          66 : }
   12925                 :             : 
   12926                 :             : /*
   12927                 :             :  * ALTER TABLE VALIDATE CONSTRAINT
   12928                 :             :  *
   12929                 :             :  * XXX The reason we handle recursion here rather than at Phase 1 is because
   12930                 :             :  * there's no good way to skip recursing when handling foreign keys: there is
   12931                 :             :  * no need to lock children in that case, yet we wouldn't be able to avoid
   12932                 :             :  * doing so at that level.
   12933                 :             :  *
   12934                 :             :  * Return value is the address of the validated constraint.  If the constraint
   12935                 :             :  * was already validated, InvalidObjectAddress is returned.
   12936                 :             :  */
   12937                 :             : static ObjectAddress
   12938                 :          52 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
   12939                 :             :                                                  bool recurse, bool recursing, LOCKMODE lockmode)
   12940                 :             : {
   12941                 :          52 :         Relation        conrel;
   12942                 :          52 :         SysScanDesc scan;
   12943                 :          52 :         ScanKeyData skey[3];
   12944                 :          52 :         HeapTuple       tuple;
   12945                 :          52 :         Form_pg_constraint con;
   12946                 :             :         ObjectAddress address;
   12947                 :             : 
   12948                 :          52 :         conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12949                 :             : 
   12950                 :             :         /*
   12951                 :             :          * Find and check the target constraint
   12952                 :             :          */
   12953                 :         104 :         ScanKeyInit(&skey[0],
   12954                 :             :                                 Anum_pg_constraint_conrelid,
   12955                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   12956                 :          52 :                                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12957                 :         104 :         ScanKeyInit(&skey[1],
   12958                 :             :                                 Anum_pg_constraint_contypid,
   12959                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   12960                 :          52 :                                 ObjectIdGetDatum(InvalidOid));
   12961                 :         104 :         ScanKeyInit(&skey[2],
   12962                 :             :                                 Anum_pg_constraint_conname,
   12963                 :             :                                 BTEqualStrategyNumber, F_NAMEEQ,
   12964                 :          52 :                                 CStringGetDatum(constrName));
   12965                 :         104 :         scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12966                 :          52 :                                                           true, NULL, 3, skey);
   12967                 :             : 
   12968                 :             :         /* There can be at most one matching row */
   12969         [ +  - ]:          52 :         if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
   12970   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   12971                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   12972                 :             :                                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12973                 :             :                                                 constrName, RelationGetRelationName(rel))));
   12974                 :             : 
   12975                 :          52 :         con = (Form_pg_constraint) GETSTRUCT(tuple);
   12976         [ +  + ]:          52 :         if (con->contype != CONSTRAINT_FOREIGN &&
   12977   [ +  +  +  - ]:          42 :                 con->contype != CONSTRAINT_CHECK &&
   12978                 :          18 :                 con->contype != CONSTRAINT_NOTNULL)
   12979   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   12980                 :             :                                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12981                 :             :                                 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
   12982                 :             :                                            constrName, RelationGetRelationName(rel)),
   12983                 :             :                                 errdetail("This operation is not supported for this type of constraint."));
   12984                 :             : 
   12985         [ +  + ]:          52 :         if (!con->conenforced)
   12986   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   12987                 :             :                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12988                 :             :                                  errmsg("cannot validate NOT ENFORCED constraint")));
   12989                 :             : 
   12990         [ +  + ]:          51 :         if (!con->convalidated)
   12991                 :             :         {
   12992         [ +  + ]:          48 :                 if (con->contype == CONSTRAINT_FOREIGN)
   12993                 :             :                 {
   12994                 :          18 :                         QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
   12995                 :           9 :                                                                                 tuple, lockmode);
   12996                 :           9 :                 }
   12997         [ +  + ]:          39 :                 else if (con->contype == CONSTRAINT_CHECK)
   12998                 :             :                 {
   12999                 :          42 :                         QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
   13000                 :          21 :                                                                                    tuple, recurse, recursing, lockmode);
   13001                 :          21 :                 }
   13002         [ -  + ]:          18 :                 else if (con->contype == CONSTRAINT_NOTNULL)
   13003                 :             :                 {
   13004                 :          36 :                         QueueNNConstraintValidation(wqueue, conrel, rel,
   13005                 :          18 :                                                                                 tuple, recurse, recursing, lockmode);
   13006                 :          18 :                 }
   13007                 :             : 
   13008                 :          48 :                 ObjectAddressSet(address, ConstraintRelationId, con->oid);
   13009                 :          48 :         }
   13010                 :             :         else
   13011                 :           3 :                 address = InvalidObjectAddress; /* already validated */
   13012                 :             : 
   13013                 :          51 :         systable_endscan(scan);
   13014                 :             : 
   13015                 :          51 :         table_close(conrel, RowExclusiveLock);
   13016                 :             : 
   13017                 :             :         return address;
   13018                 :          51 : }
   13019                 :             : 
   13020                 :             : /*
   13021                 :             :  * QueueFKConstraintValidation
   13022                 :             :  *
   13023                 :             :  * Add an entry to the wqueue to validate the given foreign key constraint in
   13024                 :             :  * Phase 3 and update the convalidated field in the pg_constraint catalog
   13025                 :             :  * for the specified relation and all its children.
   13026                 :             :  */
   13027                 :             : static void
   13028                 :          22 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
   13029                 :             :                                                         Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
   13030                 :             : {
   13031                 :          22 :         Form_pg_constraint con;
   13032                 :          22 :         AlteredTableInfo *tab;
   13033                 :          22 :         HeapTuple       copyTuple;
   13034                 :          22 :         Form_pg_constraint copy_con;
   13035                 :             : 
   13036                 :          22 :         con = (Form_pg_constraint) GETSTRUCT(contuple);
   13037         [ +  - ]:          22 :         Assert(con->contype == CONSTRAINT_FOREIGN);
   13038         [ +  - ]:          22 :         Assert(!con->convalidated);
   13039                 :             : 
   13040                 :             :         /*
   13041                 :             :          * Add the validation to phase 3's queue; not needed for partitioned
   13042                 :             :          * tables themselves, only for their partitions.
   13043                 :             :          *
   13044                 :             :          * When the referenced table (pkrelid) is partitioned, the referencing
   13045                 :             :          * table (fkrel) has one pg_constraint row pointing to each partition
   13046                 :             :          * thereof.  These rows are there only to support action triggers and no
   13047                 :             :          * table scan is needed, therefore skip this for them as well.
   13048                 :             :          */
   13049   [ +  +  +  + ]:          22 :         if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
   13050                 :          14 :                 con->confrelid == pkrelid)
   13051                 :             :         {
   13052                 :          11 :                 NewConstraint *newcon;
   13053                 :          11 :                 Constraint *fkconstraint;
   13054                 :             : 
   13055                 :             :                 /* Queue validation for phase 3 */
   13056                 :          11 :                 fkconstraint = makeNode(Constraint);
   13057                 :             :                 /* for now this is all we need */
   13058                 :          11 :                 fkconstraint->conname = pstrdup(NameStr(con->conname));
   13059                 :             : 
   13060                 :          11 :                 newcon = palloc0_object(NewConstraint);
   13061                 :          11 :                 newcon->name = fkconstraint->conname;
   13062                 :          11 :                 newcon->contype = CONSTR_FOREIGN;
   13063                 :          11 :                 newcon->refrelid = con->confrelid;
   13064                 :          11 :                 newcon->refindid = con->conindid;
   13065                 :          11 :                 newcon->conid = con->oid;
   13066                 :          11 :                 newcon->qual = (Node *) fkconstraint;
   13067                 :             : 
   13068                 :             :                 /* Find or create work queue entry for this table */
   13069                 :          11 :                 tab = ATGetQueueEntry(wqueue, fkrel);
   13070                 :          11 :                 tab->constraints = lappend(tab->constraints, newcon);
   13071                 :          11 :         }
   13072                 :             : 
   13073                 :             :         /*
   13074                 :             :          * If the table at either end of the constraint is partitioned, we need to
   13075                 :             :          * recurse and handle every unvalidated constraint that is a child of this
   13076                 :             :          * constraint.
   13077                 :             :          */
   13078   [ +  +  +  + ]:          22 :         if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   13079                 :          14 :                 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
   13080                 :             :         {
   13081                 :          13 :                 ScanKeyData pkey;
   13082                 :          13 :                 SysScanDesc pscan;
   13083                 :          13 :                 HeapTuple       childtup;
   13084                 :             : 
   13085                 :          13 :                 ScanKeyInit(&pkey,
   13086                 :             :                                         Anum_pg_constraint_conparentid,
   13087                 :             :                                         BTEqualStrategyNumber, F_OIDEQ,
   13088                 :          13 :                                         ObjectIdGetDatum(con->oid));
   13089                 :             : 
   13090                 :          13 :                 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   13091                 :             :                                                                    true, NULL, 1, &pkey);
   13092                 :             : 
   13093         [ +  + ]:          26 :                 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   13094                 :             :                 {
   13095                 :          13 :                         Form_pg_constraint childcon;
   13096                 :          13 :                         Relation        childrel;
   13097                 :             : 
   13098                 :          13 :                         childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   13099                 :             : 
   13100                 :             :                         /*
   13101                 :             :                          * If the child constraint has already been validated, no further
   13102                 :             :                          * action is required for it or its descendants, as they are all
   13103                 :             :                          * valid.
   13104                 :             :                          */
   13105         [ +  + ]:          13 :                         if (childcon->convalidated)
   13106                 :           3 :                                 continue;
   13107                 :             : 
   13108                 :          10 :                         childrel = table_open(childcon->conrelid, lockmode);
   13109                 :             : 
   13110                 :             :                         /*
   13111                 :             :                          * NB: Note that pkrelid should be passed as-is during recursion,
   13112                 :             :                          * as it is required to identify the root referenced table.
   13113                 :             :                          */
   13114                 :          20 :                         QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
   13115                 :          10 :                                                                                 childtup, lockmode);
   13116                 :          10 :                         table_close(childrel, NoLock);
   13117      [ -  +  + ]:          13 :                 }
   13118                 :             : 
   13119                 :          13 :                 systable_endscan(pscan);
   13120                 :          13 :         }
   13121                 :             : 
   13122                 :             :         /*
   13123                 :             :          * Now mark the pg_constraint row as validated (even if we didn't check,
   13124                 :             :          * notably the ones for partitions on the referenced side).
   13125                 :             :          *
   13126                 :             :          * We rely on transaction abort to roll back this change if phase 3
   13127                 :             :          * ultimately finds violating rows.  This is a bit ugly.
   13128                 :             :          */
   13129                 :          22 :         copyTuple = heap_copytuple(contuple);
   13130                 :          22 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   13131                 :          22 :         copy_con->convalidated = true;
   13132                 :          22 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   13133                 :             : 
   13134         [ +  - ]:          22 :         InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   13135                 :             : 
   13136                 :          22 :         heap_freetuple(copyTuple);
   13137                 :          22 : }
   13138                 :             : 
   13139                 :             : /*
   13140                 :             :  * QueueCheckConstraintValidation
   13141                 :             :  *
   13142                 :             :  * Add an entry to the wqueue to validate the given check constraint in Phase 3
   13143                 :             :  * and update the convalidated field in the pg_constraint catalog for the
   13144                 :             :  * specified relation and all its inheriting children.
   13145                 :             :  */
   13146                 :             : static void
   13147                 :          21 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
   13148                 :             :                                                            char *constrName, HeapTuple contuple,
   13149                 :             :                                                            bool recurse, bool recursing, LOCKMODE lockmode)
   13150                 :             : {
   13151                 :          21 :         Form_pg_constraint con;
   13152                 :          21 :         AlteredTableInfo *tab;
   13153                 :          21 :         HeapTuple       copyTuple;
   13154                 :          21 :         Form_pg_constraint copy_con;
   13155                 :             : 
   13156                 :          21 :         List       *children = NIL;
   13157                 :          21 :         ListCell   *child;
   13158                 :          21 :         NewConstraint *newcon;
   13159                 :          21 :         Datum           val;
   13160                 :          21 :         char       *conbin;
   13161                 :             : 
   13162                 :          21 :         con = (Form_pg_constraint) GETSTRUCT(contuple);
   13163         [ +  - ]:          21 :         Assert(con->contype == CONSTRAINT_CHECK);
   13164                 :             : 
   13165                 :             :         /*
   13166                 :             :          * If we're recursing, the parent has already done this, so skip it. Also,
   13167                 :             :          * if the constraint is a NO INHERIT constraint, we shouldn't try to look
   13168                 :             :          * for it in the children.
   13169                 :             :          */
   13170   [ +  +  +  + ]:          21 :         if (!recursing && !con->connoinherit)
   13171                 :          24 :                 children = find_all_inheritors(RelationGetRelid(rel),
   13172                 :          12 :                                                                            lockmode, NULL);
   13173                 :             : 
   13174                 :             :         /*
   13175                 :             :          * For CHECK constraints, we must ensure that we only mark the constraint
   13176                 :             :          * as validated on the parent if it's already validated on the children.
   13177                 :             :          *
   13178                 :             :          * We recurse before validating on the parent, to reduce risk of
   13179                 :             :          * deadlocks.
   13180                 :             :          */
   13181   [ +  +  +  +  :          41 :         foreach(child, children)
                   +  + ]
   13182                 :             :         {
   13183                 :          20 :                 Oid                     childoid = lfirst_oid(child);
   13184                 :          20 :                 Relation        childrel;
   13185                 :             : 
   13186         [ +  + ]:          20 :                 if (childoid == RelationGetRelid(rel))
   13187                 :          12 :                         continue;
   13188                 :             : 
   13189                 :             :                 /*
   13190                 :             :                  * If we are told not to recurse, there had better not be any child
   13191                 :             :                  * tables, because we can't mark the constraint on the parent valid
   13192                 :             :                  * unless it is valid for all child tables.
   13193                 :             :                  */
   13194         [ +  - ]:           8 :                 if (!recurse)
   13195   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   13196                 :             :                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13197                 :             :                                          errmsg("constraint must be validated on child tables too")));
   13198                 :             : 
   13199                 :             :                 /* find_all_inheritors already got lock */
   13200                 :           8 :                 childrel = table_open(childoid, NoLock);
   13201                 :             : 
   13202                 :          16 :                 ATExecValidateConstraint(wqueue, childrel, constrName, false,
   13203                 :           8 :                                                                  true, lockmode);
   13204                 :           8 :                 table_close(childrel, NoLock);
   13205      [ -  +  + ]:          20 :         }
   13206                 :             : 
   13207                 :             :         /* Queue validation for phase 3 */
   13208                 :          21 :         newcon = palloc0_object(NewConstraint);
   13209                 :          21 :         newcon->name = constrName;
   13210                 :          21 :         newcon->contype = CONSTR_CHECK;
   13211                 :          21 :         newcon->refrelid = InvalidOid;
   13212                 :          21 :         newcon->refindid = InvalidOid;
   13213                 :          21 :         newcon->conid = con->oid;
   13214                 :             : 
   13215                 :          21 :         val = SysCacheGetAttrNotNull(CONSTROID, contuple,
   13216                 :             :                                                                  Anum_pg_constraint_conbin);
   13217                 :          21 :         conbin = TextDatumGetCString(val);
   13218                 :          21 :         newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
   13219                 :             : 
   13220                 :             :         /* Find or create work queue entry for this table */
   13221                 :          21 :         tab = ATGetQueueEntry(wqueue, rel);
   13222                 :          21 :         tab->constraints = lappend(tab->constraints, newcon);
   13223                 :             : 
   13224                 :             :         /*
   13225                 :             :          * Invalidate relcache so that others see the new validated constraint.
   13226                 :             :          */
   13227                 :          21 :         CacheInvalidateRelcache(rel);
   13228                 :             : 
   13229                 :             :         /*
   13230                 :             :          * Now update the catalog, while we have the door open.
   13231                 :             :          */
   13232                 :          21 :         copyTuple = heap_copytuple(contuple);
   13233                 :          21 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   13234                 :          21 :         copy_con->convalidated = true;
   13235                 :          21 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   13236                 :             : 
   13237         [ +  - ]:          21 :         InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   13238                 :             : 
   13239                 :          21 :         heap_freetuple(copyTuple);
   13240                 :          21 : }
   13241                 :             : 
   13242                 :             : /*
   13243                 :             :  * QueueNNConstraintValidation
   13244                 :             :  *
   13245                 :             :  * Add an entry to the wqueue to validate the given not-null constraint in
   13246                 :             :  * Phase 3 and update the convalidated field in the pg_constraint catalog for
   13247                 :             :  * the specified relation and all its inheriting children.
   13248                 :             :  */
   13249                 :             : static void
   13250                 :          18 : QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
   13251                 :             :                                                         HeapTuple contuple, bool recurse, bool recursing,
   13252                 :             :                                                         LOCKMODE lockmode)
   13253                 :             : {
   13254                 :          18 :         Form_pg_constraint con;
   13255                 :          18 :         AlteredTableInfo *tab;
   13256                 :          18 :         HeapTuple       copyTuple;
   13257                 :          18 :         Form_pg_constraint copy_con;
   13258                 :          18 :         List       *children = NIL;
   13259                 :          18 :         AttrNumber      attnum;
   13260                 :          18 :         char       *colname;
   13261                 :             : 
   13262                 :          18 :         con = (Form_pg_constraint) GETSTRUCT(contuple);
   13263         [ +  - ]:          18 :         Assert(con->contype == CONSTRAINT_NOTNULL);
   13264                 :             : 
   13265                 :          18 :         attnum = extractNotNullColumn(contuple);
   13266                 :             : 
   13267                 :             :         /*
   13268                 :             :          * If we're recursing, we've already done this for parent, so skip it.
   13269                 :             :          * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
   13270                 :             :          * look for it in the children.
   13271                 :             :          *
   13272                 :             :          * We recurse before validating on the parent, to reduce risk of
   13273                 :             :          * deadlocks.
   13274                 :             :          */
   13275   [ +  +  -  + ]:          18 :         if (!recursing && !con->connoinherit)
   13276                 :          12 :                 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
   13277                 :             : 
   13278                 :          18 :         colname = get_attname(RelationGetRelid(rel), attnum, false);
   13279   [ +  +  +  +  :          61 :         foreach_oid(childoid, children)
             +  +  +  + ]
   13280                 :             :         {
   13281                 :          25 :                 Relation        childrel;
   13282                 :          25 :                 HeapTuple       contup;
   13283                 :          25 :                 Form_pg_constraint childcon;
   13284                 :          25 :                 char       *conname;
   13285                 :             : 
   13286         [ +  + ]:          25 :                 if (childoid == RelationGetRelid(rel))
   13287                 :          12 :                         continue;
   13288                 :             : 
   13289                 :             :                 /*
   13290                 :             :                  * If we are told not to recurse, there had better not be any child
   13291                 :             :                  * tables, because we can't mark the constraint on the parent valid
   13292                 :             :                  * unless it is valid for all child tables.
   13293                 :             :                  */
   13294         [ +  - ]:          13 :                 if (!recurse)
   13295   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   13296                 :             :                                         errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13297                 :             :                                         errmsg("constraint must be validated on child tables too"));
   13298                 :             : 
   13299                 :             :                 /*
   13300                 :             :                  * The column on child might have a different attnum, so search by
   13301                 :             :                  * column name.
   13302                 :             :                  */
   13303                 :          13 :                 contup = findNotNullConstraint(childoid, colname);
   13304         [ +  - ]:          13 :                 if (!contup)
   13305   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
   13306                 :             :                                  colname, get_rel_name(childoid));
   13307                 :          13 :                 childcon = (Form_pg_constraint) GETSTRUCT(contup);
   13308         [ +  + ]:          13 :                 if (childcon->convalidated)
   13309                 :           7 :                         continue;
   13310                 :             : 
   13311                 :             :                 /* find_all_inheritors already got lock */
   13312                 :           6 :                 childrel = table_open(childoid, NoLock);
   13313                 :           6 :                 conname = pstrdup(NameStr(childcon->conname));
   13314                 :             : 
   13315                 :             :                 /* XXX improve ATExecValidateConstraint API to avoid double search */
   13316                 :          12 :                 ATExecValidateConstraint(wqueue, childrel, conname,
   13317                 :           6 :                                                                  false, true, lockmode);
   13318                 :           6 :                 table_close(childrel, NoLock);
   13319      [ -  +  + ]:          43 :         }
   13320                 :             : 
   13321                 :             :         /* Set attnotnull appropriately without queueing another validation */
   13322                 :          18 :         set_attnotnull(NULL, rel, attnum, true, false);
   13323                 :             : 
   13324                 :          18 :         tab = ATGetQueueEntry(wqueue, rel);
   13325                 :          18 :         tab->verify_new_notnull = true;
   13326                 :             : 
   13327                 :             :         /*
   13328                 :             :          * Invalidate relcache so that others see the new validated constraint.
   13329                 :             :          */
   13330                 :          18 :         CacheInvalidateRelcache(rel);
   13331                 :             : 
   13332                 :             :         /*
   13333                 :             :          * Now update the catalogs, while we have the door open.
   13334                 :             :          */
   13335                 :          18 :         copyTuple = heap_copytuple(contuple);
   13336                 :          18 :         copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   13337                 :          18 :         copy_con->convalidated = true;
   13338                 :          18 :         CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   13339                 :             : 
   13340         [ -  + ]:          18 :         InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   13341                 :             : 
   13342                 :          18 :         heap_freetuple(copyTuple);
   13343                 :          18 : }
   13344                 :             : 
   13345                 :             : /*
   13346                 :             :  * transformColumnNameList - transform list of column names
   13347                 :             :  *
   13348                 :             :  * Lookup each name and return its attnum and, optionally, type and collation
   13349                 :             :  * OIDs
   13350                 :             :  *
   13351                 :             :  * Note: the name of this function suggests that it's general-purpose,
   13352                 :             :  * but actually it's only used to look up names appearing in foreign-key
   13353                 :             :  * clauses.  The error messages would need work to use it in other cases,
   13354                 :             :  * and perhaps the validity checks as well.
   13355                 :             :  */
   13356                 :             : static int
   13357                 :         786 : transformColumnNameList(Oid relId, List *colList,
   13358                 :             :                                                 int16 *attnums, Oid *atttypids, Oid *attcollids)
   13359                 :             : {
   13360                 :         786 :         ListCell   *l;
   13361                 :         786 :         int                     attnum;
   13362                 :             : 
   13363                 :         786 :         attnum = 0;
   13364   [ +  +  +  +  :        1483 :         foreach(l, colList)
                   +  + ]
   13365                 :             :         {
   13366                 :         708 :                 char       *attname = strVal(lfirst(l));
   13367                 :         708 :                 HeapTuple       atttuple;
   13368                 :         708 :                 Form_pg_attribute attform;
   13369                 :             : 
   13370                 :         708 :                 atttuple = SearchSysCacheAttName(relId, attname);
   13371         [ +  + ]:         708 :                 if (!HeapTupleIsValid(atttuple))
   13372   [ +  -  +  - ]:           9 :                         ereport(ERROR,
   13373                 :             :                                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   13374                 :             :                                          errmsg("column \"%s\" referenced in foreign key constraint does not exist",
   13375                 :             :                                                         attname)));
   13376                 :         699 :                 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   13377         [ +  + ]:         699 :                 if (attform->attnum < 0)
   13378   [ +  -  +  - ]:           2 :                         ereport(ERROR,
   13379                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13380                 :             :                                          errmsg("system columns cannot be used in foreign keys")));
   13381         [ +  - ]:         697 :                 if (attnum >= INDEX_MAX_KEYS)
   13382   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   13383                 :             :                                         (errcode(ERRCODE_TOO_MANY_COLUMNS),
   13384                 :             :                                          errmsg("cannot have more than %d keys in a foreign key",
   13385                 :             :                                                         INDEX_MAX_KEYS)));
   13386                 :         697 :                 attnums[attnum] = attform->attnum;
   13387         [ +  + ]:         697 :                 if (atttypids != NULL)
   13388                 :         691 :                         atttypids[attnum] = attform->atttypid;
   13389         [ +  + ]:         697 :                 if (attcollids != NULL)
   13390                 :         691 :                         attcollids[attnum] = attform->attcollation;
   13391                 :         697 :                 ReleaseSysCache(atttuple);
   13392                 :         697 :                 attnum++;
   13393                 :         697 :         }
   13394                 :             : 
   13395                 :        1550 :         return attnum;
   13396                 :         775 : }
   13397                 :             : 
   13398                 :             : /*
   13399                 :             :  * transformFkeyGetPrimaryKey -
   13400                 :             :  *
   13401                 :             :  *      Look up the names, attnums, types, and collations of the primary key attributes
   13402                 :             :  *      for the pkrel.  Also return the index OID and index opclasses of the
   13403                 :             :  *      index supporting the primary key.  Also return whether the index has
   13404                 :             :  *      WITHOUT OVERLAPS.
   13405                 :             :  *
   13406                 :             :  *      All parameters except pkrel are output parameters.  Also, the function
   13407                 :             :  *      return value is the number of attributes in the primary key.
   13408                 :             :  *
   13409                 :             :  *      Used when the column list in the REFERENCES specification is omitted.
   13410                 :             :  */
   13411                 :             : static int
   13412                 :         169 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
   13413                 :             :                                                    List **attnamelist,
   13414                 :             :                                                    int16 *attnums, Oid *atttypids, Oid *attcollids,
   13415                 :             :                                                    Oid *opclasses, bool *pk_has_without_overlaps)
   13416                 :             : {
   13417                 :         169 :         List       *indexoidlist;
   13418                 :         169 :         ListCell   *indexoidscan;
   13419                 :         169 :         HeapTuple       indexTuple = NULL;
   13420                 :         169 :         Form_pg_index indexStruct = NULL;
   13421                 :         169 :         Datum           indclassDatum;
   13422                 :         169 :         oidvector  *indclass;
   13423                 :         169 :         int                     i;
   13424                 :             : 
   13425                 :             :         /*
   13426                 :             :          * Get the list of index OIDs for the table from the relcache, and look up
   13427                 :             :          * each one in the pg_index syscache until we find one marked primary key
   13428                 :             :          * (hopefully there isn't more than one such).  Insist it's valid, too.
   13429                 :             :          */
   13430                 :         169 :         *indexOid = InvalidOid;
   13431                 :             : 
   13432                 :         169 :         indexoidlist = RelationGetIndexList(pkrel);
   13433                 :             : 
   13434   [ +  -  -  +  :         339 :         foreach(indexoidscan, indexoidlist)
                   +  - ]
   13435                 :             :         {
   13436                 :         170 :                 Oid                     indexoid = lfirst_oid(indexoidscan);
   13437                 :             : 
   13438                 :         170 :                 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   13439         [ +  - ]:         170 :                 if (!HeapTupleIsValid(indexTuple))
   13440   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for index %u", indexoid);
   13441                 :         170 :                 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   13442   [ +  +  -  + ]:         170 :                 if (indexStruct->indisprimary && indexStruct->indisvalid)
   13443                 :             :                 {
   13444                 :             :                         /*
   13445                 :             :                          * Refuse to use a deferrable primary key.  This is per SQL spec,
   13446                 :             :                          * and there would be a lot of interesting semantic problems if we
   13447                 :             :                          * tried to allow it.
   13448                 :             :                          */
   13449         [ +  - ]:         169 :                         if (!indexStruct->indimmediate)
   13450   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   13451                 :             :                                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   13452                 :             :                                                  errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
   13453                 :             :                                                                 RelationGetRelationName(pkrel))));
   13454                 :             : 
   13455                 :         169 :                         *indexOid = indexoid;
   13456                 :         169 :                         break;
   13457                 :             :                 }
   13458                 :           1 :                 ReleaseSysCache(indexTuple);
   13459         [ +  + ]:         170 :         }
   13460                 :             : 
   13461                 :         169 :         list_free(indexoidlist);
   13462                 :             : 
   13463                 :             :         /*
   13464                 :             :          * Check that we found it
   13465                 :             :          */
   13466         [ +  - ]:         169 :         if (!OidIsValid(*indexOid))
   13467   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   13468                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   13469                 :             :                                  errmsg("there is no primary key for referenced table \"%s\"",
   13470                 :             :                                                 RelationGetRelationName(pkrel))));
   13471                 :             : 
   13472                 :             :         /* Must get indclass the hard way */
   13473                 :         169 :         indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   13474                 :             :                                                                                    Anum_pg_index_indclass);
   13475                 :         169 :         indclass = (oidvector *) DatumGetPointer(indclassDatum);
   13476                 :             : 
   13477                 :             :         /*
   13478                 :             :          * Now build the list of PK attributes from the indkey definition (we
   13479                 :             :          * assume a primary key cannot have expressional elements)
   13480                 :             :          */
   13481                 :         169 :         *attnamelist = NIL;
   13482         [ +  + ]:         411 :         for (i = 0; i < indexStruct->indnkeyatts; i++)
   13483                 :             :         {
   13484                 :         242 :                 int                     pkattno = indexStruct->indkey.values[i];
   13485                 :             : 
   13486                 :         242 :                 attnums[i] = pkattno;
   13487                 :         242 :                 atttypids[i] = attnumTypeId(pkrel, pkattno);
   13488                 :         242 :                 attcollids[i] = attnumCollationId(pkrel, pkattno);
   13489                 :         242 :                 opclasses[i] = indclass->values[i];
   13490                 :         484 :                 *attnamelist = lappend(*attnamelist,
   13491                 :         242 :                                                            makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
   13492                 :         242 :         }
   13493                 :             : 
   13494                 :         169 :         *pk_has_without_overlaps = indexStruct->indisexclusion;
   13495                 :             : 
   13496                 :         169 :         ReleaseSysCache(indexTuple);
   13497                 :             : 
   13498                 :         338 :         return i;
   13499                 :         169 : }
   13500                 :             : 
   13501                 :             : /*
   13502                 :             :  * transformFkeyCheckAttrs -
   13503                 :             :  *
   13504                 :             :  *      Validate that the 'attnums' columns in the 'pkrel' relation are valid to
   13505                 :             :  *      reference as part of a foreign key constraint.
   13506                 :             :  *
   13507                 :             :  *      Returns the OID of the unique index supporting the constraint and
   13508                 :             :  *      populates the caller-provided 'opclasses' array with the opclasses
   13509                 :             :  *      associated with the index columns.  Also sets whether the index
   13510                 :             :  *      uses WITHOUT OVERLAPS.
   13511                 :             :  *
   13512                 :             :  *      Raises an ERROR on validation failure.
   13513                 :             :  */
   13514                 :             : static Oid
   13515                 :         136 : transformFkeyCheckAttrs(Relation pkrel,
   13516                 :             :                                                 int numattrs, int16 *attnums,
   13517                 :             :                                                 bool with_period, Oid *opclasses,
   13518                 :             :                                                 bool *pk_has_without_overlaps)
   13519                 :             : {
   13520                 :         136 :         Oid                     indexoid = InvalidOid;
   13521                 :         136 :         bool            found = false;
   13522                 :         136 :         bool            found_deferrable = false;
   13523                 :         136 :         List       *indexoidlist;
   13524                 :         136 :         ListCell   *indexoidscan;
   13525                 :         136 :         int                     i,
   13526                 :             :                                 j;
   13527                 :             : 
   13528                 :             :         /*
   13529                 :             :          * Reject duplicate appearances of columns in the referenced-columns list.
   13530                 :             :          * Such a case is forbidden by the SQL standard, and even if we thought it
   13531                 :             :          * useful to allow it, there would be ambiguity about how to match the
   13532                 :             :          * list to unique indexes (in particular, it'd be unclear which index
   13533                 :             :          * opclass goes with which FK column).
   13534                 :             :          */
   13535         [ +  + ]:         339 :         for (i = 0; i < numattrs; i++)
   13536                 :             :         {
   13537         [ +  + ]:         289 :                 for (j = i + 1; j < numattrs; j++)
   13538                 :             :                 {
   13539         [ +  + ]:          86 :                         if (attnums[i] == attnums[j])
   13540   [ +  -  +  - ]:           4 :                                 ereport(ERROR,
   13541                 :             :                                                 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   13542                 :             :                                                  errmsg("foreign key referenced-columns list must not contain duplicates")));
   13543                 :          82 :                 }
   13544                 :         203 :         }
   13545                 :             : 
   13546                 :             :         /*
   13547                 :             :          * Get the list of index OIDs for the table from the relcache, and look up
   13548                 :             :          * each one in the pg_index syscache, and match unique indexes to the list
   13549                 :             :          * of attnums we are given.
   13550                 :             :          */
   13551                 :         132 :         indexoidlist = RelationGetIndexList(pkrel);
   13552                 :             : 
   13553   [ +  -  +  +  :         291 :         foreach(indexoidscan, indexoidlist)
                   +  + ]
   13554                 :             :         {
   13555                 :         159 :                 HeapTuple       indexTuple;
   13556                 :         159 :                 Form_pg_index indexStruct;
   13557                 :             : 
   13558                 :         159 :                 indexoid = lfirst_oid(indexoidscan);
   13559                 :         159 :                 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   13560         [ +  - ]:         159 :                 if (!HeapTupleIsValid(indexTuple))
   13561   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for index %u", indexoid);
   13562                 :         159 :                 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   13563                 :             : 
   13564                 :             :                 /*
   13565                 :             :                  * Must have the right number of columns; must be unique (or if
   13566                 :             :                  * temporal then exclusion instead) and not a partial index; forget it
   13567                 :             :                  * if there are any expressions, too. Invalid indexes are out as well.
   13568                 :             :                  */
   13569         [ +  + ]:         159 :                 if (indexStruct->indnkeyatts == numattrs &&
   13570   [ +  +  +  + ]:         141 :                         (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
   13571                 :         139 :                         indexStruct->indisvalid &&
   13572   [ +  -  -  + ]:         139 :                         heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
   13573                 :         139 :                         heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
   13574                 :             :                 {
   13575                 :         139 :                         Datum           indclassDatum;
   13576                 :         139 :                         oidvector  *indclass;
   13577                 :             : 
   13578                 :             :                         /* Must get indclass the hard way */
   13579                 :         139 :                         indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   13580                 :             :                                                                                                    Anum_pg_index_indclass);
   13581                 :         139 :                         indclass = (oidvector *) DatumGetPointer(indclassDatum);
   13582                 :             : 
   13583                 :             :                         /*
   13584                 :             :                          * The given attnum list may match the index columns in any order.
   13585                 :             :                          * Check for a match, and extract the appropriate opclasses while
   13586                 :             :                          * we're at it.
   13587                 :             :                          *
   13588                 :             :                          * We know that attnums[] is duplicate-free per the test at the
   13589                 :             :                          * start of this function, and we checked above that the number of
   13590                 :             :                          * index columns agrees, so if we find a match for each attnums[]
   13591                 :             :                          * entry then we must have a one-to-one match in some order.
   13592                 :             :                          */
   13593         [ +  + ]:         340 :                         for (i = 0; i < numattrs; i++)
   13594                 :             :                         {
   13595                 :         210 :                                 found = false;
   13596         [ +  + ]:         301 :                                 for (j = 0; j < numattrs; j++)
   13597                 :             :                                 {
   13598         [ +  + ]:         292 :                                         if (attnums[i] == indexStruct->indkey.values[j])
   13599                 :             :                                         {
   13600                 :         201 :                                                 opclasses[i] = indclass->values[j];
   13601                 :         201 :                                                 found = true;
   13602                 :         201 :                                                 break;
   13603                 :             :                                         }
   13604                 :          91 :                                 }
   13605         [ +  + ]:         210 :                                 if (!found)
   13606                 :           9 :                                         break;
   13607                 :         201 :                         }
   13608                 :             :                         /* The last attribute in the index must be the PERIOD FK part */
   13609   [ +  +  +  + ]:         139 :                         if (found && with_period)
   13610                 :             :                         {
   13611                 :          19 :                                 int16           periodattnum = attnums[numattrs - 1];
   13612                 :             : 
   13613                 :          19 :                                 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
   13614                 :          19 :                         }
   13615                 :             : 
   13616                 :             :                         /*
   13617                 :             :                          * Refuse to use a deferrable unique/primary key.  This is per SQL
   13618                 :             :                          * spec, and there would be a lot of interesting semantic problems
   13619                 :             :                          * if we tried to allow it.
   13620                 :             :                          */
   13621   [ +  +  +  - ]:         139 :                         if (found && !indexStruct->indimmediate)
   13622                 :             :                         {
   13623                 :             :                                 /*
   13624                 :             :                                  * Remember that we found an otherwise matching index, so that
   13625                 :             :                                  * we can generate a more appropriate error message.
   13626                 :             :                                  */
   13627                 :           0 :                                 found_deferrable = true;
   13628                 :           0 :                                 found = false;
   13629                 :           0 :                         }
   13630                 :             : 
   13631                 :             :                         /* We need to know whether the index has WITHOUT OVERLAPS */
   13632         [ +  + ]:         139 :                         if (found)
   13633                 :         130 :                                 *pk_has_without_overlaps = indexStruct->indisexclusion;
   13634                 :         139 :                 }
   13635                 :         159 :                 ReleaseSysCache(indexTuple);
   13636         [ +  + ]:         159 :                 if (found)
   13637                 :         130 :                         break;
   13638         [ +  + ]:         159 :         }
   13639                 :             : 
   13640         [ +  + ]:         132 :         if (!found)
   13641                 :             :         {
   13642         [ -  + ]:           2 :                 if (found_deferrable)
   13643   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   13644                 :             :                                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   13645                 :             :                                          errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
   13646                 :             :                                                         RelationGetRelationName(pkrel))));
   13647                 :             :                 else
   13648   [ +  -  +  - ]:           2 :                         ereport(ERROR,
   13649                 :             :                                         (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   13650                 :             :                                          errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
   13651                 :             :                                                         RelationGetRelationName(pkrel))));
   13652                 :           0 :         }
   13653                 :             : 
   13654                 :         130 :         list_free(indexoidlist);
   13655                 :             : 
   13656                 :         260 :         return indexoid;
   13657                 :         130 : }
   13658                 :             : 
   13659                 :             : /*
   13660                 :             :  * findFkeyCast -
   13661                 :             :  *
   13662                 :             :  *      Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
   13663                 :             :  *      Caller has equal regard for binary coercibility and for an exact match.
   13664                 :             : */
   13665                 :             : static CoercionPathType
   13666                 :           2 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
   13667                 :             : {
   13668                 :           2 :         CoercionPathType ret;
   13669                 :             : 
   13670         [ +  - ]:           2 :         if (targetTypeId == sourceTypeId)
   13671                 :             :         {
   13672                 :           2 :                 ret = COERCION_PATH_RELABELTYPE;
   13673                 :           2 :                 *funcid = InvalidOid;
   13674                 :           2 :         }
   13675                 :             :         else
   13676                 :             :         {
   13677                 :           0 :                 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
   13678                 :           0 :                                                                         COERCION_IMPLICIT, funcid);
   13679         [ #  # ]:           0 :                 if (ret == COERCION_PATH_NONE)
   13680                 :             :                         /* A previously-relied-upon cast is now gone. */
   13681   [ #  #  #  # ]:           0 :                         elog(ERROR, "could not find cast from %u to %u",
   13682                 :             :                                  sourceTypeId, targetTypeId);
   13683                 :             :         }
   13684                 :             : 
   13685                 :           4 :         return ret;
   13686                 :           2 : }
   13687                 :             : 
   13688                 :             : /*
   13689                 :             :  * Permissions checks on the referenced table for ADD FOREIGN KEY
   13690                 :             :  *
   13691                 :             :  * Note: we have already checked that the user owns the referencing table,
   13692                 :             :  * else we'd have failed much earlier; no additional checks are needed for it.
   13693                 :             :  */
   13694                 :             : static void
   13695                 :         293 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
   13696                 :             : {
   13697                 :         293 :         Oid                     roleid = GetUserId();
   13698                 :         293 :         AclResult       aclresult;
   13699                 :         293 :         int                     i;
   13700                 :             : 
   13701                 :             :         /* Okay if we have relation-level REFERENCES permission */
   13702                 :         293 :         aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
   13703                 :             :                                                                   ACL_REFERENCES);
   13704         [ -  + ]:         293 :         if (aclresult == ACLCHECK_OK)
   13705                 :         293 :                 return;
   13706                 :             :         /* Else we must have REFERENCES on each column */
   13707         [ #  # ]:           0 :         for (i = 0; i < natts; i++)
   13708                 :             :         {
   13709                 :           0 :                 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
   13710                 :           0 :                                                                                   roleid, ACL_REFERENCES);
   13711         [ #  # ]:           0 :                 if (aclresult != ACLCHECK_OK)
   13712                 :           0 :                         aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
   13713                 :           0 :                                                    RelationGetRelationName(rel));
   13714                 :           0 :         }
   13715         [ -  + ]:         293 : }
   13716                 :             : 
   13717                 :             : /*
   13718                 :             :  * Scan the existing rows in a table to verify they meet a proposed FK
   13719                 :             :  * constraint.
   13720                 :             :  *
   13721                 :             :  * Caller must have opened and locked both relations appropriately.
   13722                 :             :  */
   13723                 :             : static void
   13724                 :         116 : validateForeignKeyConstraint(char *conname,
   13725                 :             :                                                          Relation rel,
   13726                 :             :                                                          Relation pkrel,
   13727                 :             :                                                          Oid pkindOid,
   13728                 :             :                                                          Oid constraintOid,
   13729                 :             :                                                          bool hasperiod)
   13730                 :             : {
   13731                 :         116 :         TupleTableSlot *slot;
   13732                 :         116 :         TableScanDesc scan;
   13733                 :         116 :         Trigger         trig = {0};
   13734                 :         116 :         Snapshot        snapshot;
   13735                 :         116 :         MemoryContext oldcxt;
   13736                 :         116 :         MemoryContext perTupCxt;
   13737                 :             : 
   13738   [ -  +  -  + ]:         116 :         ereport(DEBUG1,
   13739                 :             :                         (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
   13740                 :             : 
   13741                 :             :         /*
   13742                 :             :          * Build a trigger call structure; we'll need it either way.
   13743                 :             :          */
   13744                 :         116 :         trig.tgoid = InvalidOid;
   13745                 :         116 :         trig.tgname = conname;
   13746                 :         116 :         trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   13747                 :         116 :         trig.tgisinternal = true;
   13748                 :         116 :         trig.tgconstrrelid = RelationGetRelid(pkrel);
   13749                 :         116 :         trig.tgconstrindid = pkindOid;
   13750                 :         116 :         trig.tgconstraint = constraintOid;
   13751                 :         116 :         trig.tgdeferrable = false;
   13752                 :         116 :         trig.tginitdeferred = false;
   13753                 :             :         /* we needn't fill in remaining fields */
   13754                 :             : 
   13755                 :             :         /*
   13756                 :             :          * See if we can do it with a single LEFT JOIN query.  A false result
   13757                 :             :          * indicates we must proceed with the fire-the-trigger method. We can't do
   13758                 :             :          * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
   13759                 :             :          * left joins.
   13760                 :             :          */
   13761   [ +  -  +  + ]:         116 :         if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
   13762                 :         102 :                 return;
   13763                 :             : 
   13764                 :             :         /*
   13765                 :             :          * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
   13766                 :             :          * if that tuple had just been inserted.  If any of those fail, it should
   13767                 :             :          * ereport(ERROR) and that's that.
   13768                 :             :          */
   13769                 :          14 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
   13770                 :          14 :         slot = table_slot_create(rel, NULL);
   13771                 :          14 :         scan = table_beginscan(rel, snapshot, 0, NULL);
   13772                 :             : 
   13773                 :          14 :         perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   13774                 :             :                                                                           "validateForeignKeyConstraint",
   13775                 :             :                                                                           ALLOCSET_SMALL_SIZES);
   13776                 :          14 :         oldcxt = MemoryContextSwitchTo(perTupCxt);
   13777                 :             : 
   13778         [ +  + ]:          31 :         while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
   13779                 :             :         {
   13780                 :          17 :                 LOCAL_FCINFO(fcinfo, 0);
   13781                 :          17 :                 TriggerData trigdata = {0};
   13782                 :             : 
   13783         [ +  - ]:          17 :                 CHECK_FOR_INTERRUPTS();
   13784                 :             : 
   13785                 :             :                 /*
   13786                 :             :                  * Make a call to the trigger function
   13787                 :             :                  *
   13788                 :             :                  * No parameters are passed, but we do set a context
   13789                 :             :                  */
   13790   [ +  -  +  -  :          85 :                 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
          +  -  -  +  +  
                      + ]
   13791                 :             : 
   13792                 :             :                 /*
   13793                 :             :                  * We assume RI_FKey_check_ins won't look at flinfo...
   13794                 :             :                  */
   13795                 :          17 :                 trigdata.type = T_TriggerData;
   13796                 :          17 :                 trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
   13797                 :          17 :                 trigdata.tg_relation = rel;
   13798                 :          17 :                 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
   13799                 :          17 :                 trigdata.tg_trigslot = slot;
   13800                 :          17 :                 trigdata.tg_trigger = &trig;
   13801                 :             : 
   13802                 :          17 :                 fcinfo->context = (Node *) &trigdata;
   13803                 :             : 
   13804                 :          17 :                 RI_FKey_check_ins(fcinfo);
   13805                 :             : 
   13806                 :          17 :                 MemoryContextReset(perTupCxt);
   13807                 :          17 :         }
   13808                 :             : 
   13809                 :          14 :         MemoryContextSwitchTo(oldcxt);
   13810                 :          14 :         MemoryContextDelete(perTupCxt);
   13811                 :          14 :         table_endscan(scan);
   13812                 :          14 :         UnregisterSnapshot(snapshot);
   13813                 :          14 :         ExecDropSingleTupleTableSlot(slot);
   13814         [ -  + ]:         116 : }
   13815                 :             : 
   13816                 :             : /*
   13817                 :             :  * CreateFKCheckTrigger
   13818                 :             :  *              Creates the insert (on_insert=true) or update "check" trigger that
   13819                 :             :  *              implements a given foreign key
   13820                 :             :  *
   13821                 :             :  * Returns the OID of the so created trigger.
   13822                 :             :  */
   13823                 :             : static Oid
   13824                 :         724 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
   13825                 :             :                                          Oid constraintOid, Oid indexOid, Oid parentTrigOid,
   13826                 :             :                                          bool on_insert)
   13827                 :             : {
   13828                 :         724 :         ObjectAddress trigAddress;
   13829                 :         724 :         CreateTrigStmt *fk_trigger;
   13830                 :             : 
   13831                 :             :         /*
   13832                 :             :          * Note: for a self-referential FK (referencing and referenced tables are
   13833                 :             :          * the same), it is important that the ON UPDATE action fires before the
   13834                 :             :          * CHECK action, since both triggers will fire on the same row during an
   13835                 :             :          * UPDATE event; otherwise the CHECK trigger will be checking a non-final
   13836                 :             :          * state of the row.  Triggers fire in name order, so we ensure this by
   13837                 :             :          * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
   13838                 :             :          * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
   13839                 :             :          */
   13840                 :         724 :         fk_trigger = makeNode(CreateTrigStmt);
   13841                 :         724 :         fk_trigger->replace = false;
   13842                 :         724 :         fk_trigger->isconstraint = true;
   13843                 :         724 :         fk_trigger->trigname = "RI_ConstraintTrigger_c";
   13844                 :         724 :         fk_trigger->relation = NULL;
   13845                 :             : 
   13846                 :             :         /* Either ON INSERT or ON UPDATE */
   13847         [ +  + ]:         724 :         if (on_insert)
   13848                 :             :         {
   13849                 :         362 :                 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
   13850                 :         362 :                 fk_trigger->events = TRIGGER_TYPE_INSERT;
   13851                 :         362 :         }
   13852                 :             :         else
   13853                 :             :         {
   13854                 :         362 :                 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
   13855                 :         362 :                 fk_trigger->events = TRIGGER_TYPE_UPDATE;
   13856                 :             :         }
   13857                 :             : 
   13858                 :         724 :         fk_trigger->args = NIL;
   13859                 :         724 :         fk_trigger->row = true;
   13860                 :         724 :         fk_trigger->timing = TRIGGER_TYPE_AFTER;
   13861                 :         724 :         fk_trigger->columns = NIL;
   13862                 :         724 :         fk_trigger->whenClause = NULL;
   13863                 :         724 :         fk_trigger->transitionRels = NIL;
   13864                 :         724 :         fk_trigger->deferrable = fkconstraint->deferrable;
   13865                 :         724 :         fk_trigger->initdeferred = fkconstraint->initdeferred;
   13866                 :         724 :         fk_trigger->constrrel = NULL;
   13867                 :             : 
   13868                 :        1448 :         trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
   13869                 :         724 :                                                                 constraintOid, indexOid, InvalidOid,
   13870                 :         724 :                                                                 parentTrigOid, NULL, true, false);
   13871                 :             : 
   13872                 :             :         /* Make changes-so-far visible */
   13873                 :         724 :         CommandCounterIncrement();
   13874                 :             : 
   13875                 :        1448 :         return trigAddress.objectId;
   13876                 :         724 : }
   13877                 :             : 
   13878                 :             : /*
   13879                 :             :  * createForeignKeyActionTriggers
   13880                 :             :  *              Create the referenced-side "action" triggers that implement a foreign
   13881                 :             :  *              key.
   13882                 :             :  *
   13883                 :             :  * Returns the OIDs of the so created triggers in *deleteTrigOid and
   13884                 :             :  * *updateTrigOid.
   13885                 :             :  */
   13886                 :             : static void
   13887                 :         422 : createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
   13888                 :             :                                                            Oid constraintOid, Oid indexOid,
   13889                 :             :                                                            Oid parentDelTrigger, Oid parentUpdTrigger,
   13890                 :             :                                                            Oid *deleteTrigOid, Oid *updateTrigOid)
   13891                 :             : {
   13892                 :         422 :         CreateTrigStmt *fk_trigger;
   13893                 :         422 :         ObjectAddress trigAddress;
   13894                 :             : 
   13895                 :             :         /*
   13896                 :             :          * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   13897                 :             :          * DELETE action on the referenced table.
   13898                 :             :          */
   13899                 :         422 :         fk_trigger = makeNode(CreateTrigStmt);
   13900                 :         422 :         fk_trigger->replace = false;
   13901                 :         422 :         fk_trigger->isconstraint = true;
   13902                 :         422 :         fk_trigger->trigname = "RI_ConstraintTrigger_a";
   13903                 :         422 :         fk_trigger->relation = NULL;
   13904                 :         422 :         fk_trigger->args = NIL;
   13905                 :         422 :         fk_trigger->row = true;
   13906                 :         422 :         fk_trigger->timing = TRIGGER_TYPE_AFTER;
   13907                 :         422 :         fk_trigger->events = TRIGGER_TYPE_DELETE;
   13908                 :         422 :         fk_trigger->columns = NIL;
   13909                 :         422 :         fk_trigger->whenClause = NULL;
   13910                 :         422 :         fk_trigger->transitionRels = NIL;
   13911                 :         422 :         fk_trigger->constrrel = NULL;
   13912                 :             : 
   13913   [ +  +  +  +  :         422 :         switch (fkconstraint->fk_del_action)
                   +  - ]
   13914                 :             :         {
   13915                 :             :                 case FKCONSTR_ACTION_NOACTION:
   13916                 :         319 :                         fk_trigger->deferrable = fkconstraint->deferrable;
   13917                 :         319 :                         fk_trigger->initdeferred = fkconstraint->initdeferred;
   13918                 :         319 :                         fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
   13919                 :         319 :                         break;
   13920                 :             :                 case FKCONSTR_ACTION_RESTRICT:
   13921                 :           5 :                         fk_trigger->deferrable = false;
   13922                 :           5 :                         fk_trigger->initdeferred = false;
   13923                 :           5 :                         fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
   13924                 :           5 :                         break;
   13925                 :             :                 case FKCONSTR_ACTION_CASCADE:
   13926                 :          74 :                         fk_trigger->deferrable = false;
   13927                 :          74 :                         fk_trigger->initdeferred = false;
   13928                 :          74 :                         fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
   13929                 :          74 :                         break;
   13930                 :             :                 case FKCONSTR_ACTION_SETNULL:
   13931                 :          14 :                         fk_trigger->deferrable = false;
   13932                 :          14 :                         fk_trigger->initdeferred = false;
   13933                 :          14 :                         fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
   13934                 :          14 :                         break;
   13935                 :             :                 case FKCONSTR_ACTION_SETDEFAULT:
   13936                 :          10 :                         fk_trigger->deferrable = false;
   13937                 :          10 :                         fk_trigger->initdeferred = false;
   13938                 :          10 :                         fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
   13939                 :          10 :                         break;
   13940                 :             :                 default:
   13941   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized FK action type: %d",
   13942                 :             :                                  (int) fkconstraint->fk_del_action);
   13943                 :           0 :                         break;
   13944                 :             :         }
   13945                 :             : 
   13946                 :         844 :         trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
   13947                 :         422 :                                                                 constraintOid, indexOid, InvalidOid,
   13948                 :         422 :                                                                 parentDelTrigger, NULL, true, false);
   13949         [ -  + ]:         422 :         if (deleteTrigOid)
   13950                 :         422 :                 *deleteTrigOid = trigAddress.objectId;
   13951                 :             : 
   13952                 :             :         /* Make changes-so-far visible */
   13953                 :         422 :         CommandCounterIncrement();
   13954                 :             : 
   13955                 :             :         /*
   13956                 :             :          * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   13957                 :             :          * UPDATE action on the referenced table.
   13958                 :             :          */
   13959                 :         422 :         fk_trigger = makeNode(CreateTrigStmt);
   13960                 :         422 :         fk_trigger->replace = false;
   13961                 :         422 :         fk_trigger->isconstraint = true;
   13962                 :         422 :         fk_trigger->trigname = "RI_ConstraintTrigger_a";
   13963                 :         422 :         fk_trigger->relation = NULL;
   13964                 :         422 :         fk_trigger->args = NIL;
   13965                 :         422 :         fk_trigger->row = true;
   13966                 :         422 :         fk_trigger->timing = TRIGGER_TYPE_AFTER;
   13967                 :         422 :         fk_trigger->events = TRIGGER_TYPE_UPDATE;
   13968                 :         422 :         fk_trigger->columns = NIL;
   13969                 :         422 :         fk_trigger->whenClause = NULL;
   13970                 :         422 :         fk_trigger->transitionRels = NIL;
   13971                 :         422 :         fk_trigger->constrrel = NULL;
   13972                 :             : 
   13973   [ +  +  +  +  :         422 :         switch (fkconstraint->fk_upd_action)
                   +  - ]
   13974                 :             :         {
   13975                 :             :                 case FKCONSTR_ACTION_NOACTION:
   13976                 :         347 :                         fk_trigger->deferrable = fkconstraint->deferrable;
   13977                 :         347 :                         fk_trigger->initdeferred = fkconstraint->initdeferred;
   13978                 :         347 :                         fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
   13979                 :         347 :                         break;
   13980                 :             :                 case FKCONSTR_ACTION_RESTRICT:
   13981                 :           6 :                         fk_trigger->deferrable = false;
   13982                 :           6 :                         fk_trigger->initdeferred = false;
   13983                 :           6 :                         fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
   13984                 :           6 :                         break;
   13985                 :             :                 case FKCONSTR_ACTION_CASCADE:
   13986                 :          52 :                         fk_trigger->deferrable = false;
   13987                 :          52 :                         fk_trigger->initdeferred = false;
   13988                 :          52 :                         fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
   13989                 :          52 :                         break;
   13990                 :             :                 case FKCONSTR_ACTION_SETNULL:
   13991                 :          10 :                         fk_trigger->deferrable = false;
   13992                 :          10 :                         fk_trigger->initdeferred = false;
   13993                 :          10 :                         fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
   13994                 :          10 :                         break;
   13995                 :             :                 case FKCONSTR_ACTION_SETDEFAULT:
   13996                 :           7 :                         fk_trigger->deferrable = false;
   13997                 :           7 :                         fk_trigger->initdeferred = false;
   13998                 :           7 :                         fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
   13999                 :           7 :                         break;
   14000                 :             :                 default:
   14001   [ #  #  #  # ]:           0 :                         elog(ERROR, "unrecognized FK action type: %d",
   14002                 :             :                                  (int) fkconstraint->fk_upd_action);
   14003                 :           0 :                         break;
   14004                 :             :         }
   14005                 :             : 
   14006                 :         844 :         trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
   14007                 :         422 :                                                                 constraintOid, indexOid, InvalidOid,
   14008                 :         422 :                                                                 parentUpdTrigger, NULL, true, false);
   14009         [ -  + ]:         422 :         if (updateTrigOid)
   14010                 :         422 :                 *updateTrigOid = trigAddress.objectId;
   14011                 :         422 : }
   14012                 :             : 
   14013                 :             : /*
   14014                 :             :  * createForeignKeyCheckTriggers
   14015                 :             :  *              Create the referencing-side "check" triggers that implement a foreign
   14016                 :             :  *              key.
   14017                 :             :  *
   14018                 :             :  * Returns the OIDs of the so created triggers in *insertTrigOid and
   14019                 :             :  * *updateTrigOid.
   14020                 :             :  */
   14021                 :             : static void
   14022                 :         362 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
   14023                 :             :                                                           Constraint *fkconstraint, Oid constraintOid,
   14024                 :             :                                                           Oid indexOid,
   14025                 :             :                                                           Oid parentInsTrigger, Oid parentUpdTrigger,
   14026                 :             :                                                           Oid *insertTrigOid, Oid *updateTrigOid)
   14027                 :             : {
   14028                 :         724 :         *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   14029                 :         362 :                                                                                   constraintOid, indexOid,
   14030                 :         362 :                                                                                   parentInsTrigger, true);
   14031                 :         724 :         *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   14032                 :         362 :                                                                                   constraintOid, indexOid,
   14033                 :         362 :                                                                                   parentUpdTrigger, false);
   14034                 :         362 : }
   14035                 :             : 
   14036                 :             : /*
   14037                 :             :  * ALTER TABLE DROP CONSTRAINT
   14038                 :             :  *
   14039                 :             :  * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
   14040                 :             :  */
   14041                 :             : static void
   14042                 :         131 : ATExecDropConstraint(Relation rel, const char *constrName,
   14043                 :             :                                          DropBehavior behavior, bool recurse,
   14044                 :             :                                          bool missing_ok, LOCKMODE lockmode)
   14045                 :             : {
   14046                 :         131 :         Relation        conrel;
   14047                 :         131 :         SysScanDesc scan;
   14048                 :         131 :         ScanKeyData skey[3];
   14049                 :         131 :         HeapTuple       tuple;
   14050                 :         131 :         bool            found = false;
   14051                 :             : 
   14052                 :         131 :         conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   14053                 :             : 
   14054                 :             :         /*
   14055                 :             :          * Find and drop the target constraint
   14056                 :             :          */
   14057                 :         262 :         ScanKeyInit(&skey[0],
   14058                 :             :                                 Anum_pg_constraint_conrelid,
   14059                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   14060                 :         131 :                                 ObjectIdGetDatum(RelationGetRelid(rel)));
   14061                 :         262 :         ScanKeyInit(&skey[1],
   14062                 :             :                                 Anum_pg_constraint_contypid,
   14063                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   14064                 :         131 :                                 ObjectIdGetDatum(InvalidOid));
   14065                 :         262 :         ScanKeyInit(&skey[2],
   14066                 :             :                                 Anum_pg_constraint_conname,
   14067                 :             :                                 BTEqualStrategyNumber, F_NAMEEQ,
   14068                 :         131 :                                 CStringGetDatum(constrName));
   14069                 :         262 :         scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   14070                 :         131 :                                                           true, NULL, 3, skey);
   14071                 :             : 
   14072                 :             :         /* There can be at most one matching row */
   14073         [ +  + ]:         131 :         if (HeapTupleIsValid(tuple = systable_getnext(scan)))
   14074                 :             :         {
   14075                 :         250 :                 dropconstraint_internal(rel, tuple, behavior, recurse, false,
   14076                 :         125 :                                                                 missing_ok, lockmode);
   14077                 :         125 :                 found = true;
   14078                 :         125 :         }
   14079                 :             : 
   14080                 :         131 :         systable_endscan(scan);
   14081                 :             : 
   14082         [ +  + ]:         131 :         if (!found)
   14083                 :             :         {
   14084         [ +  + ]:           6 :                 if (!missing_ok)
   14085   [ +  -  +  - ]:           4 :                         ereport(ERROR,
   14086                 :             :                                         errcode(ERRCODE_UNDEFINED_OBJECT),
   14087                 :             :                                         errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   14088                 :             :                                                    constrName, RelationGetRelationName(rel)));
   14089                 :             :                 else
   14090   [ -  +  +  - ]:           2 :                         ereport(NOTICE,
   14091                 :             :                                         errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
   14092                 :             :                                                    constrName, RelationGetRelationName(rel)));
   14093                 :           2 :         }
   14094                 :             : 
   14095                 :         127 :         table_close(conrel, RowExclusiveLock);
   14096                 :         127 : }
   14097                 :             : 
   14098                 :             : /*
   14099                 :             :  * Remove a constraint, using its pg_constraint tuple
   14100                 :             :  *
   14101                 :             :  * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
   14102                 :             :  * DROP NOT NULL.
   14103                 :             :  *
   14104                 :             :  * Returns the address of the constraint being removed.
   14105                 :             :  */
   14106                 :             : static ObjectAddress
   14107                 :         186 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
   14108                 :             :                                                 bool recurse, bool recursing, bool missing_ok,
   14109                 :             :                                                 LOCKMODE lockmode)
   14110                 :             : {
   14111                 :         186 :         Relation        conrel;
   14112                 :         186 :         Form_pg_constraint con;
   14113                 :             :         ObjectAddress conobj;
   14114                 :         186 :         List       *children;
   14115                 :         186 :         bool            is_no_inherit_constraint = false;
   14116                 :         186 :         char       *constrName;
   14117                 :         186 :         char       *colname = NULL;
   14118                 :             : 
   14119                 :             :         /* Guard against stack overflow due to overly deep inheritance tree. */
   14120                 :         186 :         check_stack_depth();
   14121                 :             : 
   14122                 :             :         /* At top level, permission check was done in ATPrepCmd, else do it */
   14123         [ +  + ]:         186 :         if (recursing)
   14124                 :          34 :                 ATSimplePermissions(AT_DropConstraint, rel,
   14125                 :             :                                                         ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   14126                 :             : 
   14127                 :         186 :         conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   14128                 :             : 
   14129                 :         186 :         con = (Form_pg_constraint) GETSTRUCT(constraintTup);
   14130                 :         186 :         constrName = NameStr(con->conname);
   14131                 :             : 
   14132                 :             :         /* Don't allow drop of inherited constraints */
   14133   [ +  +  +  + ]:         186 :         if (con->coninhcount > 0 && !recursing)
   14134   [ +  -  +  - ]:          26 :                 ereport(ERROR,
   14135                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14136                 :             :                                  errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
   14137                 :             :                                                 constrName, RelationGetRelationName(rel))));
   14138                 :             : 
   14139                 :             :         /*
   14140                 :             :          * Reset pg_constraint.attnotnull, if this is a not-null constraint.
   14141                 :             :          *
   14142                 :             :          * While doing that, we're in a good position to disallow dropping a not-
   14143                 :             :          * null constraint underneath a primary key, a replica identity index, or
   14144                 :             :          * a generated identity column.
   14145                 :             :          */
   14146         [ +  + ]:         160 :         if (con->contype == CONSTRAINT_NOTNULL)
   14147                 :             :         {
   14148                 :          50 :                 Relation        attrel = table_open(AttributeRelationId, RowExclusiveLock);
   14149                 :          50 :                 AttrNumber      attnum = extractNotNullColumn(constraintTup);
   14150                 :          50 :                 Bitmapset  *pkattrs;
   14151                 :          50 :                 Bitmapset  *irattrs;
   14152                 :          50 :                 HeapTuple       atttup;
   14153                 :          50 :                 Form_pg_attribute attForm;
   14154                 :             : 
   14155                 :             :                 /* save column name for recursion step */
   14156                 :          50 :                 colname = get_attname(RelationGetRelid(rel), attnum, false);
   14157                 :             : 
   14158                 :             :                 /*
   14159                 :             :                  * Disallow if it's in the primary key.  For partitioned tables we
   14160                 :             :                  * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
   14161                 :             :                  * return NULL if the primary key is invalid; but we still need to
   14162                 :             :                  * protect not-null constraints under such a constraint, so check the
   14163                 :             :                  * slow way.
   14164                 :             :                  */
   14165                 :          50 :                 pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
   14166                 :             : 
   14167   [ +  +  +  + ]:          50 :                 if (pkattrs == NULL &&
   14168                 :          44 :                         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   14169                 :             :                 {
   14170                 :           3 :                         Oid                     pkindex = RelationGetPrimaryKeyIndex(rel, true);
   14171                 :             : 
   14172         [ +  - ]:           3 :                         if (OidIsValid(pkindex))
   14173                 :             :                         {
   14174                 :           0 :                                 Relation        pk = relation_open(pkindex, AccessShareLock);
   14175                 :             : 
   14176                 :           0 :                                 pkattrs = NULL;
   14177         [ #  # ]:           0 :                                 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
   14178                 :           0 :                                         pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
   14179                 :             : 
   14180                 :           0 :                                 relation_close(pk, AccessShareLock);
   14181                 :           0 :                         }
   14182                 :           3 :                 }
   14183                 :             : 
   14184   [ +  +  +  + ]:          50 :                 if (pkattrs &&
   14185                 :           6 :                         bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
   14186   [ +  -  +  - ]:           4 :                         ereport(ERROR,
   14187                 :             :                                         errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14188                 :             :                                         errmsg("column \"%s\" is in a primary key",
   14189                 :             :                                                    get_attname(RelationGetRelid(rel), attnum, false)));
   14190                 :             : 
   14191                 :             :                 /* Disallow if it's in the replica identity */
   14192                 :          46 :                 irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
   14193         [ +  + ]:          46 :                 if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
   14194   [ +  -  +  - ]:           2 :                         ereport(ERROR,
   14195                 :             :                                         errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14196                 :             :                                         errmsg("column \"%s\" is in index used as replica identity",
   14197                 :             :                                                    get_attname(RelationGetRelid(rel), attnum, false)));
   14198                 :             : 
   14199                 :             :                 /* Disallow if it's a GENERATED AS IDENTITY column */
   14200                 :          44 :                 atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
   14201         [ +  - ]:          44 :                 if (!HeapTupleIsValid(atttup))
   14202   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for attribute %d of relation %u",
   14203                 :             :                                  attnum, RelationGetRelid(rel));
   14204                 :          44 :                 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
   14205         [ +  - ]:          44 :                 if (attForm->attidentity != '\0')
   14206   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   14207                 :             :                                         errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   14208                 :             :                                         errmsg("column \"%s\" of relation \"%s\" is an identity column",
   14209                 :             :                                                    get_attname(RelationGetRelid(rel), attnum,
   14210                 :             :                                                                            false),
   14211                 :             :                                                    RelationGetRelationName(rel)));
   14212                 :             : 
   14213                 :             :                 /* All good -- reset attnotnull if needed */
   14214         [ -  + ]:          44 :                 if (attForm->attnotnull)
   14215                 :             :                 {
   14216                 :          44 :                         attForm->attnotnull = false;
   14217                 :          44 :                         CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
   14218                 :          44 :                 }
   14219                 :             : 
   14220                 :          44 :                 table_close(attrel, RowExclusiveLock);
   14221                 :          44 :         }
   14222                 :             : 
   14223                 :         154 :         is_no_inherit_constraint = con->connoinherit;
   14224                 :             : 
   14225                 :             :         /*
   14226                 :             :          * If it's a foreign-key constraint, we'd better lock the referenced table
   14227                 :             :          * and check that that's not in use, just as we've already done for the
   14228                 :             :          * constrained table (else we might, eg, be dropping a trigger that has
   14229                 :             :          * unfired events).  But we can/must skip that in the self-referential
   14230                 :             :          * case.
   14231                 :             :          */
   14232   [ +  +  -  + ]:         154 :         if (con->contype == CONSTRAINT_FOREIGN &&
   14233                 :          28 :                 con->confrelid != RelationGetRelid(rel))
   14234                 :             :         {
   14235                 :          28 :                 Relation        frel;
   14236                 :             : 
   14237                 :             :                 /* Must match lock taken by RemoveTriggerById: */
   14238                 :          28 :                 frel = table_open(con->confrelid, AccessExclusiveLock);
   14239                 :          28 :                 CheckAlterTableIsSafe(frel);
   14240                 :          28 :                 table_close(frel, NoLock);
   14241                 :          28 :         }
   14242                 :             : 
   14243                 :             :         /*
   14244                 :             :          * Perform the actual constraint deletion
   14245                 :             :          */
   14246                 :         154 :         ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
   14247                 :         154 :         performDeletion(&conobj, behavior, 0);
   14248                 :             : 
   14249                 :             :         /*
   14250                 :             :          * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
   14251                 :             :          * are dropped via the dependency mechanism, so we're done here.
   14252                 :             :          */
   14253         [ +  + ]:         154 :         if (con->contype != CONSTRAINT_CHECK &&
   14254   [ +  +  +  + ]:         101 :                 con->contype != CONSTRAINT_NOTNULL &&
   14255                 :          57 :                 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   14256                 :             :         {
   14257                 :          13 :                 table_close(conrel, RowExclusiveLock);
   14258                 :          13 :                 return conobj;
   14259                 :             :         }
   14260                 :             : 
   14261                 :             :         /*
   14262                 :             :          * Propagate to children as appropriate.  Unlike most other ALTER
   14263                 :             :          * routines, we have to do this one level of recursion at a time; we can't
   14264                 :             :          * use find_all_inheritors to do it in one pass.
   14265                 :             :          */
   14266         [ +  + ]:         141 :         if (!is_no_inherit_constraint)
   14267                 :          95 :                 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
   14268                 :             :         else
   14269                 :          46 :                 children = NIL;
   14270                 :             : 
   14271   [ +  +  +  +  :         344 :         foreach_oid(childrelid, children)
             +  +  +  + ]
   14272                 :             :         {
   14273                 :          63 :                 Relation        childrel;
   14274                 :          63 :                 HeapTuple       tuple;
   14275                 :          63 :                 Form_pg_constraint childcon;
   14276                 :             : 
   14277                 :             :                 /* find_inheritance_children already got lock */
   14278                 :          63 :                 childrel = table_open(childrelid, NoLock);
   14279                 :          63 :                 CheckAlterTableIsSafe(childrel);
   14280                 :             : 
   14281                 :             :                 /*
   14282                 :             :                  * We search for not-null constraints by column name, and others by
   14283                 :             :                  * constraint name.
   14284                 :             :                  */
   14285         [ +  + ]:          63 :                 if (con->contype == CONSTRAINT_NOTNULL)
   14286                 :             :                 {
   14287                 :          24 :                         tuple = findNotNullConstraint(childrelid, colname);
   14288         [ +  - ]:          24 :                         if (!HeapTupleIsValid(tuple))
   14289   [ #  #  #  # ]:           0 :                                 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
   14290                 :             :                                          colname, RelationGetRelid(childrel));
   14291                 :          24 :                 }
   14292                 :             :                 else
   14293                 :             :                 {
   14294                 :          39 :                         SysScanDesc scan;
   14295                 :          39 :                         ScanKeyData skey[3];
   14296                 :             : 
   14297                 :          78 :                         ScanKeyInit(&skey[0],
   14298                 :             :                                                 Anum_pg_constraint_conrelid,
   14299                 :             :                                                 BTEqualStrategyNumber, F_OIDEQ,
   14300                 :          39 :                                                 ObjectIdGetDatum(childrelid));
   14301                 :          78 :                         ScanKeyInit(&skey[1],
   14302                 :             :                                                 Anum_pg_constraint_contypid,
   14303                 :             :                                                 BTEqualStrategyNumber, F_OIDEQ,
   14304                 :          39 :                                                 ObjectIdGetDatum(InvalidOid));
   14305                 :          78 :                         ScanKeyInit(&skey[2],
   14306                 :             :                                                 Anum_pg_constraint_conname,
   14307                 :             :                                                 BTEqualStrategyNumber, F_NAMEEQ,
   14308                 :          39 :                                                 CStringGetDatum(constrName));
   14309                 :          78 :                         scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   14310                 :          39 :                                                                           true, NULL, 3, skey);
   14311                 :             :                         /* There can only be one, so no need to loop */
   14312                 :          39 :                         tuple = systable_getnext(scan);
   14313         [ +  - ]:          39 :                         if (!HeapTupleIsValid(tuple))
   14314   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   14315                 :             :                                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   14316                 :             :                                                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   14317                 :             :                                                                 constrName,
   14318                 :             :                                                                 RelationGetRelationName(childrel))));
   14319                 :          39 :                         tuple = heap_copytuple(tuple);
   14320                 :          39 :                         systable_endscan(scan);
   14321                 :          39 :                 }
   14322                 :             : 
   14323                 :          63 :                 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
   14324                 :             : 
   14325                 :             :                 /* Right now only CHECK and not-null constraints can be inherited */
   14326   [ +  +  +  - ]:          63 :                 if (childcon->contype != CONSTRAINT_CHECK &&
   14327                 :          24 :                         childcon->contype != CONSTRAINT_NOTNULL)
   14328   [ #  #  #  # ]:           0 :                         elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
   14329                 :             : 
   14330         [ +  - ]:          63 :                 if (childcon->coninhcount <= 0) /* shouldn't happen */
   14331   [ #  #  #  # ]:           0 :                         elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   14332                 :             :                                  childrelid, NameStr(childcon->conname));
   14333                 :             : 
   14334         [ +  + ]:          63 :                 if (recurse)
   14335                 :             :                 {
   14336                 :             :                         /*
   14337                 :             :                          * If the child constraint has other definition sources, just
   14338                 :             :                          * decrement its inheritance count; if not, recurse to delete it.
   14339                 :             :                          */
   14340   [ +  +  +  + ]:          46 :                         if (childcon->coninhcount == 1 && !childcon->conislocal)
   14341                 :             :                         {
   14342                 :             :                                 /* Time to delete this child constraint, too */
   14343                 :          68 :                                 dropconstraint_internal(childrel, tuple, behavior,
   14344                 :          34 :                                                                                 recurse, true, missing_ok,
   14345                 :          34 :                                                                                 lockmode);
   14346                 :          34 :                         }
   14347                 :             :                         else
   14348                 :             :                         {
   14349                 :             :                                 /* Child constraint must survive my deletion */
   14350                 :          12 :                                 childcon->coninhcount--;
   14351                 :          12 :                                 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
   14352                 :             : 
   14353                 :             :                                 /* Make update visible */
   14354                 :          12 :                                 CommandCounterIncrement();
   14355                 :             :                         }
   14356                 :          46 :                 }
   14357                 :             :                 else
   14358                 :             :                 {
   14359                 :             :                         /*
   14360                 :             :                          * If we were told to drop ONLY in this table (no recursion) and
   14361                 :             :                          * there are no further parents for this constraint, we need to
   14362                 :             :                          * mark the inheritors' constraints as locally defined rather than
   14363                 :             :                          * inherited.
   14364                 :             :                          */
   14365                 :          17 :                         childcon->coninhcount--;
   14366         [ -  + ]:          17 :                         if (childcon->coninhcount == 0)
   14367                 :          17 :                                 childcon->conislocal = true;
   14368                 :             : 
   14369                 :          17 :                         CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
   14370                 :             : 
   14371                 :             :                         /* Make update visible */
   14372                 :          17 :                         CommandCounterIncrement();
   14373                 :             :                 }
   14374                 :             : 
   14375                 :          63 :                 heap_freetuple(tuple);
   14376                 :             : 
   14377                 :          63 :                 table_close(childrel, NoLock);
   14378                 :         203 :         }
   14379                 :             : 
   14380                 :         141 :         table_close(conrel, RowExclusiveLock);
   14381                 :             : 
   14382                 :         141 :         return conobj;
   14383                 :         154 : }
   14384                 :             : 
   14385                 :             : /*
   14386                 :             :  * ALTER COLUMN TYPE
   14387                 :             :  *
   14388                 :             :  * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
   14389                 :             :  * TYPE during phase 1 --- the AlterTableCmd passed in here is already
   14390                 :             :  * transformed (and must be, because we rely on some transformed fields).
   14391                 :             :  *
   14392                 :             :  * The point of this is that the execution of all ALTER COLUMN TYPEs for a
   14393                 :             :  * table will be done "in parallel" during phase 3, so all the USING
   14394                 :             :  * expressions should be parsed assuming the original column types.  Also,
   14395                 :             :  * this allows a USING expression to refer to a field that will be dropped.
   14396                 :             :  *
   14397                 :             :  * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
   14398                 :             :  * the first two execution steps in phase 2; they must not see the effects
   14399                 :             :  * of any other subcommand types, since the USING expressions are parsed
   14400                 :             :  * against the unmodified table's state.
   14401                 :             :  */
   14402                 :             : static void
   14403                 :         215 : ATPrepAlterColumnType(List **wqueue,
   14404                 :             :                                           AlteredTableInfo *tab, Relation rel,
   14405                 :             :                                           bool recurse, bool recursing,
   14406                 :             :                                           AlterTableCmd *cmd, LOCKMODE lockmode,
   14407                 :             :                                           AlterTableUtilityContext *context)
   14408                 :             : {
   14409                 :         215 :         char       *colName = cmd->name;
   14410                 :         215 :         ColumnDef  *def = (ColumnDef *) cmd->def;
   14411                 :         215 :         TypeName   *typeName = def->typeName;
   14412                 :         215 :         Node       *transform = def->cooked_default;
   14413                 :         215 :         HeapTuple       tuple;
   14414                 :         215 :         Form_pg_attribute attTup;
   14415                 :         215 :         AttrNumber      attnum;
   14416                 :         215 :         Oid                     targettype;
   14417                 :         215 :         int32           targettypmod;
   14418                 :         215 :         Oid                     targetcollid;
   14419                 :         215 :         NewColumnValue *newval;
   14420                 :         215 :         ParseState *pstate = make_parsestate(NULL);
   14421                 :         215 :         AclResult       aclresult;
   14422                 :         215 :         bool            is_expr;
   14423                 :             : 
   14424                 :         215 :         pstate->p_sourcetext = context->queryString;
   14425                 :             : 
   14426   [ +  +  +  + ]:         215 :         if (rel->rd_rel->reloftype && !recursing)
   14427   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   14428                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14429                 :             :                                  errmsg("cannot alter column type of typed table"),
   14430                 :             :                                  parser_errposition(pstate, def->location)));
   14431                 :             : 
   14432                 :             :         /* lookup the attribute so we can check inheritance status */
   14433                 :         214 :         tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   14434         [ +  - ]:         214 :         if (!HeapTupleIsValid(tuple))
   14435   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   14436                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   14437                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   14438                 :             :                                                 colName, RelationGetRelationName(rel)),
   14439                 :             :                                  parser_errposition(pstate, def->location)));
   14440                 :         214 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
   14441                 :         214 :         attnum = attTup->attnum;
   14442                 :             : 
   14443                 :             :         /* Can't alter a system attribute */
   14444         [ +  + ]:         214 :         if (attnum <= 0)
   14445   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   14446                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14447                 :             :                                  errmsg("cannot alter system column \"%s\"", colName),
   14448                 :             :                                  parser_errposition(pstate, def->location)));
   14449                 :             : 
   14450                 :             :         /*
   14451                 :             :          * Cannot specify USING when altering type of a generated column, because
   14452                 :             :          * that would violate the generation expression.
   14453                 :             :          */
   14454   [ +  +  +  + ]:         213 :         if (attTup->attgenerated && def->cooked_default)
   14455   [ +  -  +  - ]:           2 :                 ereport(ERROR,
   14456                 :             :                                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
   14457                 :             :                                  errmsg("cannot specify USING when altering type of generated column"),
   14458                 :             :                                  errdetail("Column \"%s\" is a generated column.", colName),
   14459                 :             :                                  parser_errposition(pstate, def->location)));
   14460                 :             : 
   14461                 :             :         /*
   14462                 :             :          * Don't alter inherited columns.  At outer level, there had better not be
   14463                 :             :          * any inherited definition; when recursing, we assume this was checked at
   14464                 :             :          * the parent level (see below).
   14465                 :             :          */
   14466   [ +  +  +  + ]:         211 :         if (attTup->attinhcount > 0 && !recursing)
   14467   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   14468                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14469                 :             :                                  errmsg("cannot alter inherited column \"%s\"", colName),
   14470                 :             :                                  parser_errposition(pstate, def->location)));
   14471                 :             : 
   14472                 :             :         /* Don't alter columns used in the partition key */
   14473   [ +  +  +  + ]:         420 :         if (has_partition_attrs(rel,
   14474                 :         210 :                                                         bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
   14475                 :             :                                                         &is_expr))
   14476   [ +  -  +  - ]:           3 :                 ereport(ERROR,
   14477                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14478                 :             :                                  errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
   14479                 :             :                                                 colName, RelationGetRelationName(rel)),
   14480                 :             :                                  parser_errposition(pstate, def->location)));
   14481                 :             : 
   14482                 :             :         /* Look up the target type */
   14483                 :         207 :         typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
   14484                 :             : 
   14485                 :         207 :         aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
   14486         [ +  + ]:         207 :         if (aclresult != ACLCHECK_OK)
   14487                 :           2 :                 aclcheck_error_type(aclresult, targettype);
   14488                 :             : 
   14489                 :             :         /* And the collation */
   14490                 :         207 :         targetcollid = GetColumnDefCollation(pstate, def, targettype);
   14491                 :             : 
   14492                 :             :         /* make sure datatype is legal for a column */
   14493                 :         414 :         CheckAttributeType(colName, targettype, targetcollid,
   14494                 :         207 :                                            list_make1_oid(rel->rd_rel->reltype),
   14495                 :         207 :                                            (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
   14496                 :             : 
   14497         [ +  + ]:         207 :         if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
   14498                 :             :         {
   14499                 :             :                 /* do nothing */
   14500                 :           4 :         }
   14501   [ +  +  +  + ]:         203 :         else if (tab->relkind == RELKIND_RELATION ||
   14502                 :          32 :                          tab->relkind == RELKIND_PARTITIONED_TABLE)
   14503                 :             :         {
   14504                 :             :                 /*
   14505                 :             :                  * Set up an expression to transform the old data value to the new
   14506                 :             :                  * type. If a USING option was given, use the expression as
   14507                 :             :                  * transformed by transformAlterTableStmt, else just take the old
   14508                 :             :                  * value and try to coerce it.  We do this first so that type
   14509                 :             :                  * incompatibility can be detected before we waste effort, and because
   14510                 :             :                  * we need the expression to be parsed against the original table row
   14511                 :             :                  * type.
   14512                 :             :                  */
   14513         [ +  + ]:         182 :                 if (!transform)
   14514                 :             :                 {
   14515                 :         290 :                         transform = (Node *) makeVar(1, attnum,
   14516                 :         145 :                                                                                  attTup->atttypid, attTup->atttypmod,
   14517                 :         145 :                                                                                  attTup->attcollation,
   14518                 :             :                                                                                  0);
   14519                 :         145 :                 }
   14520                 :             : 
   14521                 :         364 :                 transform = coerce_to_target_type(pstate,
   14522                 :         182 :                                                                                   transform, exprType(transform),
   14523                 :         182 :                                                                                   targettype, targettypmod,
   14524                 :             :                                                                                   COERCION_ASSIGNMENT,
   14525                 :             :                                                                                   COERCE_IMPLICIT_CAST,
   14526                 :             :                                                                                   -1);
   14527         [ +  + ]:         182 :                 if (transform == NULL)
   14528                 :             :                 {
   14529                 :             :                         /* error text depends on whether USING was specified or not */
   14530         [ +  + ]:           3 :                         if (def->cooked_default != NULL)
   14531   [ +  -  +  - ]:           1 :                                 ereport(ERROR,
   14532                 :             :                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
   14533                 :             :                                                  errmsg("result of USING clause for column \"%s\""
   14534                 :             :                                                                 " cannot be cast automatically to type %s",
   14535                 :             :                                                                 colName, format_type_be(targettype)),
   14536                 :             :                                                  errhint("You might need to add an explicit cast.")));
   14537                 :             :                         else
   14538   [ +  -  +  -  :           2 :                                 ereport(ERROR,
                   -  + ]
   14539                 :             :                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
   14540                 :             :                                                  errmsg("column \"%s\" cannot be cast automatically to type %s",
   14541                 :             :                                                                 colName, format_type_be(targettype)),
   14542                 :             :                                                  !attTup->attgenerated ?
   14543                 :             :                                 /* translator: USING is SQL, don't translate it */
   14544                 :             :                                                  errhint("You might need to specify \"USING %s::%s\".",
   14545                 :             :                                                                  quote_identifier(colName),
   14546                 :             :                                                                  format_type_with_typemod(targettype,
   14547                 :             :                                                                                                                   targettypmod)) : 0));
   14548                 :           0 :                 }
   14549                 :             : 
   14550                 :             :                 /* Fix collations after all else */
   14551                 :         179 :                 assign_expr_collations(pstate, transform);
   14552                 :             : 
   14553                 :             :                 /* Expand virtual generated columns in the expr. */
   14554                 :         179 :                 transform = expand_generated_columns_in_expr(transform, rel, 1);
   14555                 :             : 
   14556                 :             :                 /* Plan the expr now so we can accurately assess the need to rewrite. */
   14557                 :         179 :                 transform = (Node *) expression_planner((Expr *) transform);
   14558                 :             : 
   14559                 :             :                 /*
   14560                 :             :                  * Add a work queue item to make ATRewriteTable update the column
   14561                 :             :                  * contents.
   14562                 :             :                  */
   14563                 :         179 :                 newval = palloc0_object(NewColumnValue);
   14564                 :         179 :                 newval->attnum = attnum;
   14565                 :         179 :                 newval->expr = (Expr *) transform;
   14566                 :         179 :                 newval->is_generated = false;
   14567                 :             : 
   14568                 :         179 :                 tab->newvals = lappend(tab->newvals, newval);
   14569         [ +  + ]:         179 :                 if (ATColumnChangeRequiresRewrite(transform, attnum))
   14570                 :         148 :                         tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
   14571                 :         179 :         }
   14572         [ +  + ]:          21 :         else if (transform)
   14573   [ +  -  +  - ]:           2 :                 ereport(ERROR,
   14574                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14575                 :             :                                  errmsg("\"%s\" is not a table",
   14576                 :             :                                                 RelationGetRelationName(rel))));
   14577                 :             : 
   14578   [ +  +  +  -  :         202 :         if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
          +  -  +  -  +  
                      + ]
   14579                 :             :         {
   14580                 :             :                 /*
   14581                 :             :                  * For relations or columns without storage, do this check now.
   14582                 :             :                  * Regular tables will check it later when the table is being
   14583                 :             :                  * rewritten.
   14584                 :             :                  */
   14585                 :          36 :                 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
   14586                 :          36 :         }
   14587                 :             : 
   14588                 :         194 :         ReleaseSysCache(tuple);
   14589                 :             : 
   14590                 :             :         /*
   14591                 :             :          * Recurse manually by queueing a new command for each child, if
   14592                 :             :          * necessary. We cannot apply ATSimpleRecursion here because we need to
   14593                 :             :          * remap attribute numbers in the USING expression, if any.
   14594                 :             :          *
   14595                 :             :          * If we are told not to recurse, there had better not be any child
   14596                 :             :          * tables; else the alter would put them out of step.
   14597                 :             :          */
   14598         [ +  + ]:         194 :         if (recurse)
   14599                 :             :         {
   14600                 :         152 :                 Oid                     relid = RelationGetRelid(rel);
   14601                 :         152 :                 List       *child_oids,
   14602                 :             :                                    *child_numparents;
   14603                 :         152 :                 ListCell   *lo,
   14604                 :             :                                    *li;
   14605                 :             : 
   14606                 :         152 :                 child_oids = find_all_inheritors(relid, lockmode,
   14607                 :             :                                                                                  &child_numparents);
   14608                 :             : 
   14609                 :             :                 /*
   14610                 :             :                  * find_all_inheritors does the recursive search of the inheritance
   14611                 :             :                  * hierarchy, so all we have to do is process all of the relids in the
   14612                 :             :                  * list that it returns.
   14613                 :             :                  */
   14614   [ +  -  +  +  :         342 :                 forboth(lo, child_oids, li, child_numparents)
          +  -  +  +  +  
                +  +  + ]
   14615                 :             :                 {
   14616                 :         192 :                         Oid                     childrelid = lfirst_oid(lo);
   14617                 :         192 :                         int                     numparents = lfirst_int(li);
   14618                 :         192 :                         Relation        childrel;
   14619                 :         192 :                         HeapTuple       childtuple;
   14620                 :         192 :                         Form_pg_attribute childattTup;
   14621                 :             : 
   14622         [ +  + ]:         192 :                         if (childrelid == relid)
   14623                 :         154 :                                 continue;
   14624                 :             : 
   14625                 :             :                         /* find_all_inheritors already got lock */
   14626                 :          38 :                         childrel = relation_open(childrelid, NoLock);
   14627                 :          38 :                         CheckAlterTableIsSafe(childrel);
   14628                 :             : 
   14629                 :             :                         /*
   14630                 :             :                          * Verify that the child doesn't have any inherited definitions of
   14631                 :             :                          * this column that came from outside this inheritance hierarchy.
   14632                 :             :                          * (renameatt makes a similar test, though in a different way
   14633                 :             :                          * because of its different recursion mechanism.)
   14634                 :             :                          */
   14635                 :          76 :                         childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
   14636                 :          38 :                                                                                            colName);
   14637         [ +  - ]:          38 :                         if (!HeapTupleIsValid(childtuple))
   14638   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   14639                 :             :                                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   14640                 :             :                                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   14641                 :             :                                                                 colName, RelationGetRelationName(childrel))));
   14642                 :          38 :                         childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
   14643                 :             : 
   14644         [ +  + ]:          38 :                         if (childattTup->attinhcount > numparents)
   14645   [ +  -  +  - ]:           1 :                                 ereport(ERROR,
   14646                 :             :                                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14647                 :             :                                                  errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
   14648                 :             :                                                                 colName, RelationGetRelationName(childrel))));
   14649                 :             : 
   14650                 :          37 :                         ReleaseSysCache(childtuple);
   14651                 :             : 
   14652                 :             :                         /*
   14653                 :             :                          * Remap the attribute numbers.  If no USING expression was
   14654                 :             :                          * specified, there is no need for this step.
   14655                 :             :                          */
   14656         [ +  + ]:          37 :                         if (def->cooked_default)
   14657                 :             :                         {
   14658                 :          13 :                                 AttrMap    *attmap;
   14659                 :          13 :                                 bool            found_whole_row;
   14660                 :             : 
   14661                 :             :                                 /* create a copy to scribble on */
   14662                 :          13 :                                 cmd = copyObject(cmd);
   14663                 :             : 
   14664                 :          26 :                                 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
   14665                 :          13 :                                                                                            RelationGetDescr(rel),
   14666                 :             :                                                                                            false);
   14667                 :          13 :                                 ((ColumnDef *) cmd->def)->cooked_default =
   14668                 :          26 :                                         map_variable_attnos(def->cooked_default,
   14669                 :             :                                                                                 1, 0,
   14670                 :          13 :                                                                                 attmap,
   14671                 :             :                                                                                 InvalidOid, &found_whole_row);
   14672         [ +  + ]:          13 :                                 if (found_whole_row)
   14673   [ +  -  +  - ]:           1 :                                         ereport(ERROR,
   14674                 :             :                                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14675                 :             :                                                          errmsg("cannot convert whole-row table reference"),
   14676                 :             :                                                          errdetail("USING expression contains a whole-row table reference.")));
   14677                 :          12 :                                 pfree(attmap);
   14678                 :          12 :                         }
   14679                 :          36 :                         ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
   14680                 :          36 :                         relation_close(childrel, NoLock);
   14681      [ -  +  + ]:         190 :                 }
   14682                 :         150 :         }
   14683   [ +  +  +  - ]:          42 :         else if (!recursing &&
   14684                 :           8 :                          find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
   14685   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   14686                 :             :                                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14687                 :             :                                  errmsg("type of inherited column \"%s\" must be changed in child tables too",
   14688                 :             :                                                 colName)));
   14689                 :             : 
   14690         [ +  + ]:         192 :         if (tab->relkind == RELKIND_COMPOSITE_TYPE)
   14691                 :           8 :                 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
   14692                 :         192 : }
   14693                 :             : 
   14694                 :             : /*
   14695                 :             :  * When the data type of a column is changed, a rewrite might not be required
   14696                 :             :  * if the new type is sufficiently identical to the old one, and the USING
   14697                 :             :  * clause isn't trying to insert some other value.  It's safe to skip the
   14698                 :             :  * rewrite in these cases:
   14699                 :             :  *
   14700                 :             :  * - the old type is binary coercible to the new type
   14701                 :             :  * - the new type is an unconstrained domain over the old type
   14702                 :             :  * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
   14703                 :             :  *
   14704                 :             :  * In the case of a constrained domain, we could get by with scanning the
   14705                 :             :  * table and checking the constraint rather than actually rewriting it, but we
   14706                 :             :  * don't currently try to do that.
   14707                 :             :  */
   14708                 :             : static bool
   14709                 :         179 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
   14710                 :             : {
   14711         [ +  - ]:         179 :         Assert(expr != NULL);
   14712                 :             : 
   14713                 :         196 :         for (;;)
   14714                 :             :         {
   14715                 :             :                 /* only one varno, so no need to check that */
   14716   [ +  +  +  - ]:         196 :                 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
   14717                 :          31 :                         return false;
   14718         [ +  + ]:         165 :                 else if (IsA(expr, RelabelType))
   14719                 :          15 :                         expr = (Node *) ((RelabelType *) expr)->arg;
   14720         [ -  + ]:         150 :                 else if (IsA(expr, CoerceToDomain))
   14721                 :             :                 {
   14722                 :           0 :                         CoerceToDomain *d = (CoerceToDomain *) expr;
   14723                 :             : 
   14724         [ #  # ]:           0 :                         if (DomainHasConstraints(d->resulttype))
   14725                 :           0 :                                 return true;
   14726                 :           0 :                         expr = (Node *) d->arg;
   14727         [ #  # ]:           0 :                 }
   14728         [ +  + ]:         150 :                 else if (IsA(expr, FuncExpr))
   14729                 :             :                 {
   14730                 :         120 :                         FuncExpr   *f = (FuncExpr *) expr;
   14731                 :             : 
   14732         [ +  + ]:         120 :                         switch (f->funcid)
   14733                 :             :                         {
   14734                 :             :                                 case F_TIMESTAMPTZ_TIMESTAMP:
   14735                 :             :                                 case F_TIMESTAMP_TIMESTAMPTZ:
   14736         [ +  + ]:           3 :                                         if (TimestampTimestampTzRequiresRewrite())
   14737                 :           1 :                                                 return true;
   14738                 :             :                                         else
   14739                 :           2 :                                                 expr = linitial(f->args);
   14740                 :           2 :                                         break;
   14741                 :             :                                 default:
   14742                 :         117 :                                         return true;
   14743                 :             :                         }
   14744         [ +  + ]:         120 :                 }
   14745                 :             :                 else
   14746                 :          30 :                         return true;
   14747                 :             :         }
   14748                 :         179 : }
   14749                 :             : 
   14750                 :             : /*
   14751                 :             :  * ALTER COLUMN .. SET DATA TYPE
   14752                 :             :  *
   14753                 :             :  * Return the address of the modified column.
   14754                 :             :  */
   14755                 :             : static ObjectAddress
   14756                 :         185 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
   14757                 :             :                                           AlterTableCmd *cmd, LOCKMODE lockmode)
   14758                 :             : {
   14759                 :         185 :         char       *colName = cmd->name;
   14760                 :         185 :         ColumnDef  *def = (ColumnDef *) cmd->def;
   14761                 :         185 :         TypeName   *typeName = def->typeName;
   14762                 :         185 :         HeapTuple       heapTup;
   14763                 :         185 :         Form_pg_attribute attTup,
   14764                 :             :                                 attOldTup;
   14765                 :         185 :         AttrNumber      attnum;
   14766                 :         185 :         HeapTuple       typeTuple;
   14767                 :         185 :         Form_pg_type tform;
   14768                 :         185 :         Oid                     targettype;
   14769                 :         185 :         int32           targettypmod;
   14770                 :         185 :         Oid                     targetcollid;
   14771                 :         185 :         Node       *defaultexpr;
   14772                 :         185 :         Relation        attrelation;
   14773                 :         185 :         Relation        depRel;
   14774                 :         185 :         ScanKeyData key[3];
   14775                 :         185 :         SysScanDesc scan;
   14776                 :         185 :         HeapTuple       depTup;
   14777                 :             :         ObjectAddress address;
   14778                 :             : 
   14779                 :             :         /*
   14780                 :             :          * Clear all the missing values if we're rewriting the table, since this
   14781                 :             :          * renders them pointless.
   14782                 :             :          */
   14783         [ +  + ]:         185 :         if (tab->rewrite)
   14784                 :             :         {
   14785                 :         138 :                 Relation        newrel;
   14786                 :             : 
   14787                 :         138 :                 newrel = table_open(RelationGetRelid(rel), NoLock);
   14788                 :         138 :                 RelationClearMissing(newrel);
   14789                 :         138 :                 relation_close(newrel, NoLock);
   14790                 :             :                 /* make sure we don't conflict with later attribute modifications */
   14791                 :         138 :                 CommandCounterIncrement();
   14792                 :         138 :         }
   14793                 :             : 
   14794                 :         185 :         attrelation = table_open(AttributeRelationId, RowExclusiveLock);
   14795                 :             : 
   14796                 :             :         /* Look up the target column */
   14797                 :         185 :         heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
   14798         [ +  - ]:         185 :         if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
   14799   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   14800                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   14801                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   14802                 :             :                                                 colName, RelationGetRelationName(rel))));
   14803                 :         185 :         attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   14804                 :         185 :         attnum = attTup->attnum;
   14805                 :         185 :         attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
   14806                 :             : 
   14807                 :             :         /* Check for multiple ALTER TYPE on same column --- can't cope */
   14808         [ +  - ]:         185 :         if (attTup->atttypid != attOldTup->atttypid ||
   14809                 :         185 :                 attTup->atttypmod != attOldTup->atttypmod)
   14810   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   14811                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14812                 :             :                                  errmsg("cannot alter type of column \"%s\" twice",
   14813                 :             :                                                 colName)));
   14814                 :             : 
   14815                 :             :         /* Look up the target type (should not fail, since prep found it) */
   14816                 :         185 :         typeTuple = typenameType(NULL, typeName, &targettypmod);
   14817                 :         185 :         tform = (Form_pg_type) GETSTRUCT(typeTuple);
   14818                 :         185 :         targettype = tform->oid;
   14819                 :             :         /* And the collation */
   14820                 :         185 :         targetcollid = GetColumnDefCollation(NULL, def, targettype);
   14821                 :             : 
   14822                 :             :         /*
   14823                 :             :          * If there is a default expression for the column, get it and ensure we
   14824                 :             :          * can coerce it to the new datatype.  (We must do this before changing
   14825                 :             :          * the column type, because build_column_default itself will try to
   14826                 :             :          * coerce, and will not issue the error message we want if it fails.)
   14827                 :             :          *
   14828                 :             :          * We remove any implicit coercion steps at the top level of the old
   14829                 :             :          * default expression; this has been agreed to satisfy the principle of
   14830                 :             :          * least surprise.  (The conversion to the new column type should act like
   14831                 :             :          * it started from what the user sees as the stored expression, and the
   14832                 :             :          * implicit coercions aren't going to be shown.)
   14833                 :             :          */
   14834         [ +  + ]:         185 :         if (attTup->atthasdef)
   14835                 :             :         {
   14836                 :          14 :                 defaultexpr = build_column_default(rel, attnum);
   14837         [ +  - ]:          14 :                 Assert(defaultexpr);
   14838                 :          14 :                 defaultexpr = strip_implicit_coercions(defaultexpr);
   14839                 :          14 :                 defaultexpr = coerce_to_target_type(NULL,       /* no UNKNOWN params */
   14840                 :          14 :                                                                                         defaultexpr, exprType(defaultexpr),
   14841                 :          14 :                                                                                         targettype, targettypmod,
   14842                 :             :                                                                                         COERCION_ASSIGNMENT,
   14843                 :             :                                                                                         COERCE_IMPLICIT_CAST,
   14844                 :             :                                                                                         -1);
   14845         [ +  + ]:          14 :                 if (defaultexpr == NULL)
   14846                 :             :                 {
   14847         [ -  + ]:           1 :                         if (attTup->attgenerated)
   14848   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   14849                 :             :                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
   14850                 :             :                                                  errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
   14851                 :             :                                                                 colName, format_type_be(targettype))));
   14852                 :             :                         else
   14853   [ +  -  +  - ]:           1 :                                 ereport(ERROR,
   14854                 :             :                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
   14855                 :             :                                                  errmsg("default for column \"%s\" cannot be cast automatically to type %s",
   14856                 :             :                                                                 colName, format_type_be(targettype))));
   14857                 :           0 :                 }
   14858                 :          13 :         }
   14859                 :             :         else
   14860                 :         171 :                 defaultexpr = NULL;
   14861                 :             : 
   14862                 :             :         /*
   14863                 :             :          * Find everything that depends on the column (constraints, indexes, etc),
   14864                 :             :          * and record enough information to let us recreate the objects.
   14865                 :             :          *
   14866                 :             :          * The actual recreation does not happen here, but only after we have
   14867                 :             :          * performed all the individual ALTER TYPE operations.  We have to save
   14868                 :             :          * the info before executing ALTER TYPE, though, else the deparser will
   14869                 :             :          * get confused.
   14870                 :             :          */
   14871                 :         184 :         RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
   14872                 :             : 
   14873                 :             :         /*
   14874                 :             :          * Now scan for dependencies of this column on other things.  The only
   14875                 :             :          * things we should find are the dependency on the column datatype and
   14876                 :             :          * possibly a collation dependency.  Those can be removed.
   14877                 :             :          */
   14878                 :         184 :         depRel = table_open(DependRelationId, RowExclusiveLock);
   14879                 :             : 
   14880                 :         368 :         ScanKeyInit(&key[0],
   14881                 :             :                                 Anum_pg_depend_classid,
   14882                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   14883                 :         184 :                                 ObjectIdGetDatum(RelationRelationId));
   14884                 :         368 :         ScanKeyInit(&key[1],
   14885                 :             :                                 Anum_pg_depend_objid,
   14886                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   14887                 :         184 :                                 ObjectIdGetDatum(RelationGetRelid(rel)));
   14888                 :         368 :         ScanKeyInit(&key[2],
   14889                 :             :                                 Anum_pg_depend_objsubid,
   14890                 :             :                                 BTEqualStrategyNumber, F_INT4EQ,
   14891                 :         184 :                                 Int32GetDatum((int32) attnum));
   14892                 :             : 
   14893                 :         368 :         scan = systable_beginscan(depRel, DependDependerIndexId, true,
   14894                 :         184 :                                                           NULL, 3, key);
   14895                 :             : 
   14896         [ -  + ]:         184 :         while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   14897                 :             :         {
   14898                 :           0 :                 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   14899                 :           0 :                 ObjectAddress foundObject;
   14900                 :             : 
   14901                 :           0 :                 foundObject.classId = foundDep->refclassid;
   14902                 :           0 :                 foundObject.objectId = foundDep->refobjid;
   14903                 :           0 :                 foundObject.objectSubId = foundDep->refobjsubid;
   14904                 :             : 
   14905         [ #  # ]:           0 :                 if (foundDep->deptype != DEPENDENCY_NORMAL)
   14906   [ #  #  #  # ]:           0 :                         elog(ERROR, "found unexpected dependency type '%c'",
   14907                 :             :                                  foundDep->deptype);
   14908         [ #  # ]:           0 :                 if (!(foundDep->refclassid == TypeRelationId &&
   14909         [ #  # ]:           0 :                           foundDep->refobjid == attTup->atttypid) &&
   14910         [ #  # ]:           0 :                         !(foundDep->refclassid == CollationRelationId &&
   14911                 :           0 :                           foundDep->refobjid == attTup->attcollation))
   14912   [ #  #  #  # ]:           0 :                         elog(ERROR, "found unexpected dependency for column: %s",
   14913                 :             :                                  getObjectDescription(&foundObject, false));
   14914                 :             : 
   14915                 :           0 :                 CatalogTupleDelete(depRel, &depTup->t_self);
   14916                 :           0 :         }
   14917                 :             : 
   14918                 :         184 :         systable_endscan(scan);
   14919                 :             : 
   14920                 :         184 :         table_close(depRel, RowExclusiveLock);
   14921                 :             : 
   14922                 :             :         /*
   14923                 :             :          * Here we go --- change the recorded column type and collation.  (Note
   14924                 :             :          * heapTup is a copy of the syscache entry, so okay to scribble on.) First
   14925                 :             :          * fix up the missing value if any.
   14926                 :             :          */
   14927         [ +  + ]:         184 :         if (attTup->atthasmissing)
   14928                 :             :         {
   14929                 :           1 :                 Datum           missingval;
   14930                 :           1 :                 bool            missingNull;
   14931                 :             : 
   14932                 :             :                 /* if rewrite is true the missing value should already be cleared */
   14933         [ +  - ]:           1 :                 Assert(tab->rewrite == 0);
   14934                 :             : 
   14935                 :             :                 /* Get the missing value datum */
   14936                 :           2 :                 missingval = heap_getattr(heapTup,
   14937                 :             :                                                                   Anum_pg_attribute_attmissingval,
   14938                 :           1 :                                                                   attrelation->rd_att,
   14939                 :             :                                                                   &missingNull);
   14940                 :             : 
   14941                 :             :                 /* if it's a null array there is nothing to do */
   14942                 :             : 
   14943         [ -  + ]:           1 :                 if (!missingNull)
   14944                 :             :                 {
   14945                 :             :                         /*
   14946                 :             :                          * Get the datum out of the array and repack it in a new array
   14947                 :             :                          * built with the new type data. We assume that since the table
   14948                 :             :                          * doesn't need rewriting, the actual Datum doesn't need to be
   14949                 :             :                          * changed, only the array metadata.
   14950                 :             :                          */
   14951                 :             : 
   14952                 :           1 :                         int                     one = 1;
   14953                 :           1 :                         bool            isNull;
   14954                 :           1 :                         Datum           valuesAtt[Natts_pg_attribute] = {0};
   14955                 :           1 :                         bool            nullsAtt[Natts_pg_attribute] = {0};
   14956                 :           1 :                         bool            replacesAtt[Natts_pg_attribute] = {0};
   14957                 :           1 :                         HeapTuple       newTup;
   14958                 :             : 
   14959                 :           2 :                         missingval = array_get_element(missingval,
   14960                 :             :                                                                                    1,
   14961                 :             :                                                                                    &one,
   14962                 :             :                                                                                    0,
   14963                 :           1 :                                                                                    attTup->attlen,
   14964                 :           1 :                                                                                    attTup->attbyval,
   14965                 :           1 :                                                                                    attTup->attalign,
   14966                 :             :                                                                                    &isNull);
   14967                 :           1 :                         missingval = PointerGetDatum(construct_array(&missingval,
   14968                 :             :                                                                                                                  1,
   14969                 :           1 :                                                                                                                  targettype,
   14970                 :           1 :                                                                                                                  tform->typlen,
   14971                 :           1 :                                                                                                                  tform->typbyval,
   14972                 :           1 :                                                                                                                  tform->typalign));
   14973                 :             : 
   14974                 :           1 :                         valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
   14975                 :           1 :                         replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
   14976                 :           1 :                         nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
   14977                 :             : 
   14978                 :           2 :                         newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
   14979                 :           1 :                                                                            valuesAtt, nullsAtt, replacesAtt);
   14980                 :           1 :                         heap_freetuple(heapTup);
   14981                 :           1 :                         heapTup = newTup;
   14982                 :           1 :                         attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   14983                 :           1 :                 }
   14984                 :           1 :         }
   14985                 :             : 
   14986                 :         184 :         attTup->atttypid = targettype;
   14987                 :         184 :         attTup->atttypmod = targettypmod;
   14988                 :         184 :         attTup->attcollation = targetcollid;
   14989         [ +  - ]:         184 :         if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
   14990   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   14991                 :             :                                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   14992                 :             :                                 errmsg("too many array dimensions"));
   14993                 :         184 :         attTup->attndims = list_length(typeName->arrayBounds);
   14994                 :         184 :         attTup->attlen = tform->typlen;
   14995                 :         184 :         attTup->attbyval = tform->typbyval;
   14996                 :         184 :         attTup->attalign = tform->typalign;
   14997                 :         184 :         attTup->attstorage = tform->typstorage;
   14998                 :         184 :         attTup->attcompression = InvalidCompressionMethod;
   14999                 :             : 
   15000                 :         184 :         ReleaseSysCache(typeTuple);
   15001                 :             : 
   15002                 :         184 :         CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
   15003                 :             : 
   15004                 :         184 :         table_close(attrelation, RowExclusiveLock);
   15005                 :             : 
   15006                 :             :         /* Install dependencies on new datatype and collation */
   15007                 :         184 :         add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
   15008                 :         184 :         add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
   15009                 :             : 
   15010                 :             :         /*
   15011                 :             :          * Drop any pg_statistic entry for the column, since it's now wrong type
   15012                 :             :          */
   15013                 :         184 :         RemoveStatistics(RelationGetRelid(rel), attnum);
   15014                 :             : 
   15015         [ +  - ]:         184 :         InvokeObjectPostAlterHook(RelationRelationId,
   15016                 :             :                                                           RelationGetRelid(rel), attnum);
   15017                 :             : 
   15018                 :             :         /*
   15019                 :             :          * Update the default, if present, by brute force --- remove and re-add
   15020                 :             :          * the default.  Probably unsafe to take shortcuts, since the new version
   15021                 :             :          * may well have additional dependencies.  (It's okay to do this now,
   15022                 :             :          * rather than after other ALTER TYPE commands, since the default won't
   15023                 :             :          * depend on other column types.)
   15024                 :             :          */
   15025         [ +  + ]:         184 :         if (defaultexpr)
   15026                 :             :         {
   15027                 :             :                 /*
   15028                 :             :                  * If it's a GENERATED default, drop its dependency records, in
   15029                 :             :                  * particular its INTERNAL dependency on the column, which would
   15030                 :             :                  * otherwise cause dependency.c to refuse to perform the deletion.
   15031                 :             :                  */
   15032         [ +  + ]:          13 :                 if (attTup->attgenerated)
   15033                 :             :                 {
   15034                 :           6 :                         Oid                     attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
   15035                 :             : 
   15036         [ +  - ]:           6 :                         if (!OidIsValid(attrdefoid))
   15037   [ #  #  #  # ]:           0 :                                 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
   15038                 :             :                                          RelationGetRelid(rel), attnum);
   15039                 :           6 :                         (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
   15040                 :           6 :                 }
   15041                 :             : 
   15042                 :             :                 /*
   15043                 :             :                  * Make updates-so-far visible, particularly the new pg_attribute row
   15044                 :             :                  * which will be updated again.
   15045                 :             :                  */
   15046                 :          13 :                 CommandCounterIncrement();
   15047                 :             : 
   15048                 :             :                 /*
   15049                 :             :                  * We use RESTRICT here for safety, but at present we do not expect
   15050                 :             :                  * anything to depend on the default.
   15051                 :             :                  */
   15052                 :          13 :                 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
   15053                 :             :                                                   true);
   15054                 :             : 
   15055                 :          13 :                 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
   15056                 :          13 :         }
   15057                 :             : 
   15058                 :         184 :         ObjectAddressSubSet(address, RelationRelationId,
   15059                 :             :                                                 RelationGetRelid(rel), attnum);
   15060                 :             : 
   15061                 :             :         /* Cleanup */
   15062                 :         184 :         heap_freetuple(heapTup);
   15063                 :             : 
   15064                 :             :         return address;
   15065                 :         184 : }
   15066                 :             : 
   15067                 :             : /*
   15068                 :             :  * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
   15069                 :             :  * that depends on the column (constraints, indexes, etc), and record enough
   15070                 :             :  * information to let us recreate the objects.
   15071                 :             :  */
   15072                 :             : static void
   15073                 :         201 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
   15074                 :             :                                                                   Relation rel, AttrNumber attnum, const char *colName)
   15075                 :             : {
   15076                 :         201 :         Relation        depRel;
   15077                 :         201 :         ScanKeyData key[3];
   15078                 :         201 :         SysScanDesc scan;
   15079                 :         201 :         HeapTuple       depTup;
   15080                 :             : 
   15081   [ +  +  +  - ]:         201 :         Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
   15082                 :             : 
   15083                 :         201 :         depRel = table_open(DependRelationId, RowExclusiveLock);
   15084                 :             : 
   15085                 :         402 :         ScanKeyInit(&key[0],
   15086                 :             :                                 Anum_pg_depend_refclassid,
   15087                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   15088                 :         201 :                                 ObjectIdGetDatum(RelationRelationId));
   15089                 :         402 :         ScanKeyInit(&key[1],
   15090                 :             :                                 Anum_pg_depend_refobjid,
   15091                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   15092                 :         201 :                                 ObjectIdGetDatum(RelationGetRelid(rel)));
   15093                 :         402 :         ScanKeyInit(&key[2],
   15094                 :             :                                 Anum_pg_depend_refobjsubid,
   15095                 :             :                                 BTEqualStrategyNumber, F_INT4EQ,
   15096                 :         201 :                                 Int32GetDatum((int32) attnum));
   15097                 :             : 
   15098                 :         402 :         scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   15099                 :         201 :                                                           NULL, 3, key);
   15100                 :             : 
   15101         [ +  + ]:         402 :         while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   15102                 :             :         {
   15103                 :         207 :                 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   15104                 :         207 :                 ObjectAddress foundObject;
   15105                 :             : 
   15106                 :         207 :                 foundObject.classId = foundDep->classid;
   15107                 :         207 :                 foundObject.objectId = foundDep->objid;
   15108                 :         207 :                 foundObject.objectSubId = foundDep->objsubid;
   15109                 :             : 
   15110   [ +  +  -  +  :         207 :                 switch (foundObject.classId)
          -  -  +  +  -  
                      - ]
   15111                 :             :                 {
   15112                 :             :                         case RelationRelationId:
   15113                 :             :                                 {
   15114                 :          46 :                                         char            relKind = get_rel_relkind(foundObject.objectId);
   15115                 :             : 
   15116   [ +  +  +  + ]:          46 :                                         if (relKind == RELKIND_INDEX ||
   15117                 :          16 :                                                 relKind == RELKIND_PARTITIONED_INDEX)
   15118                 :             :                                         {
   15119         [ +  - ]:          40 :                                                 Assert(foundObject.objectSubId == 0);
   15120                 :          40 :                                                 RememberIndexForRebuilding(foundObject.objectId, tab);
   15121                 :          40 :                                         }
   15122         [ -  + ]:           6 :                                         else if (relKind == RELKIND_SEQUENCE)
   15123                 :             :                                         {
   15124                 :             :                                                 /*
   15125                 :             :                                                  * This must be a SERIAL column's sequence.  We need
   15126                 :             :                                                  * not do anything to it.
   15127                 :             :                                                  */
   15128         [ -  + ]:           6 :                                                 Assert(foundObject.objectSubId == 0);
   15129                 :           6 :                                         }
   15130                 :             :                                         else
   15131                 :             :                                         {
   15132                 :             :                                                 /* Not expecting any other direct dependencies... */
   15133   [ #  #  #  # ]:           0 :                                                 elog(ERROR, "unexpected object depending on column: %s",
   15134                 :             :                                                          getObjectDescription(&foundObject, false));
   15135                 :             :                                         }
   15136                 :             :                                         break;
   15137                 :          46 :                                 }
   15138                 :             : 
   15139                 :             :                         case ConstraintRelationId:
   15140         [ +  - ]:         113 :                                 Assert(foundObject.objectSubId == 0);
   15141                 :         113 :                                 RememberConstraintForRebuilding(foundObject.objectId, tab);
   15142                 :         113 :                                 break;
   15143                 :             : 
   15144                 :             :                         case ProcedureRelationId:
   15145                 :             : 
   15146                 :             :                                 /*
   15147                 :             :                                  * A new-style SQL function can depend on a column, if that
   15148                 :             :                                  * column is referenced in the parsed function body.  Ideally
   15149                 :             :                                  * we'd automatically update the function by deparsing and
   15150                 :             :                                  * reparsing it, but that's risky and might well fail anyhow.
   15151                 :             :                                  * FIXME someday.
   15152                 :             :                                  *
   15153                 :             :                                  * This is only a problem for AT_AlterColumnType, not
   15154                 :             :                                  * AT_SetExpression.
   15155                 :             :                                  */
   15156         [ #  # ]:           0 :                                 if (subtype == AT_AlterColumnType)
   15157   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
   15158                 :             :                                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15159                 :             :                                                          errmsg("cannot alter type of a column used by a function or procedure"),
   15160                 :             :                                                          errdetail("%s depends on column \"%s\"",
   15161                 :             :                                                                            getObjectDescription(&foundObject, false),
   15162                 :             :                                                                            colName)));
   15163                 :           0 :                                 break;
   15164                 :             : 
   15165                 :             :                         case RewriteRelationId:
   15166                 :             : 
   15167                 :             :                                 /*
   15168                 :             :                                  * View/rule bodies have pretty much the same issues as
   15169                 :             :                                  * function bodies.  FIXME someday.
   15170                 :             :                                  */
   15171         [ -  + ]:           2 :                                 if (subtype == AT_AlterColumnType)
   15172   [ +  -  +  - ]:           2 :                                         ereport(ERROR,
   15173                 :             :                                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15174                 :             :                                                          errmsg("cannot alter type of a column used by a view or rule"),
   15175                 :             :                                                          errdetail("%s depends on column \"%s\"",
   15176                 :             :                                                                            getObjectDescription(&foundObject, false),
   15177                 :             :                                                                            colName)));
   15178                 :           0 :                                 break;
   15179                 :             : 
   15180                 :             :                         case TriggerRelationId:
   15181                 :             : 
   15182                 :             :                                 /*
   15183                 :             :                                  * A trigger can depend on a column because the column is
   15184                 :             :                                  * specified as an update target, or because the column is
   15185                 :             :                                  * used in the trigger's WHEN condition.  The first case would
   15186                 :             :                                  * not require any extra work, but the second case would
   15187                 :             :                                  * require updating the WHEN expression, which has the same
   15188                 :             :                                  * issues as above.  Since we can't easily tell which case
   15189                 :             :                                  * applies, we punt for both.  FIXME someday.
   15190                 :             :                                  */
   15191         [ #  # ]:           0 :                                 if (subtype == AT_AlterColumnType)
   15192   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
   15193                 :             :                                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15194                 :             :                                                          errmsg("cannot alter type of a column used in a trigger definition"),
   15195                 :             :                                                          errdetail("%s depends on column \"%s\"",
   15196                 :             :                                                                            getObjectDescription(&foundObject, false),
   15197                 :             :                                                                            colName)));
   15198                 :           0 :                                 break;
   15199                 :             : 
   15200                 :             :                         case PolicyRelationId:
   15201                 :             : 
   15202                 :             :                                 /*
   15203                 :             :                                  * A policy can depend on a column because the column is
   15204                 :             :                                  * specified in the policy's USING or WITH CHECK qual
   15205                 :             :                                  * expressions.  It might be possible to rewrite and recheck
   15206                 :             :                                  * the policy expression, but punt for now.  It's certainly
   15207                 :             :                                  * easy enough to remove and recreate the policy; still, FIXME
   15208                 :             :                                  * someday.
   15209                 :             :                                  */
   15210         [ #  # ]:           0 :                                 if (subtype == AT_AlterColumnType)
   15211   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
   15212                 :             :                                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15213                 :             :                                                          errmsg("cannot alter type of a column used in a policy definition"),
   15214                 :             :                                                          errdetail("%s depends on column \"%s\"",
   15215                 :             :                                                                            getObjectDescription(&foundObject, false),
   15216                 :             :                                                                            colName)));
   15217                 :           0 :                                 break;
   15218                 :             : 
   15219                 :             :                         case AttrDefaultRelationId:
   15220                 :             :                                 {
   15221                 :          34 :                                         ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
   15222                 :             : 
   15223   [ +  -  +  + ]:          34 :                                         if (col.objectId == RelationGetRelid(rel) &&
   15224                 :          34 :                                                 col.objectSubId == attnum)
   15225                 :             :                                         {
   15226                 :             :                                                 /*
   15227                 :             :                                                  * Ignore the column's own default expression.  The
   15228                 :             :                                                  * caller deals with it.
   15229                 :             :                                                  */
   15230                 :          30 :                                         }
   15231                 :             :                                         else
   15232                 :             :                                         {
   15233                 :             :                                                 /*
   15234                 :             :                                                  * This must be a reference from the expression of a
   15235                 :             :                                                  * generated column elsewhere in the same table.
   15236                 :             :                                                  * Changing the type/generated expression of a column
   15237                 :             :                                                  * that is used by a generated column is not allowed
   15238                 :             :                                                  * by SQL standard, so just punt for now.  It might be
   15239                 :             :                                                  * doable with some thinking and effort.
   15240                 :             :                                                  */
   15241         [ -  + ]:           4 :                                                 if (subtype == AT_AlterColumnType)
   15242   [ +  -  +  - ]:           4 :                                                         ereport(ERROR,
   15243                 :             :                                                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15244                 :             :                                                                          errmsg("cannot alter type of a column used by a generated column"),
   15245                 :             :                                                                          errdetail("Column \"%s\" is used by generated column \"%s\".",
   15246                 :             :                                                                                            colName,
   15247                 :             :                                                                                            get_attname(col.objectId,
   15248                 :             :                                                                                                                    col.objectSubId,
   15249                 :             :                                                                                                                    false))));
   15250                 :             :                                         }
   15251                 :             :                                         break;
   15252                 :          30 :                                 }
   15253                 :             : 
   15254                 :             :                         case StatisticExtRelationId:
   15255                 :             : 
   15256                 :             :                                 /*
   15257                 :             :                                  * Give the extended-stats machinery a chance to fix anything
   15258                 :             :                                  * that this column type change would break.
   15259                 :             :                                  */
   15260                 :          12 :                                 RememberStatisticsForRebuilding(foundObject.objectId, tab);
   15261                 :          12 :                                 break;
   15262                 :             : 
   15263                 :             :                         case PublicationRelRelationId:
   15264                 :             : 
   15265                 :             :                                 /*
   15266                 :             :                                  * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
   15267                 :             :                                  * clause.  Same issues as above.  FIXME someday.
   15268                 :             :                                  */
   15269         [ #  # ]:           0 :                                 if (subtype == AT_AlterColumnType)
   15270   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
   15271                 :             :                                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15272                 :             :                                                          errmsg("cannot alter type of a column used by a publication WHERE clause"),
   15273                 :             :                                                          errdetail("%s depends on column \"%s\"",
   15274                 :             :                                                                            getObjectDescription(&foundObject, false),
   15275                 :             :                                                                            colName)));
   15276                 :           0 :                                 break;
   15277                 :             : 
   15278                 :             :                         default:
   15279                 :             : 
   15280                 :             :                                 /*
   15281                 :             :                                  * We don't expect any other sorts of objects to depend on a
   15282                 :             :                                  * column.
   15283                 :             :                                  */
   15284   [ #  #  #  # ]:           0 :                                 elog(ERROR, "unexpected object depending on column: %s",
   15285                 :             :                                          getObjectDescription(&foundObject, false));
   15286                 :           0 :                                 break;
   15287                 :             :                 }
   15288                 :         201 :         }
   15289                 :             : 
   15290                 :         195 :         systable_endscan(scan);
   15291                 :         195 :         table_close(depRel, NoLock);
   15292                 :         195 : }
   15293                 :             : 
   15294                 :             : /*
   15295                 :             :  * Subroutine for ATExecAlterColumnType: remember that a replica identity
   15296                 :             :  * needs to be reset.
   15297                 :             :  */
   15298                 :             : static void
   15299                 :          75 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
   15300                 :             : {
   15301         [ +  + ]:          75 :         if (!get_index_isreplident(indoid))
   15302                 :          72 :                 return;
   15303                 :             : 
   15304         [ +  - ]:           3 :         if (tab->replicaIdentityIndex)
   15305   [ #  #  #  # ]:           0 :                 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
   15306                 :             : 
   15307                 :           3 :         tab->replicaIdentityIndex = get_rel_name(indoid);
   15308                 :          75 : }
   15309                 :             : 
   15310                 :             : /*
   15311                 :             :  * Subroutine for ATExecAlterColumnType: remember any clustered index.
   15312                 :             :  */
   15313                 :             : static void
   15314                 :          75 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
   15315                 :             : {
   15316         [ +  + ]:          75 :         if (!get_index_isclustered(indoid))
   15317                 :          72 :                 return;
   15318                 :             : 
   15319         [ +  - ]:           3 :         if (tab->clusterOnIndex)
   15320   [ #  #  #  # ]:           0 :                 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
   15321                 :             : 
   15322                 :           3 :         tab->clusterOnIndex = get_rel_name(indoid);
   15323                 :          75 : }
   15324                 :             : 
   15325                 :             : /*
   15326                 :             :  * Subroutine for ATExecAlterColumnType: remember that a constraint needs
   15327                 :             :  * to be rebuilt (which we might already know).
   15328                 :             :  */
   15329                 :             : static void
   15330                 :         115 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
   15331                 :             : {
   15332                 :             :         /*
   15333                 :             :          * This de-duplication check is critical for two independent reasons: we
   15334                 :             :          * mustn't try to recreate the same constraint twice, and if a constraint
   15335                 :             :          * depends on more than one column whose type is to be altered, we must
   15336                 :             :          * capture its definition string before applying any of the column type
   15337                 :             :          * changes.  ruleutils.c will get confused if we ask again later.
   15338                 :             :          */
   15339         [ +  + ]:         115 :         if (!list_member_oid(tab->changedConstraintOids, conoid))
   15340                 :             :         {
   15341                 :             :                 /* OK, capture the constraint's existing definition string */
   15342                 :         100 :                 char       *defstring = pg_get_constraintdef_command(conoid);
   15343                 :         100 :                 Oid                     indoid;
   15344                 :             : 
   15345                 :             :                 /*
   15346                 :             :                  * It is critical to create not-null constraints ahead of primary key
   15347                 :             :                  * indexes; otherwise, the not-null constraint would be created by the
   15348                 :             :                  * primary key, and the constraint name would be wrong.
   15349                 :             :                  */
   15350         [ +  + ]:         100 :                 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
   15351                 :             :                 {
   15352                 :          64 :                         tab->changedConstraintOids = lcons_oid(conoid,
   15353                 :          32 :                                                                                                    tab->changedConstraintOids);
   15354                 :          64 :                         tab->changedConstraintDefs = lcons(defstring,
   15355                 :          32 :                                                                                            tab->changedConstraintDefs);
   15356                 :          32 :                 }
   15357                 :             :                 else
   15358                 :             :                 {
   15359                 :             : 
   15360                 :         136 :                         tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
   15361                 :          68 :                                                                                                          conoid);
   15362                 :         136 :                         tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
   15363                 :          68 :                                                                                                  defstring);
   15364                 :             :                 }
   15365                 :             : 
   15366                 :             :                 /*
   15367                 :             :                  * For the index of a constraint, if any, remember if it is used for
   15368                 :             :                  * the table's replica identity or if it is a clustered index, so that
   15369                 :             :                  * ATPostAlterTypeCleanup() can queue up commands necessary to restore
   15370                 :             :                  * those properties.
   15371                 :             :                  */
   15372                 :         100 :                 indoid = get_constraint_index(conoid);
   15373         [ +  + ]:         100 :                 if (OidIsValid(indoid))
   15374                 :             :                 {
   15375                 :          38 :                         RememberReplicaIdentityForRebuilding(indoid, tab);
   15376                 :          38 :                         RememberClusterOnForRebuilding(indoid, tab);
   15377                 :          38 :                 }
   15378                 :         100 :         }
   15379                 :         115 : }
   15380                 :             : 
   15381                 :             : /*
   15382                 :             :  * Subroutine for ATExecAlterColumnType: remember that an index needs
   15383                 :             :  * to be rebuilt (which we might already know).
   15384                 :             :  */
   15385                 :             : static void
   15386                 :          40 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
   15387                 :             : {
   15388                 :             :         /*
   15389                 :             :          * This de-duplication check is critical for two independent reasons: we
   15390                 :             :          * mustn't try to recreate the same index twice, and if an index depends
   15391                 :             :          * on more than one column whose type is to be altered, we must capture
   15392                 :             :          * its definition string before applying any of the column type changes.
   15393                 :             :          * ruleutils.c will get confused if we ask again later.
   15394                 :             :          */
   15395         [ +  + ]:          40 :         if (!list_member_oid(tab->changedIndexOids, indoid))
   15396                 :             :         {
   15397                 :             :                 /*
   15398                 :             :                  * Before adding it as an index-to-rebuild, we'd better see if it
   15399                 :             :                  * belongs to a constraint, and if so rebuild the constraint instead.
   15400                 :             :                  * Typically this check fails, because constraint indexes normally
   15401                 :             :                  * have only dependencies on their constraint.  But it's possible for
   15402                 :             :                  * such an index to also have direct dependencies on table columns,
   15403                 :             :                  * for example with a partial exclusion constraint.
   15404                 :             :                  */
   15405                 :          39 :                 Oid                     conoid = get_index_constraint(indoid);
   15406                 :             : 
   15407         [ +  + ]:          39 :                 if (OidIsValid(conoid))
   15408                 :             :                 {
   15409                 :           2 :                         RememberConstraintForRebuilding(conoid, tab);
   15410                 :           2 :                 }
   15411                 :             :                 else
   15412                 :             :                 {
   15413                 :             :                         /* OK, capture the index's existing definition string */
   15414                 :          37 :                         char       *defstring = pg_get_indexdef_string(indoid);
   15415                 :             : 
   15416                 :          74 :                         tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
   15417                 :          37 :                                                                                                 indoid);
   15418                 :          74 :                         tab->changedIndexDefs = lappend(tab->changedIndexDefs,
   15419                 :          37 :                                                                                         defstring);
   15420                 :             : 
   15421                 :             :                         /*
   15422                 :             :                          * Remember if this index is used for the table's replica identity
   15423                 :             :                          * or if it is a clustered index, so that ATPostAlterTypeCleanup()
   15424                 :             :                          * can queue up commands necessary to restore those properties.
   15425                 :             :                          */
   15426                 :          37 :                         RememberReplicaIdentityForRebuilding(indoid, tab);
   15427                 :          37 :                         RememberClusterOnForRebuilding(indoid, tab);
   15428                 :          37 :                 }
   15429                 :          39 :         }
   15430                 :          40 : }
   15431                 :             : 
   15432                 :             : /*
   15433                 :             :  * Subroutine for ATExecAlterColumnType: remember that a statistics object
   15434                 :             :  * needs to be rebuilt (which we might already know).
   15435                 :             :  */
   15436                 :             : static void
   15437                 :          12 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
   15438                 :             : {
   15439                 :             :         /*
   15440                 :             :          * This de-duplication check is critical for two independent reasons: we
   15441                 :             :          * mustn't try to recreate the same statistics object twice, and if the
   15442                 :             :          * statistics object depends on more than one column whose type is to be
   15443                 :             :          * altered, we must capture its definition string before applying any of
   15444                 :             :          * the type changes. ruleutils.c will get confused if we ask again later.
   15445                 :             :          */
   15446         [ -  + ]:          12 :         if (!list_member_oid(tab->changedStatisticsOids, stxoid))
   15447                 :             :         {
   15448                 :             :                 /* OK, capture the statistics object's existing definition string */
   15449                 :          12 :                 char       *defstring = pg_get_statisticsobjdef_string(stxoid);
   15450                 :             : 
   15451                 :          24 :                 tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
   15452                 :          12 :                                                                                                  stxoid);
   15453                 :          24 :                 tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
   15454                 :          12 :                                                                                          defstring);
   15455                 :          12 :         }
   15456                 :          12 : }
   15457                 :             : 
   15458                 :             : /*
   15459                 :             :  * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
   15460                 :             :  * operations for a particular relation.  We have to drop and recreate all the
   15461                 :             :  * indexes and constraints that depend on the altered columns.  We do the
   15462                 :             :  * actual dropping here, but re-creation is managed by adding work queue
   15463                 :             :  * entries to do those steps later.
   15464                 :             :  */
   15465                 :             : static void
   15466                 :         203 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
   15467                 :             : {
   15468                 :         203 :         ObjectAddress obj;
   15469                 :         203 :         ObjectAddresses *objects;
   15470                 :         203 :         ListCell   *def_item;
   15471                 :         203 :         ListCell   *oid_item;
   15472                 :             : 
   15473                 :             :         /*
   15474                 :             :          * Collect all the constraints and indexes to drop so we can process them
   15475                 :             :          * in a single call.  That way we don't have to worry about dependencies
   15476                 :             :          * among them.
   15477                 :             :          */
   15478                 :         203 :         objects = new_object_addresses();
   15479                 :             : 
   15480                 :             :         /*
   15481                 :             :          * Re-parse the index and constraint definitions, and attach them to the
   15482                 :             :          * appropriate work queue entries.  We do this before dropping because in
   15483                 :             :          * the case of a constraint on another table, we might not yet have
   15484                 :             :          * exclusive lock on the table the constraint is attached to, and we need
   15485                 :             :          * to get that before reparsing/dropping.  (That's possible at least for
   15486                 :             :          * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
   15487                 :             :          * requires a dependency on the target table's composite type in the other
   15488                 :             :          * table's constraint expressions.)
   15489                 :             :          *
   15490                 :             :          * We can't rely on the output of deparsing to tell us which relation to
   15491                 :             :          * operate on, because concurrent activity might have made the name
   15492                 :             :          * resolve differently.  Instead, we've got to use the OID of the
   15493                 :             :          * constraint or index we're processing to figure out which relation to
   15494                 :             :          * operate on.
   15495                 :             :          */
   15496   [ +  +  +  +  :         303 :         forboth(oid_item, tab->changedConstraintOids,
          +  +  +  +  +  
                +  +  + ]
   15497                 :             :                         def_item, tab->changedConstraintDefs)
   15498                 :             :         {
   15499                 :         100 :                 Oid                     oldId = lfirst_oid(oid_item);
   15500                 :         100 :                 HeapTuple       tup;
   15501                 :         100 :                 Form_pg_constraint con;
   15502                 :         100 :                 Oid                     relid;
   15503                 :         100 :                 Oid                     confrelid;
   15504                 :         100 :                 bool            conislocal;
   15505                 :             : 
   15506                 :         100 :                 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   15507         [ +  - ]:         100 :                 if (!HeapTupleIsValid(tup)) /* should not happen */
   15508   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for constraint %u", oldId);
   15509                 :         100 :                 con = (Form_pg_constraint) GETSTRUCT(tup);
   15510         [ +  + ]:         100 :                 if (OidIsValid(con->conrelid))
   15511                 :          98 :                         relid = con->conrelid;
   15512                 :             :                 else
   15513                 :             :                 {
   15514                 :             :                         /* must be a domain constraint */
   15515                 :           2 :                         relid = get_typ_typrelid(getBaseType(con->contypid));
   15516         [ +  - ]:           2 :                         if (!OidIsValid(relid))
   15517   [ #  #  #  # ]:           0 :                                 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
   15518                 :             :                 }
   15519                 :         100 :                 confrelid = con->confrelid;
   15520                 :         100 :                 conislocal = con->conislocal;
   15521                 :         100 :                 ReleaseSysCache(tup);
   15522                 :             : 
   15523                 :         100 :                 ObjectAddressSet(obj, ConstraintRelationId, oldId);
   15524                 :         100 :                 add_exact_object_address(&obj, objects);
   15525                 :             : 
   15526                 :             :                 /*
   15527                 :             :                  * If the constraint is inherited (only), we don't want to inject a
   15528                 :             :                  * new definition here; it'll get recreated when
   15529                 :             :                  * ATAddCheckNNConstraint recurses from adding the parent table's
   15530                 :             :                  * constraint.  But we had to carry the info this far so that we can
   15531                 :             :                  * drop the constraint below.
   15532                 :             :                  */
   15533         [ +  + ]:         100 :                 if (!conislocal)
   15534                 :           4 :                         continue;
   15535                 :             : 
   15536                 :             :                 /*
   15537                 :             :                  * When rebuilding another table's constraint that references the
   15538                 :             :                  * table we're modifying, we might not yet have any lock on the other
   15539                 :             :                  * table, so get one now.  We'll need AccessExclusiveLock for the DROP
   15540                 :             :                  * CONSTRAINT step, so there's no value in asking for anything weaker.
   15541                 :             :                  */
   15542         [ +  + ]:          96 :                 if (relid != tab->relid)
   15543                 :           8 :                         LockRelationOid(relid, AccessExclusiveLock);
   15544                 :             : 
   15545                 :         192 :                 ATPostAlterTypeParse(oldId, relid, confrelid,
   15546                 :          96 :                                                          (char *) lfirst(def_item),
   15547                 :          96 :                                                          wqueue, lockmode, tab->rewrite);
   15548      [ -  +  + ]:         100 :         }
   15549   [ +  +  +  +  :         240 :         forboth(oid_item, tab->changedIndexOids,
          +  +  +  +  +  
                +  +  + ]
   15550                 :             :                         def_item, tab->changedIndexDefs)
   15551                 :             :         {
   15552                 :          37 :                 Oid                     oldId = lfirst_oid(oid_item);
   15553                 :          37 :                 Oid                     relid;
   15554                 :             : 
   15555                 :          37 :                 relid = IndexGetRelation(oldId, false);
   15556                 :             : 
   15557                 :             :                 /*
   15558                 :             :                  * As above, make sure we have lock on the index's table if it's not
   15559                 :             :                  * the same table.
   15560                 :             :                  */
   15561         [ +  + ]:          37 :                 if (relid != tab->relid)
   15562                 :           2 :                         LockRelationOid(relid, AccessExclusiveLock);
   15563                 :             : 
   15564                 :          74 :                 ATPostAlterTypeParse(oldId, relid, InvalidOid,
   15565                 :          37 :                                                          (char *) lfirst(def_item),
   15566                 :          37 :                                                          wqueue, lockmode, tab->rewrite);
   15567                 :             : 
   15568                 :          37 :                 ObjectAddressSet(obj, RelationRelationId, oldId);
   15569                 :          37 :                 add_exact_object_address(&obj, objects);
   15570                 :          37 :         }
   15571                 :             : 
   15572                 :             :         /* add dependencies for new statistics */
   15573   [ +  +  +  +  :         215 :         forboth(oid_item, tab->changedStatisticsOids,
          +  +  +  +  +  
                +  +  + ]
   15574                 :             :                         def_item, tab->changedStatisticsDefs)
   15575                 :             :         {
   15576                 :          12 :                 Oid                     oldId = lfirst_oid(oid_item);
   15577                 :          12 :                 Oid                     relid;
   15578                 :             : 
   15579                 :          12 :                 relid = StatisticsGetRelation(oldId, false);
   15580                 :             : 
   15581                 :             :                 /*
   15582                 :             :                  * As above, make sure we have lock on the statistics object's table
   15583                 :             :                  * if it's not the same table.  However, we take
   15584                 :             :                  * ShareUpdateExclusiveLock here, aligning with the lock level used in
   15585                 :             :                  * CreateStatistics and RemoveStatisticsById.
   15586                 :             :                  *
   15587                 :             :                  * CAUTION: this should be done after all cases that grab
   15588                 :             :                  * AccessExclusiveLock, else we risk causing deadlock due to needing
   15589                 :             :                  * to promote our table lock.
   15590                 :             :                  */
   15591         [ +  + ]:          12 :                 if (relid != tab->relid)
   15592                 :           2 :                         LockRelationOid(relid, ShareUpdateExclusiveLock);
   15593                 :             : 
   15594                 :          24 :                 ATPostAlterTypeParse(oldId, relid, InvalidOid,
   15595                 :          12 :                                                          (char *) lfirst(def_item),
   15596                 :          12 :                                                          wqueue, lockmode, tab->rewrite);
   15597                 :             : 
   15598                 :          12 :                 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
   15599                 :          12 :                 add_exact_object_address(&obj, objects);
   15600                 :          12 :         }
   15601                 :             : 
   15602                 :             :         /*
   15603                 :             :          * Queue up command to restore replica identity index marking
   15604                 :             :          */
   15605         [ +  + ]:         203 :         if (tab->replicaIdentityIndex)
   15606                 :             :         {
   15607                 :           3 :                 AlterTableCmd *cmd = makeNode(AlterTableCmd);
   15608                 :           3 :                 ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
   15609                 :             : 
   15610                 :           3 :                 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
   15611                 :           3 :                 subcmd->name = tab->replicaIdentityIndex;
   15612                 :           3 :                 cmd->subtype = AT_ReplicaIdentity;
   15613                 :           3 :                 cmd->def = (Node *) subcmd;
   15614                 :             : 
   15615                 :             :                 /* do it after indexes and constraints */
   15616                 :           3 :                 tab->subcmds[AT_PASS_OLD_CONSTR] =
   15617                 :           3 :                         lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   15618                 :           3 :         }
   15619                 :             : 
   15620                 :             :         /*
   15621                 :             :          * Queue up command to restore marking of index used for cluster.
   15622                 :             :          */
   15623         [ +  + ]:         203 :         if (tab->clusterOnIndex)
   15624                 :             :         {
   15625                 :           3 :                 AlterTableCmd *cmd = makeNode(AlterTableCmd);
   15626                 :             : 
   15627                 :           3 :                 cmd->subtype = AT_ClusterOn;
   15628                 :           3 :                 cmd->name = tab->clusterOnIndex;
   15629                 :             : 
   15630                 :             :                 /* do it after indexes and constraints */
   15631                 :           3 :                 tab->subcmds[AT_PASS_OLD_CONSTR] =
   15632                 :           3 :                         lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   15633                 :           3 :         }
   15634                 :             : 
   15635                 :             :         /*
   15636                 :             :          * It should be okay to use DROP_RESTRICT here, since nothing else should
   15637                 :             :          * be depending on these objects.
   15638                 :             :          */
   15639                 :         203 :         performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   15640                 :             : 
   15641                 :         203 :         free_object_addresses(objects);
   15642                 :             : 
   15643                 :             :         /*
   15644                 :             :          * The objects will get recreated during subsequent passes over the work
   15645                 :             :          * queue.
   15646                 :             :          */
   15647                 :         203 : }
   15648                 :             : 
   15649                 :             : /*
   15650                 :             :  * Parse the previously-saved definition string for a constraint, index or
   15651                 :             :  * statistics object against the newly-established column data type(s), and
   15652                 :             :  * queue up the resulting command parsetrees for execution.
   15653                 :             :  *
   15654                 :             :  * This might fail if, for example, you have a WHERE clause that uses an
   15655                 :             :  * operator that's not available for the new column type.
   15656                 :             :  */
   15657                 :             : static void
   15658                 :         145 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
   15659                 :             :                                          List **wqueue, LOCKMODE lockmode, bool rewrite)
   15660                 :             : {
   15661                 :         145 :         List       *raw_parsetree_list;
   15662                 :         145 :         List       *querytree_list;
   15663                 :         145 :         ListCell   *list_item;
   15664                 :         145 :         Relation        rel;
   15665                 :             : 
   15666                 :             :         /*
   15667                 :             :          * We expect that we will get only ALTER TABLE and CREATE INDEX
   15668                 :             :          * statements. Hence, there is no need to pass them through
   15669                 :             :          * parse_analyze_*() or the rewriter, but instead we need to pass them
   15670                 :             :          * through parse_utilcmd.c to make them ready for execution.
   15671                 :             :          */
   15672                 :         145 :         raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
   15673                 :         145 :         querytree_list = NIL;
   15674   [ +  -  +  +  :         290 :         foreach(list_item, raw_parsetree_list)
                   +  + ]
   15675                 :             :         {
   15676                 :         145 :                 RawStmt    *rs = lfirst_node(RawStmt, list_item);
   15677                 :         145 :                 Node       *stmt = rs->stmt;
   15678                 :             : 
   15679         [ +  + ]:         145 :                 if (IsA(stmt, IndexStmt))
   15680                 :          74 :                         querytree_list = lappend(querytree_list,
   15681                 :          74 :                                                                          transformIndexStmt(oldRelId,
   15682                 :          37 :                                                                                                                 (IndexStmt *) stmt,
   15683                 :          37 :                                                                                                                 cmd));
   15684         [ +  + ]:         108 :                 else if (IsA(stmt, AlterTableStmt))
   15685                 :             :                 {
   15686                 :          94 :                         List       *beforeStmts;
   15687                 :          94 :                         List       *afterStmts;
   15688                 :             : 
   15689                 :         188 :                         stmt = (Node *) transformAlterTableStmt(oldRelId,
   15690                 :          94 :                                                                                                         (AlterTableStmt *) stmt,
   15691                 :          94 :                                                                                                         cmd,
   15692                 :             :                                                                                                         &beforeStmts,
   15693                 :             :                                                                                                         &afterStmts);
   15694                 :          94 :                         querytree_list = list_concat(querytree_list, beforeStmts);
   15695                 :          94 :                         querytree_list = lappend(querytree_list, stmt);
   15696                 :          94 :                         querytree_list = list_concat(querytree_list, afterStmts);
   15697                 :          94 :                 }
   15698         [ +  + ]:          14 :                 else if (IsA(stmt, CreateStatsStmt))
   15699                 :          24 :                         querytree_list = lappend(querytree_list,
   15700                 :          24 :                                                                          transformStatsStmt(oldRelId,
   15701                 :          12 :                                                                                                                 (CreateStatsStmt *) stmt,
   15702                 :          12 :                                                                                                                 cmd));
   15703                 :             :                 else
   15704                 :           2 :                         querytree_list = lappend(querytree_list, stmt);
   15705                 :         145 :         }
   15706                 :             : 
   15707                 :             :         /* Caller should already have acquired whatever lock we need. */
   15708                 :         145 :         rel = relation_open(oldRelId, NoLock);
   15709                 :             : 
   15710                 :             :         /*
   15711                 :             :          * Attach each generated command to the proper place in the work queue.
   15712                 :             :          * Note this could result in creation of entirely new work-queue entries.
   15713                 :             :          *
   15714                 :             :          * Also note that we have to tweak the command subtypes, because it turns
   15715                 :             :          * out that re-creation of indexes and constraints has to act a bit
   15716                 :             :          * differently from initial creation.
   15717                 :             :          */
   15718   [ +  -  +  +  :         290 :         foreach(list_item, querytree_list)
                   +  + ]
   15719                 :             :         {
   15720                 :         145 :                 Node       *stm = (Node *) lfirst(list_item);
   15721                 :         145 :                 AlteredTableInfo *tab;
   15722                 :             : 
   15723                 :         145 :                 tab = ATGetQueueEntry(wqueue, rel);
   15724                 :             : 
   15725         [ +  + ]:         145 :                 if (IsA(stm, IndexStmt))
   15726                 :             :                 {
   15727                 :          37 :                         IndexStmt  *stmt = (IndexStmt *) stm;
   15728                 :          37 :                         AlterTableCmd *newcmd;
   15729                 :             : 
   15730         [ +  + ]:          37 :                         if (!rewrite)
   15731                 :           9 :                                 TryReuseIndex(oldId, stmt);
   15732                 :          37 :                         stmt->reset_default_tblspc = true;
   15733                 :             :                         /* keep the index's comment */
   15734                 :          37 :                         stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
   15735                 :             : 
   15736                 :          37 :                         newcmd = makeNode(AlterTableCmd);
   15737                 :          37 :                         newcmd->subtype = AT_ReAddIndex;
   15738                 :          37 :                         newcmd->def = (Node *) stmt;
   15739                 :          37 :                         tab->subcmds[AT_PASS_OLD_INDEX] =
   15740                 :          37 :                                 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
   15741                 :          37 :                 }
   15742         [ +  + ]:         108 :                 else if (IsA(stm, AlterTableStmt))
   15743                 :             :                 {
   15744                 :          94 :                         AlterTableStmt *stmt = (AlterTableStmt *) stm;
   15745                 :          94 :                         ListCell   *lcmd;
   15746                 :             : 
   15747   [ +  -  +  +  :         188 :                         foreach(lcmd, stmt->cmds)
                   +  + ]
   15748                 :             :                         {
   15749                 :          94 :                                 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
   15750                 :             : 
   15751         [ +  + ]:          94 :                                 if (cmd->subtype == AT_AddIndex)
   15752                 :             :                                 {
   15753                 :          38 :                                         IndexStmt  *indstmt;
   15754                 :          38 :                                         Oid                     indoid;
   15755                 :             : 
   15756                 :          38 :                                         indstmt = castNode(IndexStmt, cmd->def);
   15757                 :          38 :                                         indoid = get_constraint_index(oldId);
   15758                 :             : 
   15759         [ +  + ]:          38 :                                         if (!rewrite)
   15760                 :           8 :                                                 TryReuseIndex(indoid, indstmt);
   15761                 :             :                                         /* keep any comment on the index */
   15762                 :          38 :                                         indstmt->idxcomment = GetComment(indoid,
   15763                 :             :                                                                                                          RelationRelationId, 0);
   15764                 :          38 :                                         indstmt->reset_default_tblspc = true;
   15765                 :             : 
   15766                 :          38 :                                         cmd->subtype = AT_ReAddIndex;
   15767                 :          38 :                                         tab->subcmds[AT_PASS_OLD_INDEX] =
   15768                 :          38 :                                                 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
   15769                 :             : 
   15770                 :             :                                         /* recreate any comment on the constraint */
   15771                 :          76 :                                         RebuildConstraintComment(tab,
   15772                 :             :                                                                                          AT_PASS_OLD_INDEX,
   15773                 :          38 :                                                                                          oldId,
   15774                 :          38 :                                                                                          rel,
   15775                 :             :                                                                                          NIL,
   15776                 :          38 :                                                                                          indstmt->idxname);
   15777                 :          38 :                                 }
   15778         [ +  - ]:          56 :                                 else if (cmd->subtype == AT_AddConstraint)
   15779                 :             :                                 {
   15780                 :          56 :                                         Constraint *con = castNode(Constraint, cmd->def);
   15781                 :             : 
   15782                 :          56 :                                         con->old_pktable_oid = refRelId;
   15783                 :             :                                         /* rewriting neither side of a FK */
   15784         [ +  + ]:          56 :                                         if (con->contype == CONSTR_FOREIGN &&
   15785   [ +  +  -  + ]:          12 :                                                 !rewrite && tab->rewrite == 0)
   15786                 :           1 :                                                 TryReuseForeignKey(oldId, con);
   15787                 :          56 :                                         con->reset_default_tblspc = true;
   15788                 :          56 :                                         cmd->subtype = AT_ReAddConstraint;
   15789                 :          56 :                                         tab->subcmds[AT_PASS_OLD_CONSTR] =
   15790                 :          56 :                                                 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   15791                 :             : 
   15792                 :             :                                         /*
   15793                 :             :                                          * Recreate any comment on the constraint.  If we have
   15794                 :             :                                          * recreated a primary key, then transformTableConstraint
   15795                 :             :                                          * has added an unnamed not-null constraint here; skip
   15796                 :             :                                          * this in that case.
   15797                 :             :                                          */
   15798         [ +  - ]:          56 :                                         if (con->conname)
   15799                 :         112 :                                                 RebuildConstraintComment(tab,
   15800                 :             :                                                                                                  AT_PASS_OLD_CONSTR,
   15801                 :          56 :                                                                                                  oldId,
   15802                 :          56 :                                                                                                  rel,
   15803                 :             :                                                                                                  NIL,
   15804                 :          56 :                                                                                                  con->conname);
   15805                 :             :                                         else
   15806         [ #  # ]:           0 :                                                 Assert(con->contype == CONSTR_NOTNULL);
   15807                 :          56 :                                 }
   15808                 :             :                                 else
   15809   [ #  #  #  # ]:           0 :                                         elog(ERROR, "unexpected statement subtype: %d",
   15810                 :             :                                                  (int) cmd->subtype);
   15811                 :          94 :                         }
   15812                 :          94 :                 }
   15813         [ +  + ]:          14 :                 else if (IsA(stm, AlterDomainStmt))
   15814                 :             :                 {
   15815                 :           2 :                         AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
   15816                 :             : 
   15817         [ +  - ]:           2 :                         if (stmt->subtype == AD_AddConstraint)
   15818                 :             :                         {
   15819                 :           2 :                                 Constraint *con = castNode(Constraint, stmt->def);
   15820                 :           2 :                                 AlterTableCmd *cmd = makeNode(AlterTableCmd);
   15821                 :             : 
   15822                 :           2 :                                 cmd->subtype = AT_ReAddDomainConstraint;
   15823                 :           2 :                                 cmd->def = (Node *) stmt;
   15824                 :           2 :                                 tab->subcmds[AT_PASS_OLD_CONSTR] =
   15825                 :           2 :                                         lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   15826                 :             : 
   15827                 :             :                                 /* recreate any comment on the constraint */
   15828                 :           4 :                                 RebuildConstraintComment(tab,
   15829                 :             :                                                                                  AT_PASS_OLD_CONSTR,
   15830                 :           2 :                                                                                  oldId,
   15831                 :             :                                                                                  NULL,
   15832                 :           2 :                                                                                  stmt->typeName,
   15833                 :           2 :                                                                                  con->conname);
   15834                 :           2 :                         }
   15835                 :             :                         else
   15836   [ #  #  #  # ]:           0 :                                 elog(ERROR, "unexpected statement subtype: %d",
   15837                 :             :                                          (int) stmt->subtype);
   15838                 :           2 :                 }
   15839         [ +  - ]:          12 :                 else if (IsA(stm, CreateStatsStmt))
   15840                 :             :                 {
   15841                 :          12 :                         CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
   15842                 :          12 :                         AlterTableCmd *newcmd;
   15843                 :             : 
   15844                 :             :                         /* keep the statistics object's comment */
   15845                 :          12 :                         stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
   15846                 :             : 
   15847                 :          12 :                         newcmd = makeNode(AlterTableCmd);
   15848                 :          12 :                         newcmd->subtype = AT_ReAddStatistics;
   15849                 :          12 :                         newcmd->def = (Node *) stmt;
   15850                 :          12 :                         tab->subcmds[AT_PASS_MISC] =
   15851                 :          12 :                                 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
   15852                 :          12 :                 }
   15853                 :             :                 else
   15854   [ #  #  #  # ]:           0 :                         elog(ERROR, "unexpected statement type: %d",
   15855                 :             :                                  (int) nodeTag(stm));
   15856                 :         145 :         }
   15857                 :             : 
   15858                 :         145 :         relation_close(rel, NoLock);
   15859                 :         145 : }
   15860                 :             : 
   15861                 :             : /*
   15862                 :             :  * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
   15863                 :             :  * for a table or domain constraint that is being rebuilt.
   15864                 :             :  *
   15865                 :             :  * objid is the OID of the constraint.
   15866                 :             :  * Pass "rel" for a table constraint, or "domname" (domain's qualified name
   15867                 :             :  * as a string list) for a domain constraint.
   15868                 :             :  * (We could dig that info, as well as the conname, out of the pg_constraint
   15869                 :             :  * entry; but callers already have them so might as well pass them.)
   15870                 :             :  */
   15871                 :             : static void
   15872                 :          96 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
   15873                 :             :                                                  Relation rel, List *domname,
   15874                 :             :                                                  const char *conname)
   15875                 :             : {
   15876                 :          96 :         CommentStmt *cmd;
   15877                 :          96 :         char       *comment_str;
   15878                 :          96 :         AlterTableCmd *newcmd;
   15879                 :             : 
   15880                 :             :         /* Look for comment for object wanted, and leave if none */
   15881                 :          96 :         comment_str = GetComment(objid, ConstraintRelationId, 0);
   15882         [ +  + ]:          96 :         if (comment_str == NULL)
   15883                 :          81 :                 return;
   15884                 :             : 
   15885                 :             :         /* Build CommentStmt node, copying all input data for safety */
   15886                 :          15 :         cmd = makeNode(CommentStmt);
   15887         [ +  + ]:          15 :         if (rel)
   15888                 :             :         {
   15889                 :          13 :                 cmd->objtype = OBJECT_TABCONSTRAINT;
   15890                 :          13 :                 cmd->object = (Node *)
   15891                 :          13 :                         list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
   15892                 :             :                                            makeString(pstrdup(RelationGetRelationName(rel))),
   15893                 :             :                                            makeString(pstrdup(conname)));
   15894                 :          13 :         }
   15895                 :             :         else
   15896                 :             :         {
   15897                 :           2 :                 cmd->objtype = OBJECT_DOMCONSTRAINT;
   15898                 :           2 :                 cmd->object = (Node *)
   15899                 :           2 :                         list_make2(makeTypeNameFromNameList(copyObject(domname)),
   15900                 :             :                                            makeString(pstrdup(conname)));
   15901                 :             :         }
   15902                 :          15 :         cmd->comment = comment_str;
   15903                 :             : 
   15904                 :             :         /* Append it to list of commands */
   15905                 :          15 :         newcmd = makeNode(AlterTableCmd);
   15906                 :          15 :         newcmd->subtype = AT_ReAddComment;
   15907                 :          15 :         newcmd->def = (Node *) cmd;
   15908                 :          15 :         tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
   15909         [ -  + ]:          96 : }
   15910                 :             : 
   15911                 :             : /*
   15912                 :             :  * Subroutine for ATPostAlterTypeParse().  Calls out to CheckIndexCompatible()
   15913                 :             :  * for the real analysis, then mutates the IndexStmt based on that verdict.
   15914                 :             :  */
   15915                 :             : static void
   15916                 :          17 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
   15917                 :             : {
   15918   [ -  +  -  + ]:          34 :         if (CheckIndexCompatible(oldId,
   15919                 :          17 :                                                          stmt->accessMethod,
   15920                 :          17 :                                                          stmt->indexParams,
   15921                 :          17 :                                                          stmt->excludeOpNames,
   15922                 :          17 :                                                          stmt->iswithoutoverlaps))
   15923                 :             :         {
   15924                 :          17 :                 Relation        irel = index_open(oldId, NoLock);
   15925                 :             : 
   15926                 :             :                 /* If it's a partitioned index, there is no storage to share. */
   15927         [ +  + ]:          17 :                 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   15928                 :             :                 {
   15929                 :          12 :                         stmt->oldNumber = irel->rd_locator.relNumber;
   15930                 :          12 :                         stmt->oldCreateSubid = irel->rd_createSubid;
   15931                 :          12 :                         stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
   15932                 :          12 :                 }
   15933                 :          17 :                 index_close(irel, NoLock);
   15934                 :          17 :         }
   15935                 :          17 : }
   15936                 :             : 
   15937                 :             : /*
   15938                 :             :  * Subroutine for ATPostAlterTypeParse().
   15939                 :             :  *
   15940                 :             :  * Stash the old P-F equality operator into the Constraint node, for possible
   15941                 :             :  * use by ATAddForeignKeyConstraint() in determining whether revalidation of
   15942                 :             :  * this constraint can be skipped.
   15943                 :             :  */
   15944                 :             : static void
   15945                 :           1 : TryReuseForeignKey(Oid oldId, Constraint *con)
   15946                 :             : {
   15947                 :           1 :         HeapTuple       tup;
   15948                 :           1 :         Datum           adatum;
   15949                 :           1 :         ArrayType  *arr;
   15950                 :           1 :         Oid                *rawarr;
   15951                 :           1 :         int                     numkeys;
   15952                 :           1 :         int                     i;
   15953                 :             : 
   15954         [ +  - ]:           1 :         Assert(con->contype == CONSTR_FOREIGN);
   15955         [ +  - ]:           1 :         Assert(con->old_conpfeqop == NIL);   /* already prepared this node */
   15956                 :             : 
   15957                 :           1 :         tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   15958         [ +  - ]:           1 :         if (!HeapTupleIsValid(tup)) /* should not happen */
   15959   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for constraint %u", oldId);
   15960                 :             : 
   15961                 :           1 :         adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
   15962                 :             :                                                                         Anum_pg_constraint_conpfeqop);
   15963                 :           1 :         arr = DatumGetArrayTypeP(adatum);       /* ensure not toasted */
   15964                 :           1 :         numkeys = ARR_DIMS(arr)[0];
   15965                 :             :         /* test follows the one in ri_FetchConstraintInfo() */
   15966         [ +  - ]:           1 :         if (ARR_NDIM(arr) != 1 ||
   15967                 :           1 :                 ARR_HASNULL(arr) ||
   15968                 :           1 :                 ARR_ELEMTYPE(arr) != OIDOID)
   15969   [ #  #  #  # ]:           0 :                 elog(ERROR, "conpfeqop is not a 1-D Oid array");
   15970         [ -  + ]:           1 :         rawarr = (Oid *) ARR_DATA_PTR(arr);
   15971                 :             : 
   15972                 :             :         /* stash a List of the operator Oids in our Constraint node */
   15973         [ +  + ]:           2 :         for (i = 0; i < numkeys; i++)
   15974                 :           1 :                 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
   15975                 :             : 
   15976                 :           1 :         ReleaseSysCache(tup);
   15977                 :           1 : }
   15978                 :             : 
   15979                 :             : /*
   15980                 :             :  * ALTER COLUMN .. OPTIONS ( ... )
   15981                 :             :  *
   15982                 :             :  * Returns the address of the modified column
   15983                 :             :  */
   15984                 :             : static ObjectAddress
   15985                 :           7 : ATExecAlterColumnGenericOptions(Relation rel,
   15986                 :             :                                                                 const char *colName,
   15987                 :             :                                                                 List *options,
   15988                 :             :                                                                 LOCKMODE lockmode)
   15989                 :             : {
   15990                 :           7 :         Relation        ftrel;
   15991                 :           7 :         Relation        attrel;
   15992                 :           7 :         ForeignServer *server;
   15993                 :           7 :         ForeignDataWrapper *fdw;
   15994                 :           7 :         HeapTuple       tuple;
   15995                 :           7 :         HeapTuple       newtuple;
   15996                 :           7 :         bool            isnull;
   15997                 :           7 :         Datum           repl_val[Natts_pg_attribute];
   15998                 :           7 :         bool            repl_null[Natts_pg_attribute];
   15999                 :           7 :         bool            repl_repl[Natts_pg_attribute];
   16000                 :           7 :         Datum           datum;
   16001                 :           7 :         Form_pg_foreign_table fttableform;
   16002                 :           7 :         Form_pg_attribute atttableform;
   16003                 :           7 :         AttrNumber      attnum;
   16004                 :           7 :         ObjectAddress address;
   16005                 :             : 
   16006         [ +  - ]:           7 :         if (options == NIL)
   16007                 :           0 :                 return InvalidObjectAddress;
   16008                 :             : 
   16009                 :             :         /* First, determine FDW validator associated to the foreign table. */
   16010                 :           7 :         ftrel = table_open(ForeignTableRelationId, AccessShareLock);
   16011                 :           7 :         tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
   16012         [ +  - ]:           7 :         if (!HeapTupleIsValid(tuple))
   16013   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   16014                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   16015                 :             :                                  errmsg("foreign table \"%s\" does not exist",
   16016                 :             :                                                 RelationGetRelationName(rel))));
   16017                 :           7 :         fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   16018                 :           7 :         server = GetForeignServer(fttableform->ftserver);
   16019                 :           7 :         fdw = GetForeignDataWrapper(server->fdwid);
   16020                 :             : 
   16021                 :           7 :         table_close(ftrel, AccessShareLock);
   16022                 :           7 :         ReleaseSysCache(tuple);
   16023                 :             : 
   16024                 :           7 :         attrel = table_open(AttributeRelationId, RowExclusiveLock);
   16025                 :           7 :         tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   16026         [ +  - ]:           7 :         if (!HeapTupleIsValid(tuple))
   16027   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   16028                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   16029                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   16030                 :             :                                                 colName, RelationGetRelationName(rel))));
   16031                 :             : 
   16032                 :             :         /* Prevent them from altering a system attribute */
   16033                 :           7 :         atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   16034                 :           7 :         attnum = atttableform->attnum;
   16035         [ +  + ]:           7 :         if (attnum <= 0)
   16036   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   16037                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16038                 :             :                                  errmsg("cannot alter system column \"%s\"", colName)));
   16039                 :             : 
   16040                 :             : 
   16041                 :             :         /* Initialize buffers for new tuple values */
   16042                 :           6 :         memset(repl_val, 0, sizeof(repl_val));
   16043                 :           6 :         memset(repl_null, false, sizeof(repl_null));
   16044                 :           6 :         memset(repl_repl, false, sizeof(repl_repl));
   16045                 :             : 
   16046                 :             :         /* Extract the current options */
   16047                 :           6 :         datum = SysCacheGetAttr(ATTNAME,
   16048                 :           6 :                                                         tuple,
   16049                 :             :                                                         Anum_pg_attribute_attfdwoptions,
   16050                 :             :                                                         &isnull);
   16051         [ +  + ]:           6 :         if (isnull)
   16052                 :           5 :                 datum = PointerGetDatum(NULL);
   16053                 :             : 
   16054                 :             :         /* Transform the options */
   16055                 :           6 :         datum = transformGenericOptions(AttributeRelationId,
   16056                 :           6 :                                                                         datum,
   16057                 :           6 :                                                                         options,
   16058                 :           6 :                                                                         fdw->fdwvalidator);
   16059                 :             : 
   16060         [ +  - ]:           6 :         if (DatumGetPointer(datum) != NULL)
   16061                 :           6 :                 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
   16062                 :             :         else
   16063                 :           0 :                 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
   16064                 :             : 
   16065                 :           6 :         repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
   16066                 :             : 
   16067                 :             :         /* Everything looks good - update the tuple */
   16068                 :             : 
   16069                 :          12 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
   16070                 :           6 :                                                                  repl_val, repl_null, repl_repl);
   16071                 :             : 
   16072                 :           6 :         CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
   16073                 :             : 
   16074         [ +  - ]:           6 :         InvokeObjectPostAlterHook(RelationRelationId,
   16075                 :             :                                                           RelationGetRelid(rel),
   16076                 :             :                                                           atttableform->attnum);
   16077                 :           6 :         ObjectAddressSubSet(address, RelationRelationId,
   16078                 :             :                                                 RelationGetRelid(rel), attnum);
   16079                 :             : 
   16080                 :           6 :         ReleaseSysCache(tuple);
   16081                 :             : 
   16082                 :           6 :         table_close(attrel, RowExclusiveLock);
   16083                 :             : 
   16084                 :           6 :         heap_freetuple(newtuple);
   16085                 :             : 
   16086                 :           6 :         return address;
   16087                 :           6 : }
   16088                 :             : 
   16089                 :             : /*
   16090                 :             :  * ALTER TABLE OWNER
   16091                 :             :  *
   16092                 :             :  * recursing is true if we are recursing from a table to its indexes,
   16093                 :             :  * sequences, or toast table.  We don't allow the ownership of those things to
   16094                 :             :  * be changed separately from the parent table.  Also, we can skip permission
   16095                 :             :  * checks (this is necessary not just an optimization, else we'd fail to
   16096                 :             :  * handle toast tables properly).
   16097                 :             :  *
   16098                 :             :  * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
   16099                 :             :  * free-standing composite type.
   16100                 :             :  */
   16101                 :             : void
   16102                 :          73 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
   16103                 :             : {
   16104                 :          73 :         Relation        target_rel;
   16105                 :          73 :         Relation        class_rel;
   16106                 :          73 :         HeapTuple       tuple;
   16107                 :          73 :         Form_pg_class tuple_class;
   16108                 :             : 
   16109                 :             :         /*
   16110                 :             :          * Get exclusive lock till end of transaction on the target table. Use
   16111                 :             :          * relation_open so that we can work on indexes and sequences.
   16112                 :             :          */
   16113                 :          73 :         target_rel = relation_open(relationOid, lockmode);
   16114                 :             : 
   16115                 :             :         /* Get its pg_class tuple, too */
   16116                 :          73 :         class_rel = table_open(RelationRelationId, RowExclusiveLock);
   16117                 :             : 
   16118                 :          73 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
   16119         [ +  - ]:          73 :         if (!HeapTupleIsValid(tuple))
   16120   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", relationOid);
   16121                 :          73 :         tuple_class = (Form_pg_class) GETSTRUCT(tuple);
   16122                 :             : 
   16123                 :             :         /* Can we change the ownership of this tuple? */
   16124   [ +  +  +  +  :          73 :         switch (tuple_class->relkind)
                +  +  - ]
   16125                 :             :         {
   16126                 :             :                 case RELKIND_RELATION:
   16127                 :             :                 case RELKIND_VIEW:
   16128                 :             :                 case RELKIND_MATVIEW:
   16129                 :             :                 case RELKIND_FOREIGN_TABLE:
   16130                 :             :                 case RELKIND_PARTITIONED_TABLE:
   16131                 :             :                         /* ok to change owner */
   16132                 :          44 :                         break;
   16133                 :             :                 case RELKIND_INDEX:
   16134         [ +  - ]:          14 :                         if (!recursing)
   16135                 :             :                         {
   16136                 :             :                                 /*
   16137                 :             :                                  * Because ALTER INDEX OWNER used to be allowed, and in fact
   16138                 :             :                                  * is generated by old versions of pg_dump, we give a warning
   16139                 :             :                                  * and do nothing rather than erroring out.  Also, to avoid
   16140                 :             :                                  * unnecessary chatter while restoring those old dumps, say
   16141                 :             :                                  * nothing at all if the command would be a no-op anyway.
   16142                 :             :                                  */
   16143         [ #  # ]:           0 :                                 if (tuple_class->relowner != newOwnerId)
   16144   [ #  #  #  # ]:           0 :                                         ereport(WARNING,
   16145                 :             :                                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16146                 :             :                                                          errmsg("cannot change owner of index \"%s\"",
   16147                 :             :                                                                         NameStr(tuple_class->relname)),
   16148                 :             :                                                          errhint("Change the ownership of the index's table instead.")));
   16149                 :             :                                 /* quick hack to exit via the no-op path */
   16150                 :           0 :                                 newOwnerId = tuple_class->relowner;
   16151                 :           0 :                         }
   16152                 :          14 :                         break;
   16153                 :             :                 case RELKIND_PARTITIONED_INDEX:
   16154         [ +  - ]:           2 :                         if (recursing)
   16155                 :           2 :                                 break;
   16156   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   16157                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16158                 :             :                                          errmsg("cannot change owner of index \"%s\"",
   16159                 :             :                                                         NameStr(tuple_class->relname)),
   16160                 :             :                                          errhint("Change the ownership of the index's table instead.")));
   16161                 :           0 :                         break;
   16162                 :             :                 case RELKIND_SEQUENCE:
   16163   [ -  +  #  # ]:           6 :                         if (!recursing &&
   16164                 :           0 :                                 tuple_class->relowner != newOwnerId)
   16165                 :             :                         {
   16166                 :             :                                 /* if it's an owned sequence, disallow changing it by itself */
   16167                 :           0 :                                 Oid                     tableId;
   16168                 :           0 :                                 int32           colId;
   16169                 :             : 
   16170         [ #  # ]:           0 :                                 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
   16171                 :           0 :                                         sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
   16172   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
   16173                 :             :                                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16174                 :             :                                                          errmsg("cannot change owner of sequence \"%s\"",
   16175                 :             :                                                                         NameStr(tuple_class->relname)),
   16176                 :             :                                                          errdetail("Sequence \"%s\" is linked to table \"%s\".",
   16177                 :             :                                                                            NameStr(tuple_class->relname),
   16178                 :             :                                                                            get_rel_name(tableId))));
   16179                 :           0 :                         }
   16180                 :           6 :                         break;
   16181                 :             :                 case RELKIND_COMPOSITE_TYPE:
   16182         [ +  - ]:           1 :                         if (recursing)
   16183                 :           1 :                                 break;
   16184   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   16185                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16186                 :             :                                          errmsg("\"%s\" is a composite type",
   16187                 :             :                                                         NameStr(tuple_class->relname)),
   16188                 :             :                         /* translator: %s is an SQL ALTER command */
   16189                 :             :                                          errhint("Use %s instead.",
   16190                 :             :                                                          "ALTER TYPE")));
   16191                 :           0 :                         break;
   16192                 :             :                 case RELKIND_TOASTVALUE:
   16193         [ +  - ]:           6 :                         if (recursing)
   16194                 :           6 :                                 break;
   16195                 :             :                         /* FALL THRU */
   16196                 :             :                 default:
   16197   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   16198                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16199                 :             :                                          errmsg("cannot change owner of relation \"%s\"",
   16200                 :             :                                                         NameStr(tuple_class->relname)),
   16201                 :             :                                          errdetail_relkind_not_supported(tuple_class->relkind)));
   16202                 :           0 :         }
   16203                 :             : 
   16204                 :             :         /*
   16205                 :             :          * If the new owner is the same as the existing owner, consider the
   16206                 :             :          * command to have succeeded.  This is for dump restoration purposes.
   16207                 :             :          */
   16208         [ +  + ]:          73 :         if (tuple_class->relowner != newOwnerId)
   16209                 :             :         {
   16210                 :          69 :                 Datum           repl_val[Natts_pg_class];
   16211                 :          69 :                 bool            repl_null[Natts_pg_class];
   16212                 :          69 :                 bool            repl_repl[Natts_pg_class];
   16213                 :          69 :                 Acl                *newAcl;
   16214                 :          69 :                 Datum           aclDatum;
   16215                 :          69 :                 bool            isNull;
   16216                 :          69 :                 HeapTuple       newtuple;
   16217                 :             : 
   16218                 :             :                 /* skip permission checks when recursing to index or toast table */
   16219         [ +  + ]:          69 :                 if (!recursing)
   16220                 :             :                 {
   16221                 :             :                         /* Superusers can always do it */
   16222         [ +  + ]:          40 :                         if (!superuser())
   16223                 :             :                         {
   16224                 :           5 :                                 Oid                     namespaceOid = tuple_class->relnamespace;
   16225                 :           5 :                                 AclResult       aclresult;
   16226                 :             : 
   16227                 :             :                                 /* Otherwise, must be owner of the existing object */
   16228         [ +  - ]:           5 :                                 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
   16229                 :           0 :                                         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
   16230                 :           0 :                                                                    RelationGetRelationName(target_rel));
   16231                 :             : 
   16232                 :             :                                 /* Must be able to become new owner */
   16233                 :           5 :                                 check_can_set_role(GetUserId(), newOwnerId);
   16234                 :             : 
   16235                 :             :                                 /* New owner must have CREATE privilege on namespace */
   16236                 :           5 :                                 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
   16237                 :             :                                                                                         ACL_CREATE);
   16238         [ +  - ]:           5 :                                 if (aclresult != ACLCHECK_OK)
   16239                 :           0 :                                         aclcheck_error(aclresult, OBJECT_SCHEMA,
   16240                 :           0 :                                                                    get_namespace_name(namespaceOid));
   16241                 :           5 :                         }
   16242                 :          40 :                 }
   16243                 :             : 
   16244                 :          69 :                 memset(repl_null, false, sizeof(repl_null));
   16245                 :          69 :                 memset(repl_repl, false, sizeof(repl_repl));
   16246                 :             : 
   16247                 :          69 :                 repl_repl[Anum_pg_class_relowner - 1] = true;
   16248                 :          69 :                 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
   16249                 :             : 
   16250                 :             :                 /*
   16251                 :             :                  * Determine the modified ACL for the new owner.  This is only
   16252                 :             :                  * necessary when the ACL is non-null.
   16253                 :             :                  */
   16254                 :          69 :                 aclDatum = SysCacheGetAttr(RELOID, tuple,
   16255                 :             :                                                                    Anum_pg_class_relacl,
   16256                 :             :                                                                    &isNull);
   16257         [ +  + ]:          69 :                 if (!isNull)
   16258                 :             :                 {
   16259                 :          10 :                         newAcl = aclnewowner(DatumGetAclP(aclDatum),
   16260                 :           5 :                                                                  tuple_class->relowner, newOwnerId);
   16261                 :           5 :                         repl_repl[Anum_pg_class_relacl - 1] = true;
   16262                 :           5 :                         repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
   16263                 :           5 :                 }
   16264                 :             : 
   16265                 :          69 :                 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
   16266                 :             : 
   16267                 :          69 :                 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
   16268                 :             : 
   16269                 :          69 :                 heap_freetuple(newtuple);
   16270                 :             : 
   16271                 :             :                 /*
   16272                 :             :                  * We must similarly update any per-column ACLs to reflect the new
   16273                 :             :                  * owner; for neatness reasons that's split out as a subroutine.
   16274                 :             :                  */
   16275                 :         138 :                 change_owner_fix_column_acls(relationOid,
   16276                 :          69 :                                                                          tuple_class->relowner,
   16277                 :          69 :                                                                          newOwnerId);
   16278                 :             : 
   16279                 :             :                 /*
   16280                 :             :                  * Update owner dependency reference, if any.  A composite type has
   16281                 :             :                  * none, because it's tracked for the pg_type entry instead of here;
   16282                 :             :                  * indexes and TOAST tables don't have their own entries either.
   16283                 :             :                  */
   16284         [ +  + ]:          69 :                 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
   16285         [ +  + ]:          68 :                         tuple_class->relkind != RELKIND_INDEX &&
   16286   [ +  +  +  + ]:          54 :                         tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
   16287                 :          52 :                         tuple_class->relkind != RELKIND_TOASTVALUE)
   16288                 :          92 :                         changeDependencyOnOwner(RelationRelationId, relationOid,
   16289                 :          46 :                                                                         newOwnerId);
   16290                 :             : 
   16291                 :             :                 /*
   16292                 :             :                  * Also change the ownership of the table's row type, if it has one
   16293                 :             :                  */
   16294         [ +  + ]:          69 :                 if (OidIsValid(tuple_class->reltype))
   16295                 :          43 :                         AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
   16296                 :             : 
   16297                 :             :                 /*
   16298                 :             :                  * If we are operating on a table or materialized view, also change
   16299                 :             :                  * the ownership of any indexes and sequences that belong to the
   16300                 :             :                  * relation, as well as its toast table (if it has one).
   16301                 :             :                  */
   16302         [ +  + ]:          69 :                 if (tuple_class->relkind == RELKIND_RELATION ||
   16303         [ +  + ]:          36 :                         tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
   16304   [ +  -  +  + ]:          29 :                         tuple_class->relkind == RELKIND_MATVIEW ||
   16305                 :          29 :                         tuple_class->relkind == RELKIND_TOASTVALUE)
   16306                 :             :                 {
   16307                 :          46 :                         List       *index_oid_list;
   16308                 :          46 :                         ListCell   *i;
   16309                 :             : 
   16310                 :             :                         /* Find all the indexes belonging to this relation */
   16311                 :          46 :                         index_oid_list = RelationGetIndexList(target_rel);
   16312                 :             : 
   16313                 :             :                         /* For each index, recursively change its ownership */
   16314   [ +  +  +  +  :          62 :                         foreach(i, index_oid_list)
                   +  + ]
   16315                 :          16 :                                 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
   16316                 :             : 
   16317                 :          46 :                         list_free(index_oid_list);
   16318                 :          46 :                 }
   16319                 :             : 
   16320                 :             :                 /* If it has a toast table, recurse to change its ownership */
   16321         [ +  + ]:          69 :                 if (tuple_class->reltoastrelid != InvalidOid)
   16322                 :          12 :                         ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
   16323                 :           6 :                                                           true, lockmode);
   16324                 :             : 
   16325                 :             :                 /* If it has dependent sequences, recurse to change them too */
   16326                 :          69 :                 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
   16327                 :          69 :         }
   16328                 :             : 
   16329         [ +  - ]:          73 :         InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
   16330                 :             : 
   16331                 :          73 :         ReleaseSysCache(tuple);
   16332                 :          73 :         table_close(class_rel, RowExclusiveLock);
   16333                 :          73 :         relation_close(target_rel, NoLock);
   16334                 :          73 : }
   16335                 :             : 
   16336                 :             : /*
   16337                 :             :  * change_owner_fix_column_acls
   16338                 :             :  *
   16339                 :             :  * Helper function for ATExecChangeOwner.  Scan the columns of the table
   16340                 :             :  * and fix any non-null column ACLs to reflect the new owner.
   16341                 :             :  */
   16342                 :             : static void
   16343                 :          69 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
   16344                 :             : {
   16345                 :          69 :         Relation        attRelation;
   16346                 :          69 :         SysScanDesc scan;
   16347                 :          69 :         ScanKeyData key[1];
   16348                 :          69 :         HeapTuple       attributeTuple;
   16349                 :             : 
   16350                 :          69 :         attRelation = table_open(AttributeRelationId, RowExclusiveLock);
   16351                 :         138 :         ScanKeyInit(&key[0],
   16352                 :             :                                 Anum_pg_attribute_attrelid,
   16353                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   16354                 :          69 :                                 ObjectIdGetDatum(relationOid));
   16355                 :         138 :         scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
   16356                 :          69 :                                                           true, NULL, 1, key);
   16357         [ +  + ]:         491 :         while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   16358                 :             :         {
   16359                 :         422 :                 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   16360                 :         422 :                 Datum           repl_val[Natts_pg_attribute];
   16361                 :         422 :                 bool            repl_null[Natts_pg_attribute];
   16362                 :         422 :                 bool            repl_repl[Natts_pg_attribute];
   16363                 :         422 :                 Acl                *newAcl;
   16364                 :         422 :                 Datum           aclDatum;
   16365                 :         422 :                 bool            isNull;
   16366                 :         422 :                 HeapTuple       newtuple;
   16367                 :             : 
   16368                 :             :                 /* Ignore dropped columns */
   16369         [ -  + ]:         422 :                 if (att->attisdropped)
   16370                 :           0 :                         continue;
   16371                 :             : 
   16372                 :         844 :                 aclDatum = heap_getattr(attributeTuple,
   16373                 :             :                                                                 Anum_pg_attribute_attacl,
   16374                 :         422 :                                                                 RelationGetDescr(attRelation),
   16375                 :             :                                                                 &isNull);
   16376                 :             :                 /* Null ACLs do not require changes */
   16377         [ +  - ]:         422 :                 if (isNull)
   16378                 :         422 :                         continue;
   16379                 :             : 
   16380                 :           0 :                 memset(repl_null, false, sizeof(repl_null));
   16381                 :           0 :                 memset(repl_repl, false, sizeof(repl_repl));
   16382                 :             : 
   16383                 :           0 :                 newAcl = aclnewowner(DatumGetAclP(aclDatum),
   16384                 :           0 :                                                          oldOwnerId, newOwnerId);
   16385                 :           0 :                 repl_repl[Anum_pg_attribute_attacl - 1] = true;
   16386                 :           0 :                 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
   16387                 :             : 
   16388                 :           0 :                 newtuple = heap_modify_tuple(attributeTuple,
   16389                 :           0 :                                                                          RelationGetDescr(attRelation),
   16390                 :           0 :                                                                          repl_val, repl_null, repl_repl);
   16391                 :             : 
   16392                 :           0 :                 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
   16393                 :             : 
   16394                 :           0 :                 heap_freetuple(newtuple);
   16395      [ -  +  - ]:         422 :         }
   16396                 :          69 :         systable_endscan(scan);
   16397                 :          69 :         table_close(attRelation, RowExclusiveLock);
   16398                 :          69 : }
   16399                 :             : 
   16400                 :             : /*
   16401                 :             :  * change_owner_recurse_to_sequences
   16402                 :             :  *
   16403                 :             :  * Helper function for ATExecChangeOwner.  Examines pg_depend searching
   16404                 :             :  * for sequences that are dependent on serial columns, and changes their
   16405                 :             :  * ownership.
   16406                 :             :  */
   16407                 :             : static void
   16408                 :          69 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
   16409                 :             : {
   16410                 :          69 :         Relation        depRel;
   16411                 :          69 :         SysScanDesc scan;
   16412                 :          69 :         ScanKeyData key[2];
   16413                 :          69 :         HeapTuple       tup;
   16414                 :             : 
   16415                 :             :         /*
   16416                 :             :          * SERIAL sequences are those having an auto dependency on one of the
   16417                 :             :          * table's columns (we don't care *which* column, exactly).
   16418                 :             :          */
   16419                 :          69 :         depRel = table_open(DependRelationId, AccessShareLock);
   16420                 :             : 
   16421                 :         138 :         ScanKeyInit(&key[0],
   16422                 :             :                                 Anum_pg_depend_refclassid,
   16423                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   16424                 :          69 :                                 ObjectIdGetDatum(RelationRelationId));
   16425                 :         138 :         ScanKeyInit(&key[1],
   16426                 :             :                                 Anum_pg_depend_refobjid,
   16427                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   16428                 :          69 :                                 ObjectIdGetDatum(relationOid));
   16429                 :             :         /* we leave refobjsubid unspecified */
   16430                 :             : 
   16431                 :         138 :         scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   16432                 :          69 :                                                           NULL, 2, key);
   16433                 :             : 
   16434         [ +  + ]:         200 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
   16435                 :             :         {
   16436                 :         131 :                 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   16437                 :         131 :                 Relation        seqRel;
   16438                 :             : 
   16439                 :             :                 /* skip dependencies other than auto dependencies on columns */
   16440         [ +  + ]:         131 :                 if (depForm->refobjsubid == 0 ||
   16441         [ +  + ]:          52 :                         depForm->classid != RelationRelationId ||
   16442   [ +  -  +  - ]:          20 :                         depForm->objsubid != 0 ||
   16443         [ +  + ]:          19 :                         !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   16444                 :         112 :                         continue;
   16445                 :             : 
   16446                 :             :                 /* Use relation_open just in case it's an index */
   16447                 :          19 :                 seqRel = relation_open(depForm->objid, lockmode);
   16448                 :             : 
   16449                 :             :                 /* skip non-sequence relations */
   16450         [ +  + ]:          19 :                 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   16451                 :             :                 {
   16452                 :             :                         /* No need to keep the lock */
   16453                 :          15 :                         relation_close(seqRel, lockmode);
   16454                 :          15 :                         continue;
   16455                 :             :                 }
   16456                 :             : 
   16457                 :             :                 /* We don't need to close the sequence while we alter it. */
   16458                 :           4 :                 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
   16459                 :             : 
   16460                 :             :                 /* Now we can close it.  Keep the lock till end of transaction. */
   16461                 :           4 :                 relation_close(seqRel, NoLock);
   16462      [ -  +  + ]:         131 :         }
   16463                 :             : 
   16464                 :          69 :         systable_endscan(scan);
   16465                 :             : 
   16466                 :          69 :         relation_close(depRel, AccessShareLock);
   16467                 :          69 : }
   16468                 :             : 
   16469                 :             : /*
   16470                 :             :  * ALTER TABLE CLUSTER ON
   16471                 :             :  *
   16472                 :             :  * The only thing we have to do is to change the indisclustered bits.
   16473                 :             :  *
   16474                 :             :  * Return the address of the new clustering index.
   16475                 :             :  */
   16476                 :             : static ObjectAddress
   16477                 :           9 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
   16478                 :             : {
   16479                 :           9 :         Oid                     indexOid;
   16480                 :             :         ObjectAddress address;
   16481                 :             : 
   16482                 :           9 :         indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
   16483                 :             : 
   16484         [ +  - ]:           9 :         if (!OidIsValid(indexOid))
   16485   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   16486                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   16487                 :             :                                  errmsg("index \"%s\" for table \"%s\" does not exist",
   16488                 :             :                                                 indexName, RelationGetRelationName(rel))));
   16489                 :             : 
   16490                 :             :         /* Check index is valid to cluster on */
   16491                 :           9 :         check_index_is_clusterable(rel, indexOid, lockmode);
   16492                 :             : 
   16493                 :             :         /* And do the work */
   16494                 :           9 :         mark_index_clustered(rel, indexOid, false);
   16495                 :             : 
   16496                 :           9 :         ObjectAddressSet(address,
   16497                 :             :                                          RelationRelationId, indexOid);
   16498                 :             : 
   16499                 :             :         return address;
   16500                 :           9 : }
   16501                 :             : 
   16502                 :             : /*
   16503                 :             :  * ALTER TABLE SET WITHOUT CLUSTER
   16504                 :             :  *
   16505                 :             :  * We have to find any indexes on the table that have indisclustered bit
   16506                 :             :  * set and turn it off.
   16507                 :             :  */
   16508                 :             : static void
   16509                 :           3 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
   16510                 :             : {
   16511                 :           3 :         mark_index_clustered(rel, InvalidOid, false);
   16512                 :           3 : }
   16513                 :             : 
   16514                 :             : /*
   16515                 :             :  * Preparation phase for SET ACCESS METHOD
   16516                 :             :  *
   16517                 :             :  * Check that the access method exists and determine whether a change is
   16518                 :             :  * actually needed.
   16519                 :             :  */
   16520                 :             : static void
   16521                 :          18 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
   16522                 :             : {
   16523                 :          18 :         Oid                     amoid;
   16524                 :             : 
   16525                 :             :         /*
   16526                 :             :          * Look up the access method name and check that it differs from the
   16527                 :             :          * table's current AM.  If DEFAULT was specified for a partitioned table
   16528                 :             :          * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
   16529                 :             :          */
   16530         [ +  + ]:          18 :         if (amname != NULL)
   16531                 :          12 :                 amoid = get_table_am_oid(amname, false);
   16532         [ +  + ]:           6 :         else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   16533                 :           3 :                 amoid = InvalidOid;
   16534                 :             :         else
   16535                 :           3 :                 amoid = get_table_am_oid(default_table_access_method, false);
   16536                 :             : 
   16537                 :             :         /* if it's a match, phase 3 doesn't need to do anything */
   16538         [ +  + ]:          18 :         if (rel->rd_rel->relam == amoid)
   16539                 :           2 :                 return;
   16540                 :             : 
   16541                 :             :         /* Save info for Phase 3 to do the real work */
   16542                 :          16 :         tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
   16543                 :          16 :         tab->newAccessMethod = amoid;
   16544                 :          16 :         tab->chgAccessMethod = true;
   16545         [ -  + ]:          18 : }
   16546                 :             : 
   16547                 :             : /*
   16548                 :             :  * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
   16549                 :             :  * storage that have an interest in preserving AM.
   16550                 :             :  *
   16551                 :             :  * Since these have no storage, setting the access method is a catalog only
   16552                 :             :  * operation.
   16553                 :             :  */
   16554                 :             : static void
   16555                 :           7 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
   16556                 :             : {
   16557                 :           7 :         Relation        pg_class;
   16558                 :           7 :         Oid                     oldAccessMethodId;
   16559                 :           7 :         HeapTuple       tuple;
   16560                 :           7 :         Form_pg_class rd_rel;
   16561                 :           7 :         Oid                     reloid = RelationGetRelid(rel);
   16562                 :             : 
   16563                 :             :         /*
   16564                 :             :          * Shouldn't be called on relations having storage; these are processed in
   16565                 :             :          * phase 3.
   16566                 :             :          */
   16567         [ +  - ]:           7 :         Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   16568                 :             : 
   16569                 :             :         /* Get a modifiable copy of the relation's pg_class row. */
   16570                 :           7 :         pg_class = table_open(RelationRelationId, RowExclusiveLock);
   16571                 :             : 
   16572                 :           7 :         tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
   16573         [ +  - ]:           7 :         if (!HeapTupleIsValid(tuple))
   16574   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", reloid);
   16575                 :           7 :         rd_rel = (Form_pg_class) GETSTRUCT(tuple);
   16576                 :             : 
   16577                 :             :         /* Update the pg_class row. */
   16578                 :           7 :         oldAccessMethodId = rd_rel->relam;
   16579                 :           7 :         rd_rel->relam = newAccessMethodId;
   16580                 :             : 
   16581                 :             :         /* Leave if no update required */
   16582         [ -  + ]:           7 :         if (rd_rel->relam == oldAccessMethodId)
   16583                 :             :         {
   16584                 :           0 :                 heap_freetuple(tuple);
   16585                 :           0 :                 table_close(pg_class, RowExclusiveLock);
   16586                 :           0 :                 return;
   16587                 :             :         }
   16588                 :             : 
   16589                 :           7 :         CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   16590                 :             : 
   16591                 :             :         /*
   16592                 :             :          * Update the dependency on the new access method.  No dependency is added
   16593                 :             :          * if the new access method is InvalidOid (default case).  Be very careful
   16594                 :             :          * that this has to compare the previous value stored in pg_class with the
   16595                 :             :          * new one.
   16596                 :             :          */
   16597   [ +  +  -  + ]:           7 :         if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
   16598                 :             :         {
   16599                 :           3 :                 ObjectAddress relobj,
   16600                 :             :                                         referenced;
   16601                 :             : 
   16602                 :             :                 /*
   16603                 :             :                  * New access method is defined and there was no dependency
   16604                 :             :                  * previously, so record a new one.
   16605                 :             :                  */
   16606                 :           3 :                 ObjectAddressSet(relobj, RelationRelationId, reloid);
   16607                 :           3 :                 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
   16608                 :           3 :                 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
   16609                 :           3 :         }
   16610   [ +  -  +  + ]:           4 :         else if (OidIsValid(oldAccessMethodId) &&
   16611                 :           4 :                          !OidIsValid(rd_rel->relam))
   16612                 :             :         {
   16613                 :             :                 /*
   16614                 :             :                  * There was an access method defined, and no new one, so just remove
   16615                 :             :                  * the existing dependency.
   16616                 :             :                  */
   16617                 :           2 :                 deleteDependencyRecordsForClass(RelationRelationId, reloid,
   16618                 :             :                                                                                 AccessMethodRelationId,
   16619                 :             :                                                                                 DEPENDENCY_NORMAL);
   16620                 :           2 :         }
   16621                 :             :         else
   16622                 :             :         {
   16623         [ +  - ]:           2 :                 Assert(OidIsValid(oldAccessMethodId) &&
   16624                 :             :                            OidIsValid(rd_rel->relam));
   16625                 :             : 
   16626                 :             :                 /* Both are valid, so update the dependency */
   16627                 :           4 :                 changeDependencyFor(RelationRelationId, reloid,
   16628                 :             :                                                         AccessMethodRelationId,
   16629                 :           2 :                                                         oldAccessMethodId, rd_rel->relam);
   16630                 :             :         }
   16631                 :             : 
   16632                 :             :         /* make the relam and dependency changes visible */
   16633                 :           7 :         CommandCounterIncrement();
   16634                 :             : 
   16635         [ -  + ]:           7 :         InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   16636                 :             : 
   16637                 :           7 :         heap_freetuple(tuple);
   16638                 :           7 :         table_close(pg_class, RowExclusiveLock);
   16639         [ -  + ]:           7 : }
   16640                 :             : 
   16641                 :             : /*
   16642                 :             :  * ALTER TABLE SET TABLESPACE
   16643                 :             :  */
   16644                 :             : static void
   16645                 :          25 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
   16646                 :             : {
   16647                 :          25 :         Oid                     tablespaceId;
   16648                 :             : 
   16649                 :             :         /* Check that the tablespace exists */
   16650                 :          25 :         tablespaceId = get_tablespace_oid(tablespacename, false);
   16651                 :             : 
   16652                 :             :         /* Check permissions except when moving to database's default */
   16653   [ +  -  +  + ]:          25 :         if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
   16654                 :             :         {
   16655                 :           9 :                 AclResult       aclresult;
   16656                 :             : 
   16657                 :           9 :                 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
   16658         [ +  - ]:           9 :                 if (aclresult != ACLCHECK_OK)
   16659                 :           0 :                         aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
   16660                 :           9 :         }
   16661                 :             : 
   16662                 :             :         /* Save info for Phase 3 to do the real work */
   16663         [ +  - ]:          25 :         if (OidIsValid(tab->newTableSpace))
   16664   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   16665                 :             :                                 (errcode(ERRCODE_SYNTAX_ERROR),
   16666                 :             :                                  errmsg("cannot have multiple SET TABLESPACE subcommands")));
   16667                 :             : 
   16668                 :          25 :         tab->newTableSpace = tablespaceId;
   16669                 :          25 : }
   16670                 :             : 
   16671                 :             : /*
   16672                 :             :  * Set, reset, or replace reloptions.
   16673                 :             :  */
   16674                 :             : static void
   16675                 :         119 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
   16676                 :             :                                         LOCKMODE lockmode)
   16677                 :             : {
   16678                 :         119 :         Oid                     relid;
   16679                 :         119 :         Relation        pgclass;
   16680                 :         119 :         HeapTuple       tuple;
   16681                 :         119 :         HeapTuple       newtuple;
   16682                 :         119 :         Datum           datum;
   16683                 :         119 :         Datum           newOptions;
   16684                 :         119 :         Datum           repl_val[Natts_pg_class];
   16685                 :         119 :         bool            repl_null[Natts_pg_class];
   16686                 :         119 :         bool            repl_repl[Natts_pg_class];
   16687                 :         119 :         const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
   16688                 :             : 
   16689   [ +  +  +  - ]:         119 :         if (defList == NIL && operation != AT_ReplaceRelOptions)
   16690                 :           0 :                 return;                                 /* nothing to do */
   16691                 :             : 
   16692                 :         119 :         pgclass = table_open(RelationRelationId, RowExclusiveLock);
   16693                 :             : 
   16694                 :             :         /* Fetch heap tuple */
   16695                 :         119 :         relid = RelationGetRelid(rel);
   16696                 :         119 :         tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
   16697         [ +  - ]:         119 :         if (!HeapTupleIsValid(tuple))
   16698   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", relid);
   16699                 :             : 
   16700         [ +  + ]:         119 :         if (operation == AT_ReplaceRelOptions)
   16701                 :             :         {
   16702                 :             :                 /*
   16703                 :             :                  * If we're supposed to replace the reloptions list, we just pretend
   16704                 :             :                  * there were none before.
   16705                 :             :                  */
   16706                 :          30 :                 datum = (Datum) 0;
   16707                 :          30 :         }
   16708                 :             :         else
   16709                 :             :         {
   16710                 :          89 :                 bool            isnull;
   16711                 :             : 
   16712                 :             :                 /* Get the old reloptions */
   16713                 :          89 :                 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   16714                 :             :                                                                 &isnull);
   16715         [ +  + ]:          89 :                 if (isnull)
   16716                 :          53 :                         datum = (Datum) 0;
   16717                 :          89 :         }
   16718                 :             : 
   16719                 :             :         /* Generate new proposed reloptions (text array) */
   16720                 :         238 :         newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
   16721                 :         119 :                                                                          operation == AT_ResetRelOptions);
   16722                 :             : 
   16723                 :             :         /* Validate */
   16724   [ +  +  +  +  :         119 :         switch (rel->rd_rel->relkind)
                   -  - ]
   16725                 :             :         {
   16726                 :             :                 case RELKIND_RELATION:
   16727                 :             :                 case RELKIND_MATVIEW:
   16728                 :          64 :                         (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
   16729                 :          64 :                         break;
   16730                 :             :                 case RELKIND_PARTITIONED_TABLE:
   16731                 :           1 :                         (void) partitioned_table_reloptions(newOptions, true);
   16732                 :           1 :                         break;
   16733                 :             :                 case RELKIND_VIEW:
   16734                 :          48 :                         (void) view_reloptions(newOptions, true);
   16735                 :          48 :                         break;
   16736                 :             :                 case RELKIND_INDEX:
   16737                 :             :                 case RELKIND_PARTITIONED_INDEX:
   16738                 :           6 :                         (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
   16739                 :           6 :                         break;
   16740                 :           0 :                 case RELKIND_TOASTVALUE:
   16741                 :             :                         /* fall through to error -- shouldn't ever get here */
   16742                 :             :                 default:
   16743   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   16744                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16745                 :             :                                          errmsg("cannot set options for relation \"%s\"",
   16746                 :             :                                                         RelationGetRelationName(rel)),
   16747                 :             :                                          errdetail_relkind_not_supported(rel->rd_rel->relkind)));
   16748                 :           0 :                         break;
   16749                 :             :         }
   16750                 :             : 
   16751                 :             :         /* Special-case validation of view options */
   16752         [ +  + ]:         119 :         if (rel->rd_rel->relkind == RELKIND_VIEW)
   16753                 :             :         {
   16754                 :          45 :                 Query      *view_query = get_view_query(rel);
   16755                 :          45 :                 List       *view_options = untransformRelOptions(newOptions);
   16756                 :          45 :                 ListCell   *cell;
   16757                 :          45 :                 bool            check_option = false;
   16758                 :             : 
   16759   [ +  +  +  +  :          62 :                 foreach(cell, view_options)
                   +  + ]
   16760                 :             :                 {
   16761                 :          17 :                         DefElem    *defel = (DefElem *) lfirst(cell);
   16762                 :             : 
   16763         [ +  + ]:          17 :                         if (strcmp(defel->defname, "check_option") == 0)
   16764                 :           4 :                                 check_option = true;
   16765                 :          17 :                 }
   16766                 :             : 
   16767                 :             :                 /*
   16768                 :             :                  * If the check option is specified, look to see if the view is
   16769                 :             :                  * actually auto-updatable or not.
   16770                 :             :                  */
   16771         [ +  + ]:          45 :                 if (check_option)
   16772                 :             :                 {
   16773                 :           8 :                         const char *view_updatable_error =
   16774                 :           4 :                                 view_query_is_auto_updatable(view_query, true);
   16775                 :             : 
   16776         [ +  - ]:           4 :                         if (view_updatable_error)
   16777   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   16778                 :             :                                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16779                 :             :                                                  errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
   16780                 :             :                                                  errhint("%s", _(view_updatable_error))));
   16781                 :           4 :                 }
   16782                 :          45 :         }
   16783                 :             : 
   16784                 :             :         /*
   16785                 :             :          * All we need do here is update the pg_class row; the new options will be
   16786                 :             :          * propagated into relcaches during post-commit cache inval.
   16787                 :             :          */
   16788                 :         119 :         memset(repl_val, 0, sizeof(repl_val));
   16789                 :         119 :         memset(repl_null, false, sizeof(repl_null));
   16790                 :         119 :         memset(repl_repl, false, sizeof(repl_repl));
   16791                 :             : 
   16792         [ +  + ]:         119 :         if (newOptions != (Datum) 0)
   16793                 :          74 :                 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   16794                 :             :         else
   16795                 :          45 :                 repl_null[Anum_pg_class_reloptions - 1] = true;
   16796                 :             : 
   16797                 :         119 :         repl_repl[Anum_pg_class_reloptions - 1] = true;
   16798                 :             : 
   16799                 :         238 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   16800                 :         119 :                                                                  repl_val, repl_null, repl_repl);
   16801                 :             : 
   16802                 :         119 :         CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   16803                 :         119 :         UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
   16804                 :             : 
   16805         [ +  - ]:         119 :         InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   16806                 :             : 
   16807                 :         119 :         heap_freetuple(newtuple);
   16808                 :             : 
   16809                 :         119 :         ReleaseSysCache(tuple);
   16810                 :             : 
   16811                 :             :         /* repeat the whole exercise for the toast table, if there's one */
   16812         [ +  + ]:         119 :         if (OidIsValid(rel->rd_rel->reltoastrelid))
   16813                 :             :         {
   16814                 :          38 :                 Relation        toastrel;
   16815                 :          38 :                 Oid                     toastid = rel->rd_rel->reltoastrelid;
   16816                 :             : 
   16817                 :          38 :                 toastrel = table_open(toastid, lockmode);
   16818                 :             : 
   16819                 :             :                 /* Fetch heap tuple */
   16820                 :          38 :                 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
   16821         [ +  - ]:          38 :                 if (!HeapTupleIsValid(tuple))
   16822   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for relation %u", toastid);
   16823                 :             : 
   16824         [ -  + ]:          38 :                 if (operation == AT_ReplaceRelOptions)
   16825                 :             :                 {
   16826                 :             :                         /*
   16827                 :             :                          * If we're supposed to replace the reloptions list, we just
   16828                 :             :                          * pretend there were none before.
   16829                 :             :                          */
   16830                 :           0 :                         datum = (Datum) 0;
   16831                 :           0 :                 }
   16832                 :             :                 else
   16833                 :             :                 {
   16834                 :          38 :                         bool            isnull;
   16835                 :             : 
   16836                 :             :                         /* Get the old reloptions */
   16837                 :          38 :                         datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   16838                 :             :                                                                         &isnull);
   16839         [ +  + ]:          38 :                         if (isnull)
   16840                 :          32 :                                 datum = (Datum) 0;
   16841                 :          38 :                 }
   16842                 :             : 
   16843                 :          76 :                 newOptions = transformRelOptions(datum, defList, "toast", validnsps,
   16844                 :          38 :                                                                                  false, operation == AT_ResetRelOptions);
   16845                 :             : 
   16846                 :          38 :                 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
   16847                 :             : 
   16848                 :          38 :                 memset(repl_val, 0, sizeof(repl_val));
   16849                 :          38 :                 memset(repl_null, false, sizeof(repl_null));
   16850                 :          38 :                 memset(repl_repl, false, sizeof(repl_repl));
   16851                 :             : 
   16852         [ +  + ]:          38 :                 if (newOptions != (Datum) 0)
   16853                 :           7 :                         repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   16854                 :             :                 else
   16855                 :          31 :                         repl_null[Anum_pg_class_reloptions - 1] = true;
   16856                 :             : 
   16857                 :          38 :                 repl_repl[Anum_pg_class_reloptions - 1] = true;
   16858                 :             : 
   16859                 :          76 :                 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   16860                 :          38 :                                                                          repl_val, repl_null, repl_repl);
   16861                 :             : 
   16862                 :          38 :                 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   16863                 :             : 
   16864         [ -  + ]:          38 :                 InvokeObjectPostAlterHookArg(RelationRelationId,
   16865                 :             :                                                                          RelationGetRelid(toastrel), 0,
   16866                 :             :                                                                          InvalidOid, true);
   16867                 :             : 
   16868                 :          38 :                 heap_freetuple(newtuple);
   16869                 :             : 
   16870                 :          38 :                 ReleaseSysCache(tuple);
   16871                 :             : 
   16872                 :          38 :                 table_close(toastrel, NoLock);
   16873                 :          38 :         }
   16874                 :             : 
   16875                 :         119 :         table_close(pgclass, RowExclusiveLock);
   16876         [ -  + ]:         119 : }
   16877                 :             : 
   16878                 :             : /*
   16879                 :             :  * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
   16880                 :             :  * rewriting to be done, so we just want to copy the data as fast as possible.
   16881                 :             :  */
   16882                 :             : static void
   16883                 :          25 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
   16884                 :             : {
   16885                 :          25 :         Relation        rel;
   16886                 :          25 :         Oid                     reltoastrelid;
   16887                 :          25 :         RelFileNumber newrelfilenumber;
   16888                 :          25 :         RelFileLocator newrlocator;
   16889                 :          25 :         List       *reltoastidxids = NIL;
   16890                 :          25 :         ListCell   *lc;
   16891                 :             : 
   16892                 :             :         /*
   16893                 :             :          * Need lock here in case we are recursing to toast table or index
   16894                 :             :          */
   16895                 :          25 :         rel = relation_open(tableOid, lockmode);
   16896                 :             : 
   16897                 :             :         /* Check first if relation can be moved to new tablespace */
   16898         [ +  + ]:          25 :         if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   16899                 :             :         {
   16900         [ +  - ]:           1 :                 InvokeObjectPostAlterHook(RelationRelationId,
   16901                 :             :                                                                   RelationGetRelid(rel), 0);
   16902                 :           1 :                 relation_close(rel, NoLock);
   16903                 :           1 :                 return;
   16904                 :             :         }
   16905                 :             : 
   16906                 :          24 :         reltoastrelid = rel->rd_rel->reltoastrelid;
   16907                 :             :         /* Fetch the list of indexes on toast relation if necessary */
   16908         [ +  + ]:          24 :         if (OidIsValid(reltoastrelid))
   16909                 :             :         {
   16910                 :           3 :                 Relation        toastRel = relation_open(reltoastrelid, lockmode);
   16911                 :             : 
   16912                 :           3 :                 reltoastidxids = RelationGetIndexList(toastRel);
   16913                 :           3 :                 relation_close(toastRel, lockmode);
   16914                 :           3 :         }
   16915                 :             : 
   16916                 :             :         /*
   16917                 :             :          * Relfilenumbers are not unique in databases across tablespaces, so we
   16918                 :             :          * need to allocate a new one in the new tablespace.
   16919                 :             :          */
   16920                 :          48 :         newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
   16921                 :          24 :                                                                                    rel->rd_rel->relpersistence);
   16922                 :             : 
   16923                 :             :         /* Open old and new relation */
   16924                 :          24 :         newrlocator = rel->rd_locator;
   16925                 :          24 :         newrlocator.relNumber = newrelfilenumber;
   16926                 :          24 :         newrlocator.spcOid = newTableSpace;
   16927                 :             : 
   16928                 :             :         /* hand off to AM to actually create new rel storage and copy the data */
   16929         [ +  + ]:          24 :         if (rel->rd_rel->relkind == RELKIND_INDEX)
   16930                 :             :         {
   16931                 :          10 :                 index_copy_data(rel, newrlocator);
   16932                 :          10 :         }
   16933                 :             :         else
   16934                 :             :         {
   16935   [ +  +  +  +  :          14 :                 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
                   +  - ]
   16936                 :          14 :                 table_relation_copy_data(rel, &newrlocator);
   16937                 :             :         }
   16938                 :             : 
   16939                 :             :         /*
   16940                 :             :          * Update the pg_class row.
   16941                 :             :          *
   16942                 :             :          * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
   16943                 :             :          * executed on pg_class or its indexes (the above copy wouldn't contain
   16944                 :             :          * the updated pg_class entry), but that's forbidden with
   16945                 :             :          * CheckRelationTableSpaceMove().
   16946                 :             :          */
   16947                 :          24 :         SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
   16948                 :             : 
   16949         [ +  - ]:          24 :         InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   16950                 :             : 
   16951                 :          24 :         RelationAssumeNewRelfilelocator(rel);
   16952                 :             : 
   16953                 :          24 :         relation_close(rel, NoLock);
   16954                 :             : 
   16955                 :             :         /* Make sure the reltablespace change is visible */
   16956                 :          24 :         CommandCounterIncrement();
   16957                 :             : 
   16958                 :             :         /* Move associated toast relation and/or indexes, too */
   16959         [ +  + ]:          24 :         if (OidIsValid(reltoastrelid))
   16960                 :           3 :                 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
   16961   [ +  +  +  +  :          27 :         foreach(lc, reltoastidxids)
                   +  + ]
   16962                 :           3 :                 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
   16963                 :             : 
   16964                 :             :         /* Clean up */
   16965                 :          24 :         list_free(reltoastidxids);
   16966         [ -  + ]:          25 : }
   16967                 :             : 
   16968                 :             : /*
   16969                 :             :  * Special handling of ALTER TABLE SET TABLESPACE for relations with no
   16970                 :             :  * storage that have an interest in preserving tablespace.
   16971                 :             :  *
   16972                 :             :  * Since these have no storage the tablespace can be updated with a simple
   16973                 :             :  * metadata only operation to update the tablespace.
   16974                 :             :  */
   16975                 :             : static void
   16976                 :           5 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
   16977                 :             : {
   16978                 :             :         /*
   16979                 :             :          * Shouldn't be called on relations having storage; these are processed in
   16980                 :             :          * phase 3.
   16981                 :             :          */
   16982         [ +  - ]:           5 :         Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   16983                 :             : 
   16984                 :             :         /* check if relation can be moved to its new tablespace */
   16985         [ +  - ]:           5 :         if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   16986                 :             :         {
   16987         [ #  # ]:           0 :                 InvokeObjectPostAlterHook(RelationRelationId,
   16988                 :             :                                                                   RelationGetRelid(rel),
   16989                 :             :                                                                   0);
   16990                 :           0 :                 return;
   16991                 :             :         }
   16992                 :             : 
   16993                 :             :         /* Update can be done, so change reltablespace */
   16994                 :           5 :         SetRelationTableSpace(rel, newTableSpace, InvalidOid);
   16995                 :             : 
   16996         [ +  - ]:           5 :         InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   16997                 :             : 
   16998                 :             :         /* Make sure the reltablespace change is visible */
   16999                 :           5 :         CommandCounterIncrement();
   17000                 :           5 : }
   17001                 :             : 
   17002                 :             : /*
   17003                 :             :  * Alter Table ALL ... SET TABLESPACE
   17004                 :             :  *
   17005                 :             :  * Allows a user to move all objects of some type in a given tablespace in the
   17006                 :             :  * current database to another tablespace.  Objects can be chosen based on the
   17007                 :             :  * owner of the object also, to allow users to move only their objects.
   17008                 :             :  * The user must have CREATE rights on the new tablespace, as usual.   The main
   17009                 :             :  * permissions handling is done by the lower-level table move function.
   17010                 :             :  *
   17011                 :             :  * All to-be-moved objects are locked first. If NOWAIT is specified and the
   17012                 :             :  * lock can't be acquired then we ereport(ERROR).
   17013                 :             :  */
   17014                 :             : Oid
   17015                 :           5 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
   17016                 :             : {
   17017                 :           5 :         List       *relations = NIL;
   17018                 :           5 :         ListCell   *l;
   17019                 :           5 :         ScanKeyData key[1];
   17020                 :           5 :         Relation        rel;
   17021                 :           5 :         TableScanDesc scan;
   17022                 :           5 :         HeapTuple       tuple;
   17023                 :           5 :         Oid                     orig_tablespaceoid;
   17024                 :           5 :         Oid                     new_tablespaceoid;
   17025                 :           5 :         List       *role_oids = roleSpecsToIds(stmt->roles);
   17026                 :             : 
   17027                 :             :         /* Ensure we were not asked to move something we can't */
   17028   [ +  +  +  +  :           5 :         if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
                   +  - ]
   17029                 :           2 :                 stmt->objtype != OBJECT_MATVIEW)
   17030   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   17031                 :             :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   17032                 :             :                                  errmsg("only tables, indexes, and materialized views exist in tablespaces")));
   17033                 :             : 
   17034                 :             :         /* Get the orig and new tablespace OIDs */
   17035                 :           5 :         orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
   17036                 :           5 :         new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
   17037                 :             : 
   17038                 :             :         /* Can't move shared relations in to or out of pg_global */
   17039                 :             :         /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
   17040         [ +  - ]:           5 :         if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
   17041                 :           5 :                 new_tablespaceoid == GLOBALTABLESPACE_OID)
   17042   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   17043                 :             :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   17044                 :             :                                  errmsg("cannot move relations in to or out of pg_global tablespace")));
   17045                 :             : 
   17046                 :             :         /*
   17047                 :             :          * Must have CREATE rights on the new tablespace, unless it is the
   17048                 :             :          * database default tablespace (which all users implicitly have CREATE
   17049                 :             :          * rights on).
   17050                 :             :          */
   17051   [ +  -  +  - ]:           5 :         if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
   17052                 :             :         {
   17053                 :           0 :                 AclResult       aclresult;
   17054                 :             : 
   17055                 :           0 :                 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
   17056                 :             :                                                                         ACL_CREATE);
   17057         [ #  # ]:           0 :                 if (aclresult != ACLCHECK_OK)
   17058                 :           0 :                         aclcheck_error(aclresult, OBJECT_TABLESPACE,
   17059                 :           0 :                                                    get_tablespace_name(new_tablespaceoid));
   17060                 :           0 :         }
   17061                 :             : 
   17062                 :             :         /*
   17063                 :             :          * Now that the checks are done, check if we should set either to
   17064                 :             :          * InvalidOid because it is our database's default tablespace.
   17065                 :             :          */
   17066         [ +  - ]:           5 :         if (orig_tablespaceoid == MyDatabaseTableSpace)
   17067                 :           0 :                 orig_tablespaceoid = InvalidOid;
   17068                 :             : 
   17069         [ -  + ]:           5 :         if (new_tablespaceoid == MyDatabaseTableSpace)
   17070                 :           5 :                 new_tablespaceoid = InvalidOid;
   17071                 :             : 
   17072                 :             :         /* no-op */
   17073         [ -  + ]:           5 :         if (orig_tablespaceoid == new_tablespaceoid)
   17074                 :           0 :                 return new_tablespaceoid;
   17075                 :             : 
   17076                 :             :         /*
   17077                 :             :          * Walk the list of objects in the tablespace and move them. This will
   17078                 :             :          * only find objects in our database, of course.
   17079                 :             :          */
   17080                 :          10 :         ScanKeyInit(&key[0],
   17081                 :             :                                 Anum_pg_class_reltablespace,
   17082                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   17083                 :           5 :                                 ObjectIdGetDatum(orig_tablespaceoid));
   17084                 :             : 
   17085                 :           5 :         rel = table_open(RelationRelationId, AccessShareLock);
   17086                 :           5 :         scan = table_beginscan_catalog(rel, 1, key);
   17087         [ +  + ]:          22 :         while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
   17088                 :             :         {
   17089                 :          17 :                 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
   17090                 :          17 :                 Oid                     relOid = relForm->oid;
   17091                 :             : 
   17092                 :             :                 /*
   17093                 :             :                  * Do not move objects in pg_catalog as part of this, if an admin
   17094                 :             :                  * really wishes to do so, they can issue the individual ALTER
   17095                 :             :                  * commands directly.
   17096                 :             :                  *
   17097                 :             :                  * Also, explicitly avoid any shared tables, temp tables, or TOAST
   17098                 :             :                  * (TOAST will be moved with the main table).
   17099                 :             :                  */
   17100         [ +  - ]:          17 :                 if (IsCatalogNamespace(relForm->relnamespace) ||
   17101         [ +  - ]:          17 :                         relForm->relisshared ||
   17102   [ +  -  -  + ]:          17 :                         isAnyTempNamespace(relForm->relnamespace) ||
   17103                 :          17 :                         IsToastNamespace(relForm->relnamespace))
   17104                 :           0 :                         continue;
   17105                 :             : 
   17106                 :             :                 /* Only move the object type requested */
   17107         [ +  + ]:          17 :                 if ((stmt->objtype == OBJECT_TABLE &&
   17108         [ +  + ]:          10 :                          relForm->relkind != RELKIND_RELATION &&
   17109                 :           6 :                          relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
   17110         [ +  + ]:          11 :                         (stmt->objtype == OBJECT_INDEX &&
   17111         [ +  + ]:           6 :                          relForm->relkind != RELKIND_INDEX &&
   17112         [ -  + ]:           2 :                          relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
   17113         [ +  + ]:          10 :                         (stmt->objtype == OBJECT_MATVIEW &&
   17114                 :           1 :                          relForm->relkind != RELKIND_MATVIEW))
   17115                 :           7 :                         continue;
   17116                 :             : 
   17117                 :             :                 /* Check if we are only moving objects owned by certain roles */
   17118   [ -  +  #  # ]:          10 :                 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
   17119                 :           0 :                         continue;
   17120                 :             : 
   17121                 :             :                 /*
   17122                 :             :                  * Handle permissions-checking here since we are locking the tables
   17123                 :             :                  * and also to avoid doing a bunch of work only to fail part-way. Note
   17124                 :             :                  * that permissions will also be checked by AlterTableInternal().
   17125                 :             :                  *
   17126                 :             :                  * Caller must be considered an owner on the table to move it.
   17127                 :             :                  */
   17128         [ +  - ]:          10 :                 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
   17129                 :           0 :                         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
   17130                 :           0 :                                                    NameStr(relForm->relname));
   17131                 :             : 
   17132   [ -  +  #  # ]:          10 :                 if (stmt->nowait &&
   17133                 :           0 :                         !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
   17134   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   17135                 :             :                                         (errcode(ERRCODE_OBJECT_IN_USE),
   17136                 :             :                                          errmsg("aborting because lock on relation \"%s.%s\" is not available",
   17137                 :             :                                                         get_namespace_name(relForm->relnamespace),
   17138                 :             :                                                         NameStr(relForm->relname))));
   17139                 :             :                 else
   17140                 :          10 :                         LockRelationOid(relOid, AccessExclusiveLock);
   17141                 :             : 
   17142                 :             :                 /* Add to our list of objects to move */
   17143                 :          10 :                 relations = lappend_oid(relations, relOid);
   17144      [ -  +  + ]:          17 :         }
   17145                 :             : 
   17146                 :           5 :         table_endscan(scan);
   17147                 :           5 :         table_close(rel, AccessShareLock);
   17148                 :             : 
   17149         [ +  + ]:           5 :         if (relations == NIL)
   17150   [ -  +  +  -  :           2 :                 ereport(NOTICE,
                   +  - ]
   17151                 :             :                                 (errcode(ERRCODE_NO_DATA_FOUND),
   17152                 :             :                                  errmsg("no matching relations in tablespace \"%s\" found",
   17153                 :             :                                                 orig_tablespaceoid == InvalidOid ? "(database default)" :
   17154                 :             :                                                 get_tablespace_name(orig_tablespaceoid))));
   17155                 :             : 
   17156                 :             :         /* Everything is locked, loop through and move all of the relations. */
   17157   [ +  +  +  +  :          15 :         foreach(l, relations)
                   +  + ]
   17158                 :             :         {
   17159                 :          10 :                 List       *cmds = NIL;
   17160                 :          10 :                 AlterTableCmd *cmd = makeNode(AlterTableCmd);
   17161                 :             : 
   17162                 :          10 :                 cmd->subtype = AT_SetTableSpace;
   17163                 :          10 :                 cmd->name = stmt->new_tablespacename;
   17164                 :             : 
   17165                 :          10 :                 cmds = lappend(cmds, cmd);
   17166                 :             : 
   17167                 :          10 :                 EventTriggerAlterTableStart((Node *) stmt);
   17168                 :             :                 /* OID is set by AlterTableInternal */
   17169                 :          10 :                 AlterTableInternal(lfirst_oid(l), cmds, false);
   17170                 :          10 :                 EventTriggerAlterTableEnd();
   17171                 :          10 :         }
   17172                 :             : 
   17173                 :           5 :         return new_tablespaceoid;
   17174                 :           5 : }
   17175                 :             : 
   17176                 :             : static void
   17177                 :          10 : index_copy_data(Relation rel, RelFileLocator newrlocator)
   17178                 :             : {
   17179                 :          10 :         SMgrRelation dstrel;
   17180                 :             : 
   17181                 :             :         /*
   17182                 :             :          * Since we copy the file directly without looking at the shared buffers,
   17183                 :             :          * we'd better first flush out any pages of the source relation that are
   17184                 :             :          * in shared buffers.  We assume no new changes will be made while we are
   17185                 :             :          * holding exclusive lock on the rel.
   17186                 :             :          */
   17187                 :          10 :         FlushRelationBuffers(rel);
   17188                 :             : 
   17189                 :             :         /*
   17190                 :             :          * Create and copy all forks of the relation, and schedule unlinking of
   17191                 :             :          * old physical files.
   17192                 :             :          *
   17193                 :             :          * NOTE: any conflict in relfilenumber value will be caught in
   17194                 :             :          * RelationCreateStorage().
   17195                 :             :          */
   17196                 :          10 :         dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
   17197                 :             : 
   17198                 :             :         /* copy main fork */
   17199                 :          20 :         RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
   17200                 :          10 :                                                 rel->rd_rel->relpersistence);
   17201                 :             : 
   17202                 :             :         /* copy those extra forks that exist */
   17203         [ +  + ]:          40 :         for (ForkNumber forkNum = MAIN_FORKNUM + 1;
   17204                 :          40 :                  forkNum <= MAX_FORKNUM; forkNum++)
   17205                 :             :         {
   17206         [ +  - ]:          30 :                 if (smgrexists(RelationGetSmgr(rel), forkNum))
   17207                 :             :                 {
   17208                 :           0 :                         smgrcreate(dstrel, forkNum, false);
   17209                 :             : 
   17210                 :             :                         /*
   17211                 :             :                          * WAL log creation if the relation is persistent, or this is the
   17212                 :             :                          * init fork of an unlogged relation.
   17213                 :             :                          */
   17214   [ #  #  #  # ]:           0 :                         if (RelationIsPermanent(rel) ||
   17215         [ #  # ]:           0 :                                 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
   17216                 :           0 :                                  forkNum == INIT_FORKNUM))
   17217                 :           0 :                                 log_smgrcreate(&newrlocator, forkNum);
   17218                 :           0 :                         RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
   17219                 :           0 :                                                                 rel->rd_rel->relpersistence);
   17220                 :           0 :                 }
   17221                 :          30 :         }
   17222                 :             : 
   17223                 :             :         /* drop old relation, and close new one */
   17224                 :          10 :         RelationDropStorage(rel);
   17225                 :          10 :         smgrclose(dstrel);
   17226                 :          10 : }
   17227                 :             : 
   17228                 :             : /*
   17229                 :             :  * ALTER TABLE ENABLE/DISABLE TRIGGER
   17230                 :             :  *
   17231                 :             :  * We just pass this off to trigger.c.
   17232                 :             :  */
   17233                 :             : static void
   17234                 :          20 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
   17235                 :             :                                                    char fires_when, bool skip_system, bool recurse,
   17236                 :             :                                                    LOCKMODE lockmode)
   17237                 :             : {
   17238                 :          40 :         EnableDisableTrigger(rel, trigname, InvalidOid,
   17239                 :          20 :                                                  fires_when, skip_system, recurse,
   17240                 :          20 :                                                  lockmode);
   17241                 :             : 
   17242         [ +  - ]:          20 :         InvokeObjectPostAlterHook(RelationRelationId,
   17243                 :             :                                                           RelationGetRelid(rel), 0);
   17244                 :          20 : }
   17245                 :             : 
   17246                 :             : /*
   17247                 :             :  * ALTER TABLE ENABLE/DISABLE RULE
   17248                 :             :  *
   17249                 :             :  * We just pass this off to rewriteDefine.c.
   17250                 :             :  */
   17251                 :             : static void
   17252                 :           6 : ATExecEnableDisableRule(Relation rel, const char *rulename,
   17253                 :             :                                                 char fires_when, LOCKMODE lockmode)
   17254                 :             : {
   17255                 :           6 :         EnableDisableRule(rel, rulename, fires_when);
   17256                 :             : 
   17257         [ +  - ]:           6 :         InvokeObjectPostAlterHook(RelationRelationId,
   17258                 :             :                                                           RelationGetRelid(rel), 0);
   17259                 :           6 : }
   17260                 :             : 
   17261                 :             : /*
   17262                 :             :  * ALTER TABLE INHERIT
   17263                 :             :  *
   17264                 :             :  * Add a parent to the child's parents. This verifies that all the columns and
   17265                 :             :  * check constraints of the parent appear in the child and that they have the
   17266                 :             :  * same data types and expressions.
   17267                 :             :  */
   17268                 :             : static void
   17269                 :          53 : ATPrepAddInherit(Relation child_rel)
   17270                 :             : {
   17271         [ +  + ]:          53 :         if (child_rel->rd_rel->reloftype)
   17272   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   17273                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17274                 :             :                                  errmsg("cannot change inheritance of typed table")));
   17275                 :             : 
   17276         [ +  + ]:          52 :         if (child_rel->rd_rel->relispartition)
   17277   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   17278                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17279                 :             :                                  errmsg("cannot change inheritance of a partition")));
   17280                 :             : 
   17281         [ +  + ]:          51 :         if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   17282   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   17283                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17284                 :             :                                  errmsg("cannot change inheritance of partitioned table")));
   17285                 :          50 : }
   17286                 :             : 
   17287                 :             : /*
   17288                 :             :  * Return the address of the new parent relation.
   17289                 :             :  */
   17290                 :             : static ObjectAddress
   17291                 :          35 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
   17292                 :             : {
   17293                 :          35 :         Relation        parent_rel;
   17294                 :          35 :         List       *children;
   17295                 :             :         ObjectAddress address;
   17296                 :          35 :         const char *trigger_name;
   17297                 :             : 
   17298                 :             :         /*
   17299                 :             :          * A self-exclusive lock is needed here.  See the similar case in
   17300                 :             :          * MergeAttributes() for a full explanation.
   17301                 :             :          */
   17302                 :          35 :         parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
   17303                 :             : 
   17304                 :             :         /*
   17305                 :             :          * Must be owner of both parent and child -- child was checked by
   17306                 :             :          * ATSimplePermissions call in ATPrepCmd
   17307                 :             :          */
   17308                 :          35 :         ATSimplePermissions(AT_AddInherit, parent_rel,
   17309                 :             :                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   17310                 :             : 
   17311                 :             :         /* Permanent rels cannot inherit from temporary ones */
   17312   [ +  +  +  - ]:          35 :         if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   17313                 :           1 :                 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   17314   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   17315                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17316                 :             :                                  errmsg("cannot inherit from temporary relation \"%s\"",
   17317                 :             :                                                 RelationGetRelationName(parent_rel))));
   17318                 :             : 
   17319                 :             :         /* If parent rel is temp, it must belong to this session */
   17320   [ +  +  +  - ]:          35 :         if (RELATION_IS_OTHER_TEMP(parent_rel))
   17321   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   17322                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17323                 :             :                                  errmsg("cannot inherit from temporary relation of another session")));
   17324                 :             : 
   17325                 :             :         /* Ditto for the child */
   17326   [ +  +  +  - ]:          35 :         if (RELATION_IS_OTHER_TEMP(child_rel))
   17327   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   17328                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17329                 :             :                                  errmsg("cannot inherit to temporary relation of another session")));
   17330                 :             : 
   17331                 :             :         /* Prevent partitioned tables from becoming inheritance parents */
   17332         [ +  + ]:          35 :         if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   17333   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   17334                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17335                 :             :                                  errmsg("cannot inherit from partitioned table \"%s\"",
   17336                 :             :                                                 parent->relname)));
   17337                 :             : 
   17338                 :             :         /* Likewise for partitions */
   17339         [ +  + ]:          34 :         if (parent_rel->rd_rel->relispartition)
   17340   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   17341                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17342                 :             :                                  errmsg("cannot inherit from a partition")));
   17343                 :             : 
   17344                 :             :         /*
   17345                 :             :          * Prevent circularity by seeing if proposed parent inherits from child.
   17346                 :             :          * (In particular, this disallows making a rel inherit from itself.)
   17347                 :             :          *
   17348                 :             :          * This is not completely bulletproof because of race conditions: in
   17349                 :             :          * multi-level inheritance trees, someone else could concurrently be
   17350                 :             :          * making another inheritance link that closes the loop but does not join
   17351                 :             :          * either of the rels we have locked.  Preventing that seems to require
   17352                 :             :          * exclusive locks on the entire inheritance tree, which is a cure worse
   17353                 :             :          * than the disease.  find_all_inheritors() will cope with circularity
   17354                 :             :          * anyway, so don't sweat it too much.
   17355                 :             :          *
   17356                 :             :          * We use weakest lock we can on child's children, namely AccessShareLock.
   17357                 :             :          */
   17358                 :          33 :         children = find_all_inheritors(RelationGetRelid(child_rel),
   17359                 :             :                                                                    AccessShareLock, NULL);
   17360                 :             : 
   17361         [ +  + ]:          33 :         if (list_member_oid(children, RelationGetRelid(parent_rel)))
   17362   [ +  -  +  - ]:           2 :                 ereport(ERROR,
   17363                 :             :                                 (errcode(ERRCODE_DUPLICATE_TABLE),
   17364                 :             :                                  errmsg("circular inheritance not allowed"),
   17365                 :             :                                  errdetail("\"%s\" is already a child of \"%s\".",
   17366                 :             :                                                    parent->relname,
   17367                 :             :                                                    RelationGetRelationName(child_rel))));
   17368                 :             : 
   17369                 :             :         /*
   17370                 :             :          * If child_rel has row-level triggers with transition tables, we
   17371                 :             :          * currently don't allow it to become an inheritance child.  See also
   17372                 :             :          * prohibitions in ATExecAttachPartition() and CreateTrigger().
   17373                 :             :          */
   17374                 :          31 :         trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
   17375         [ +  + ]:          31 :         if (trigger_name != NULL)
   17376   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   17377                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17378                 :             :                                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
   17379                 :             :                                                 trigger_name, RelationGetRelationName(child_rel)),
   17380                 :             :                                  errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
   17381                 :             : 
   17382                 :             :         /* OK to create inheritance */
   17383                 :          30 :         CreateInheritance(child_rel, parent_rel, false);
   17384                 :             : 
   17385                 :          30 :         ObjectAddressSet(address, RelationRelationId,
   17386                 :             :                                          RelationGetRelid(parent_rel));
   17387                 :             : 
   17388                 :             :         /* keep our lock on the parent relation until commit */
   17389                 :          30 :         table_close(parent_rel, NoLock);
   17390                 :             : 
   17391                 :             :         return address;
   17392                 :          30 : }
   17393                 :             : 
   17394                 :             : /*
   17395                 :             :  * CreateInheritance
   17396                 :             :  *              Catalog manipulation portion of creating inheritance between a child
   17397                 :             :  *              table and a parent table.
   17398                 :             :  *
   17399                 :             :  * Common to ATExecAddInherit() and ATExecAttachPartition().
   17400                 :             :  */
   17401                 :             : static void
   17402                 :         422 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
   17403                 :             : {
   17404                 :         422 :         Relation        catalogRelation;
   17405                 :         422 :         SysScanDesc scan;
   17406                 :         422 :         ScanKeyData key;
   17407                 :         422 :         HeapTuple       inheritsTuple;
   17408                 :         422 :         int32           inhseqno;
   17409                 :             : 
   17410                 :             :         /* Note: get RowExclusiveLock because we will write pg_inherits below. */
   17411                 :         422 :         catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   17412                 :             : 
   17413                 :             :         /*
   17414                 :             :          * Check for duplicates in the list of parents, and determine the highest
   17415                 :             :          * inhseqno already present; we'll use the next one for the new parent.
   17416                 :             :          * Also, if proposed child is a partition, it cannot already be
   17417                 :             :          * inheriting.
   17418                 :             :          *
   17419                 :             :          * Note: we do not reject the case where the child already inherits from
   17420                 :             :          * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
   17421                 :             :          */
   17422                 :         422 :         ScanKeyInit(&key,
   17423                 :             :                                 Anum_pg_inherits_inhrelid,
   17424                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   17425                 :         422 :                                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   17426                 :         422 :         scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
   17427                 :             :                                                           true, NULL, 1, &key);
   17428                 :             : 
   17429                 :             :         /* inhseqno sequences start at 1 */
   17430                 :         422 :         inhseqno = 0;
   17431         [ +  + ]:         430 :         while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   17432                 :             :         {
   17433                 :           9 :                 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   17434                 :             : 
   17435         [ +  + ]:           9 :                 if (inh->inhparent == RelationGetRelid(parent_rel))
   17436   [ +  -  +  - ]:           1 :                         ereport(ERROR,
   17437                 :             :                                         (errcode(ERRCODE_DUPLICATE_TABLE),
   17438                 :             :                                          errmsg("relation \"%s\" would be inherited from more than once",
   17439                 :             :                                                         RelationGetRelationName(parent_rel))));
   17440                 :             : 
   17441         [ -  + ]:           8 :                 if (inh->inhseqno > inhseqno)
   17442                 :           8 :                         inhseqno = inh->inhseqno;
   17443                 :           8 :         }
   17444                 :         421 :         systable_endscan(scan);
   17445                 :             : 
   17446                 :             :         /* Match up the columns and bump attinhcount as needed */
   17447                 :         421 :         MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
   17448                 :             : 
   17449                 :             :         /* Match up the constraints and bump coninhcount as needed */
   17450                 :         421 :         MergeConstraintsIntoExisting(child_rel, parent_rel);
   17451                 :             : 
   17452                 :             :         /*
   17453                 :             :          * OK, it looks valid.  Make the catalog entries that show inheritance.
   17454                 :             :          */
   17455                 :         842 :         StoreCatalogInheritance1(RelationGetRelid(child_rel),
   17456                 :         421 :                                                          RelationGetRelid(parent_rel),
   17457                 :         421 :                                                          inhseqno + 1,
   17458                 :         421 :                                                          catalogRelation,
   17459                 :         421 :                                                          parent_rel->rd_rel->relkind ==
   17460                 :             :                                                          RELKIND_PARTITIONED_TABLE);
   17461                 :             : 
   17462                 :             :         /* Now we're done with pg_inherits */
   17463                 :         421 :         table_close(catalogRelation, RowExclusiveLock);
   17464                 :         421 : }
   17465                 :             : 
   17466                 :             : /*
   17467                 :             :  * Obtain the source-text form of the constraint expression for a check
   17468                 :             :  * constraint, given its pg_constraint tuple
   17469                 :             :  */
   17470                 :             : static char *
   17471                 :          62 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
   17472                 :             : {
   17473                 :          62 :         Form_pg_constraint con;
   17474                 :          62 :         bool            isnull;
   17475                 :          62 :         Datum           attr;
   17476                 :          62 :         Datum           expr;
   17477                 :             : 
   17478                 :          62 :         con = (Form_pg_constraint) GETSTRUCT(contup);
   17479                 :          62 :         attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
   17480         [ +  - ]:          62 :         if (isnull)
   17481   [ #  #  #  # ]:           0 :                 elog(ERROR, "null conbin for constraint %u", con->oid);
   17482                 :             : 
   17483                 :          62 :         expr = DirectFunctionCall2(pg_get_expr, attr,
   17484                 :             :                                                            ObjectIdGetDatum(con->conrelid));
   17485                 :         124 :         return TextDatumGetCString(expr);
   17486                 :          62 : }
   17487                 :             : 
   17488                 :             : /*
   17489                 :             :  * Determine whether two check constraints are functionally equivalent
   17490                 :             :  *
   17491                 :             :  * The test we apply is to see whether they reverse-compile to the same
   17492                 :             :  * source string.  This insulates us from issues like whether attributes
   17493                 :             :  * have the same physical column numbers in parent and child relations.
   17494                 :             :  *
   17495                 :             :  * Note that we ignore enforceability as there are cases where constraints
   17496                 :             :  * with differing enforceability are allowed.
   17497                 :             :  */
   17498                 :             : static bool
   17499                 :          31 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
   17500                 :             : {
   17501                 :          31 :         Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
   17502                 :          31 :         Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
   17503                 :             : 
   17504         [ +  - ]:          31 :         if (acon->condeferrable != bcon->condeferrable ||
   17505   [ +  -  +  + ]:          31 :                 acon->condeferred != bcon->condeferred ||
   17506                 :          62 :                 strcmp(decompile_conbin(a, tupleDesc),
   17507                 :          62 :                            decompile_conbin(b, tupleDesc)) != 0)
   17508                 :           1 :                 return false;
   17509                 :             :         else
   17510                 :          30 :                 return true;
   17511                 :          31 : }
   17512                 :             : 
   17513                 :             : /*
   17514                 :             :  * Check columns in child table match up with columns in parent, and increment
   17515                 :             :  * their attinhcount.
   17516                 :             :  *
   17517                 :             :  * Called by CreateInheritance
   17518                 :             :  *
   17519                 :             :  * Currently all parent columns must be found in child. Missing columns are an
   17520                 :             :  * error.  One day we might consider creating new columns like CREATE TABLE
   17521                 :             :  * does.  However, that is widely unpopular --- in the common use case of
   17522                 :             :  * partitioned tables it's a foot-gun.
   17523                 :             :  *
   17524                 :             :  * The data type must match exactly. If the parent column is NOT NULL then
   17525                 :             :  * the child must be as well. Defaults are not compared, however.
   17526                 :             :  */
   17527                 :             : static void
   17528                 :         421 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
   17529                 :             : {
   17530                 :         421 :         Relation        attrrel;
   17531                 :         421 :         TupleDesc       parent_desc;
   17532                 :             : 
   17533                 :         421 :         attrrel = table_open(AttributeRelationId, RowExclusiveLock);
   17534                 :         421 :         parent_desc = RelationGetDescr(parent_rel);
   17535                 :             : 
   17536         [ +  + ]:        1389 :         for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
   17537                 :             :         {
   17538                 :         990 :                 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
   17539                 :         990 :                 char       *parent_attname = NameStr(parent_att->attname);
   17540                 :         990 :                 HeapTuple       tuple;
   17541                 :             : 
   17542                 :             :                 /* Ignore dropped columns in the parent. */
   17543         [ +  + ]:         990 :                 if (parent_att->attisdropped)
   17544                 :          32 :                         continue;
   17545                 :             : 
   17546                 :             :                 /* Find same column in child (matching on column name). */
   17547                 :         958 :                 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
   17548         [ +  + ]:         958 :                 if (HeapTupleIsValid(tuple))
   17549                 :             :                 {
   17550                 :         956 :                         Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
   17551                 :             : 
   17552         [ +  + ]:         956 :                         if (parent_att->atttypid != child_att->atttypid ||
   17553                 :         954 :                                 parent_att->atttypmod != child_att->atttypmod)
   17554   [ +  -  +  - ]:           2 :                                 ereport(ERROR,
   17555                 :             :                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
   17556                 :             :                                                  errmsg("child table \"%s\" has different type for column \"%s\"",
   17557                 :             :                                                                 RelationGetRelationName(child_rel), parent_attname)));
   17558                 :             : 
   17559         [ +  + ]:         954 :                         if (parent_att->attcollation != child_att->attcollation)
   17560   [ +  -  +  - ]:           1 :                                 ereport(ERROR,
   17561                 :             :                                                 (errcode(ERRCODE_COLLATION_MISMATCH),
   17562                 :             :                                                  errmsg("child table \"%s\" has different collation for column \"%s\"",
   17563                 :             :                                                                 RelationGetRelationName(child_rel), parent_attname)));
   17564                 :             : 
   17565                 :             :                         /*
   17566                 :             :                          * If the parent has a not-null constraint that's not NO INHERIT,
   17567                 :             :                          * make sure the child has one too.
   17568                 :             :                          *
   17569                 :             :                          * Other constraints are checked elsewhere.
   17570                 :             :                          */
   17571   [ +  +  +  + ]:         953 :                         if (parent_att->attnotnull && !child_att->attnotnull)
   17572                 :             :                         {
   17573                 :           8 :                                 HeapTuple       contup;
   17574                 :             : 
   17575                 :          16 :                                 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
   17576                 :           8 :                                                                                                          parent_att->attnum);
   17577   [ +  -  +  + ]:           8 :                                 if (HeapTupleIsValid(contup) &&
   17578                 :           8 :                                         !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
   17579   [ +  -  +  - ]:           5 :                                         ereport(ERROR,
   17580                 :             :                                                         errcode(ERRCODE_DATATYPE_MISMATCH),
   17581                 :             :                                                         errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
   17582                 :             :                                                                    parent_attname, RelationGetRelationName(child_rel)));
   17583                 :           3 :                         }
   17584                 :             : 
   17585                 :             :                         /*
   17586                 :             :                          * Child column must be generated if and only if parent column is.
   17587                 :             :                          */
   17588   [ +  +  +  + ]:         948 :                         if (parent_att->attgenerated && !child_att->attgenerated)
   17589   [ +  -  +  - ]:           6 :                                 ereport(ERROR,
   17590                 :             :                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
   17591                 :             :                                                  errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
   17592   [ +  +  +  + ]:         942 :                         if (child_att->attgenerated && !parent_att->attgenerated)
   17593   [ +  -  +  - ]:           4 :                                 ereport(ERROR,
   17594                 :             :                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
   17595                 :             :                                                  errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
   17596                 :             : 
   17597   [ +  +  +  -  :         938 :                         if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
                   +  + ]
   17598   [ +  -  +  - ]:           2 :                                 ereport(ERROR,
   17599                 :             :                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
   17600                 :             :                                                  errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
   17601                 :             :                                                  errdetail("Parent column is %s, child column is %s.",
   17602                 :             :                                                                    parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
   17603                 :             :                                                                    child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
   17604                 :             : 
   17605                 :             :                         /*
   17606                 :             :                          * Regular inheritance children are independent enough not to
   17607                 :             :                          * inherit identity columns.  But partitions are integral part of
   17608                 :             :                          * a partitioned table and inherit identity column.
   17609                 :             :                          */
   17610         [ +  + ]:         936 :                         if (ispartition)
   17611                 :         863 :                                 child_att->attidentity = parent_att->attidentity;
   17612                 :             : 
   17613                 :             :                         /*
   17614                 :             :                          * OK, bump the child column's inheritance count.  (If we fail
   17615                 :             :                          * later on, this change will just roll back.)
   17616                 :             :                          */
   17617   [ +  -  +  - ]:        1872 :                         if (pg_add_s16_overflow(child_att->attinhcount, 1,
   17618                 :         936 :                                                                         &child_att->attinhcount))
   17619   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   17620                 :             :                                                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   17621                 :             :                                                 errmsg("too many inheritance parents"));
   17622                 :             : 
   17623                 :             :                         /*
   17624                 :             :                          * In case of partitions, we must enforce that value of attislocal
   17625                 :             :                          * is same in all partitions. (Note: there are only inherited
   17626                 :             :                          * attributes in partitions)
   17627                 :             :                          */
   17628         [ +  + ]:         936 :                         if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   17629                 :             :                         {
   17630         [ -  + ]:         863 :                                 Assert(child_att->attinhcount == 1);
   17631                 :         863 :                                 child_att->attislocal = false;
   17632                 :         863 :                         }
   17633                 :             : 
   17634                 :         936 :                         CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
   17635                 :         936 :                         heap_freetuple(tuple);
   17636                 :         936 :                 }
   17637                 :             :                 else
   17638                 :             :                 {
   17639   [ +  -  +  - ]:           2 :                         ereport(ERROR,
   17640                 :             :                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   17641                 :             :                                          errmsg("child table is missing column \"%s\"", parent_attname)));
   17642                 :             :                 }
   17643      [ -  +  + ]:         968 :         }
   17644                 :             : 
   17645                 :         399 :         table_close(attrrel, RowExclusiveLock);
   17646                 :         399 : }
   17647                 :             : 
   17648                 :             : /*
   17649                 :             :  * Check constraints in child table match up with constraints in parent,
   17650                 :             :  * and increment their coninhcount.
   17651                 :             :  *
   17652                 :             :  * Constraints that are marked ONLY in the parent are ignored.
   17653                 :             :  *
   17654                 :             :  * Called by CreateInheritance
   17655                 :             :  *
   17656                 :             :  * Currently all constraints in parent must be present in the child. One day we
   17657                 :             :  * may consider adding new constraints like CREATE TABLE does.
   17658                 :             :  *
   17659                 :             :  * XXX This is O(N^2) which may be an issue with tables with hundreds of
   17660                 :             :  * constraints. As long as tables have more like 10 constraints it shouldn't be
   17661                 :             :  * a problem though. Even 100 constraints ought not be the end of the world.
   17662                 :             :  *
   17663                 :             :  * XXX See MergeWithExistingConstraint too if you change this code.
   17664                 :             :  */
   17665                 :             : static void
   17666                 :         399 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
   17667                 :             : {
   17668                 :         399 :         Relation        constraintrel;
   17669                 :         399 :         SysScanDesc parent_scan;
   17670                 :         399 :         ScanKeyData parent_key;
   17671                 :         399 :         HeapTuple       parent_tuple;
   17672                 :         399 :         Oid                     parent_relid = RelationGetRelid(parent_rel);
   17673                 :         399 :         AttrMap    *attmap;
   17674                 :             : 
   17675                 :         399 :         constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
   17676                 :             : 
   17677                 :             :         /* Outer loop scans through the parent's constraint definitions */
   17678                 :         399 :         ScanKeyInit(&parent_key,
   17679                 :             :                                 Anum_pg_constraint_conrelid,
   17680                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   17681                 :         399 :                                 ObjectIdGetDatum(parent_relid));
   17682                 :         399 :         parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
   17683                 :             :                                                                          true, NULL, 1, &parent_key);
   17684                 :             : 
   17685                 :         798 :         attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
   17686                 :         399 :                                                                    RelationGetDescr(child_rel),
   17687                 :             :                                                                    true);
   17688                 :             : 
   17689         [ +  + ]:         756 :         while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
   17690                 :             :         {
   17691                 :         367 :                 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
   17692                 :         367 :                 SysScanDesc child_scan;
   17693                 :         367 :                 ScanKeyData child_key;
   17694                 :         367 :                 HeapTuple       child_tuple;
   17695                 :         367 :                 AttrNumber      parent_attno;
   17696                 :         367 :                 bool            found = false;
   17697                 :             : 
   17698   [ +  +  +  + ]:         367 :                 if (parent_con->contype != CONSTRAINT_CHECK &&
   17699                 :         330 :                         parent_con->contype != CONSTRAINT_NOTNULL)
   17700                 :         171 :                         continue;
   17701                 :             : 
   17702                 :             :                 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
   17703         [ +  + ]:         196 :                 if (parent_con->connoinherit)
   17704                 :           6 :                         continue;
   17705                 :             : 
   17706         [ +  + ]:         190 :                 if (parent_con->contype == CONSTRAINT_NOTNULL)
   17707                 :         155 :                         parent_attno = extractNotNullColumn(parent_tuple);
   17708                 :             :                 else
   17709                 :          35 :                         parent_attno = InvalidAttrNumber;
   17710                 :             : 
   17711                 :             :                 /* Search for a child constraint matching this one */
   17712                 :         190 :                 ScanKeyInit(&child_key,
   17713                 :             :                                         Anum_pg_constraint_conrelid,
   17714                 :             :                                         BTEqualStrategyNumber, F_OIDEQ,
   17715                 :         190 :                                         ObjectIdGetDatum(RelationGetRelid(child_rel)));
   17716                 :         190 :                 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
   17717                 :             :                                                                                 true, NULL, 1, &child_key);
   17718                 :             : 
   17719         [ +  + ]:         497 :                 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
   17720                 :             :                 {
   17721                 :         313 :                         Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
   17722                 :         313 :                         HeapTuple       child_copy;
   17723                 :             : 
   17724         [ +  + ]:         313 :                         if (child_con->contype != parent_con->contype)
   17725                 :          68 :                                 continue;
   17726                 :             : 
   17727                 :             :                         /*
   17728                 :             :                          * CHECK constraint are matched by constraint name, NOT NULL ones
   17729                 :             :                          * by attribute number.
   17730                 :             :                          */
   17731         [ +  + ]:         245 :                         if (child_con->contype == CONSTRAINT_CHECK)
   17732                 :             :                         {
   17733                 :          90 :                                 if (strcmp(NameStr(parent_con->conname),
   17734   [ +  +  +  + ]:          90 :                                                    NameStr(child_con->conname)) != 0)
   17735                 :          14 :                                         continue;
   17736                 :          31 :                         }
   17737         [ -  + ]:         200 :                         else if (child_con->contype == CONSTRAINT_NOTNULL)
   17738                 :             :                         {
   17739                 :         200 :                                 Form_pg_attribute parent_attr;
   17740                 :         200 :                                 Form_pg_attribute child_attr;
   17741                 :         200 :                                 AttrNumber      child_attno;
   17742                 :             : 
   17743                 :         200 :                                 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
   17744                 :         200 :                                 child_attno = extractNotNullColumn(child_tuple);
   17745         [ +  + ]:         200 :                                 if (parent_attno != attmap->attnums[child_attno - 1])
   17746                 :          45 :                                         continue;
   17747                 :             : 
   17748                 :         155 :                                 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
   17749                 :             :                                 /* there shouldn't be constraints on dropped columns */
   17750         [ +  - ]:         155 :                                 if (parent_attr->attisdropped || child_attr->attisdropped)
   17751   [ #  #  #  # ]:           0 :                                         elog(ERROR, "found not-null constraint on dropped columns");
   17752         [ +  + ]:         200 :                         }
   17753                 :             : 
   17754   [ +  +  +  + ]:         186 :                         if (child_con->contype == CONSTRAINT_CHECK &&
   17755                 :          31 :                                 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
   17756   [ -  +  +  - ]:           1 :                                 ereport(ERROR,
   17757                 :             :                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
   17758                 :             :                                                  errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
   17759                 :             :                                                                 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
   17760                 :             : 
   17761                 :             :                         /*
   17762                 :             :                          * If the child constraint is "no inherit" then cannot merge
   17763                 :             :                          */
   17764         [ +  + ]:         185 :                         if (child_con->connoinherit)
   17765   [ -  +  +  - ]:           2 :                                 ereport(ERROR,
   17766                 :             :                                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17767                 :             :                                                  errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
   17768                 :             :                                                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   17769                 :             : 
   17770                 :             :                         /*
   17771                 :             :                          * If the child constraint is "not valid" then cannot merge with a
   17772                 :             :                          * valid parent constraint
   17773                 :             :                          */
   17774   [ +  +  +  +  :         183 :                         if (parent_con->convalidated && child_con->conenforced &&
                   +  + ]
   17775                 :         165 :                                 !child_con->convalidated)
   17776   [ +  -  +  - ]:           2 :                                 ereport(ERROR,
   17777                 :             :                                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17778                 :             :                                                  errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
   17779                 :             :                                                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   17780                 :             : 
   17781                 :             :                         /*
   17782                 :             :                          * A NOT ENFORCED child constraint cannot be merged with an
   17783                 :             :                          * ENFORCED parent constraint. However, the reverse is allowed,
   17784                 :             :                          * where the child constraint is ENFORCED.
   17785                 :             :                          */
   17786   [ +  +  +  + ]:         181 :                         if (parent_con->conenforced && !child_con->conenforced)
   17787   [ -  +  +  - ]:           1 :                                 ereport(ERROR,
   17788                 :             :                                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   17789                 :             :                                                  errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
   17790                 :             :                                                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   17791                 :             : 
   17792                 :             :                         /*
   17793                 :             :                          * OK, bump the child constraint's inheritance count.  (If we fail
   17794                 :             :                          * later on, this change will just roll back.)
   17795                 :             :                          */
   17796                 :         180 :                         child_copy = heap_copytuple(child_tuple);
   17797                 :         180 :                         child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
   17798                 :             : 
   17799   [ -  +  -  + ]:         360 :                         if (pg_add_s16_overflow(child_con->coninhcount, 1,
   17800                 :         180 :                                                                         &child_con->coninhcount))
   17801   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   17802                 :             :                                                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   17803                 :             :                                                 errmsg("too many inheritance parents"));
   17804                 :             : 
   17805                 :             :                         /*
   17806                 :             :                          * In case of partitions, an inherited constraint must be
   17807                 :             :                          * inherited only once since it cannot have multiple parents and
   17808                 :             :                          * it is never considered local.
   17809                 :             :                          */
   17810         [ +  + ]:         180 :                         if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   17811                 :             :                         {
   17812         [ -  + ]:         163 :                                 Assert(child_con->coninhcount == 1);
   17813                 :         163 :                                 child_con->conislocal = false;
   17814                 :         163 :                         }
   17815                 :             : 
   17816                 :         180 :                         CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
   17817                 :         180 :                         heap_freetuple(child_copy);
   17818                 :             : 
   17819                 :         180 :                         found = true;
   17820                 :         180 :                         break;
   17821                 :         307 :                 }
   17822                 :             : 
   17823                 :         184 :                 systable_endscan(child_scan);
   17824                 :             : 
   17825         [ +  + ]:         184 :                 if (!found)
   17826                 :             :                 {
   17827         [ +  - ]:           4 :                         if (parent_con->contype == CONSTRAINT_NOTNULL)
   17828   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   17829                 :             :                                                 errcode(ERRCODE_DATATYPE_MISMATCH),
   17830                 :             :                                                 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
   17831                 :             :                                                            get_attname(parent_relid,
   17832                 :             :                                                                                    extractNotNullColumn(parent_tuple),
   17833                 :             :                                                                                    false),
   17834                 :             :                                                            RelationGetRelationName(child_rel)));
   17835                 :             : 
   17836   [ +  -  +  - ]:           4 :                         ereport(ERROR,
   17837                 :             :                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   17838                 :             :                                          errmsg("child table is missing constraint \"%s\"",
   17839                 :             :                                                         NameStr(parent_con->conname))));
   17840                 :           0 :                 }
   17841         [ +  + ]:         357 :         }
   17842                 :             : 
   17843                 :         389 :         systable_endscan(parent_scan);
   17844                 :         389 :         table_close(constraintrel, RowExclusiveLock);
   17845                 :         389 : }
   17846                 :             : 
   17847                 :             : /*
   17848                 :             :  * ALTER TABLE NO INHERIT
   17849                 :             :  *
   17850                 :             :  * Return value is the address of the relation that is no longer parent.
   17851                 :             :  */
   17852                 :             : static ObjectAddress
   17853                 :          13 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
   17854                 :             : {
   17855                 :             :         ObjectAddress address;
   17856                 :          13 :         Relation        parent_rel;
   17857                 :             : 
   17858         [ +  - ]:          13 :         if (rel->rd_rel->relispartition)
   17859   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   17860                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17861                 :             :                                  errmsg("cannot change inheritance of a partition")));
   17862                 :             : 
   17863                 :             :         /*
   17864                 :             :          * AccessShareLock on the parent is probably enough, seeing that DROP
   17865                 :             :          * TABLE doesn't lock parent tables at all.  We need some lock since we'll
   17866                 :             :          * be inspecting the parent's schema.
   17867                 :             :          */
   17868                 :          13 :         parent_rel = table_openrv(parent, AccessShareLock);
   17869                 :             : 
   17870                 :             :         /*
   17871                 :             :          * We don't bother to check ownership of the parent table --- ownership of
   17872                 :             :          * the child is presumed enough rights.
   17873                 :             :          */
   17874                 :             : 
   17875                 :             :         /* Off to RemoveInheritance() where most of the work happens */
   17876                 :          13 :         RemoveInheritance(rel, parent_rel, false);
   17877                 :             : 
   17878                 :          13 :         ObjectAddressSet(address, RelationRelationId,
   17879                 :             :                                          RelationGetRelid(parent_rel));
   17880                 :             : 
   17881                 :             :         /* keep our lock on the parent relation until commit */
   17882                 :          13 :         table_close(parent_rel, NoLock);
   17883                 :             : 
   17884                 :             :         return address;
   17885                 :          13 : }
   17886                 :             : 
   17887                 :             : /*
   17888                 :             :  * MarkInheritDetached
   17889                 :             :  *
   17890                 :             :  * Set inhdetachpending for a partition, for ATExecDetachPartition
   17891                 :             :  * in concurrent mode.  While at it, verify that no other partition is
   17892                 :             :  * already pending detach.
   17893                 :             :  */
   17894                 :             : static void
   17895                 :           2 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
   17896                 :             : {
   17897                 :           2 :         Relation        catalogRelation;
   17898                 :           2 :         SysScanDesc scan;
   17899                 :           2 :         ScanKeyData key;
   17900                 :           2 :         HeapTuple       inheritsTuple;
   17901                 :           2 :         bool            found = false;
   17902                 :             : 
   17903         [ +  - ]:           2 :         Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   17904                 :             : 
   17905                 :             :         /*
   17906                 :             :          * Find pg_inherits entries by inhparent.  (We need to scan them all in
   17907                 :             :          * order to verify that no other partition is pending detach.)
   17908                 :             :          */
   17909                 :           2 :         catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   17910                 :           2 :         ScanKeyInit(&key,
   17911                 :             :                                 Anum_pg_inherits_inhparent,
   17912                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   17913                 :           2 :                                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   17914                 :           2 :         scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
   17915                 :             :                                                           true, NULL, 1, &key);
   17916                 :             : 
   17917         [ +  + ]:           4 :         while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   17918                 :             :         {
   17919                 :           2 :                 Form_pg_inherits inhForm;
   17920                 :             : 
   17921                 :           2 :                 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   17922         [ +  - ]:           2 :                 if (inhForm->inhdetachpending)
   17923   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   17924                 :             :                                         errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   17925                 :             :                                         errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
   17926                 :             :                                                    get_rel_name(inhForm->inhrelid),
   17927                 :             :                                                    get_namespace_name(parent_rel->rd_rel->relnamespace),
   17928                 :             :                                                    RelationGetRelationName(parent_rel)),
   17929                 :             :                                         errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
   17930                 :             : 
   17931         [ -  + ]:           2 :                 if (inhForm->inhrelid == RelationGetRelid(child_rel))
   17932                 :             :                 {
   17933                 :           2 :                         HeapTuple       newtup;
   17934                 :             : 
   17935                 :           2 :                         newtup = heap_copytuple(inheritsTuple);
   17936                 :           2 :                         ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
   17937                 :             : 
   17938                 :           4 :                         CatalogTupleUpdate(catalogRelation,
   17939                 :           2 :                                                            &inheritsTuple->t_self,
   17940                 :           2 :                                                            newtup);
   17941                 :           2 :                         found = true;
   17942                 :           2 :                         heap_freetuple(newtup);
   17943                 :             :                         /* keep looking, to ensure we catch others pending detach */
   17944                 :           2 :                 }
   17945                 :           2 :         }
   17946                 :             : 
   17947                 :             :         /* Done */
   17948                 :           2 :         systable_endscan(scan);
   17949                 :           2 :         table_close(catalogRelation, RowExclusiveLock);
   17950                 :             : 
   17951         [ +  - ]:           2 :         if (!found)
   17952   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   17953                 :             :                                 (errcode(ERRCODE_UNDEFINED_TABLE),
   17954                 :             :                                  errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   17955                 :             :                                                 RelationGetRelationName(child_rel),
   17956                 :             :                                                 RelationGetRelationName(parent_rel))));
   17957                 :           2 : }
   17958                 :             : 
   17959                 :             : /*
   17960                 :             :  * RemoveInheritance
   17961                 :             :  *
   17962                 :             :  * Drop a parent from the child's parents. This just adjusts the attinhcount
   17963                 :             :  * and attislocal of the columns and removes the pg_inherit and pg_depend
   17964                 :             :  * entries.  expect_detached is passed down to DeleteInheritsTuple, q.v..
   17965                 :             :  *
   17966                 :             :  * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
   17967                 :             :  * up attislocal stays true, which means if a child is ever removed from a
   17968                 :             :  * parent then its columns will never be automatically dropped which may
   17969                 :             :  * surprise. But at least we'll never surprise by dropping columns someone
   17970                 :             :  * isn't expecting to be dropped which would actually mean data loss.
   17971                 :             :  *
   17972                 :             :  * coninhcount and conislocal for inherited constraints are adjusted in
   17973                 :             :  * exactly the same way.
   17974                 :             :  *
   17975                 :             :  * Common to ATExecDropInherit() and ATExecDetachPartition().
   17976                 :             :  */
   17977                 :             : static void
   17978                 :         166 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
   17979                 :             : {
   17980                 :         166 :         Relation        catalogRelation;
   17981                 :         166 :         SysScanDesc scan;
   17982                 :         166 :         ScanKeyData key[3];
   17983                 :         166 :         HeapTuple       attributeTuple,
   17984                 :             :                                 constraintTuple;
   17985                 :         166 :         AttrMap    *attmap;
   17986                 :         166 :         List       *connames;
   17987                 :         166 :         List       *nncolumns;
   17988                 :         166 :         bool            found;
   17989                 :         166 :         bool            is_partitioning;
   17990                 :             : 
   17991                 :         166 :         is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   17992                 :             : 
   17993                 :         332 :         found = DeleteInheritsTuple(RelationGetRelid(child_rel),
   17994                 :         166 :                                                                 RelationGetRelid(parent_rel),
   17995                 :         166 :                                                                 expect_detached,
   17996                 :         166 :                                                                 RelationGetRelationName(child_rel));
   17997         [ +  + ]:         166 :         if (!found)
   17998                 :             :         {
   17999         [ +  + ]:           4 :                 if (is_partitioning)
   18000   [ +  -  +  - ]:           3 :                         ereport(ERROR,
   18001                 :             :                                         (errcode(ERRCODE_UNDEFINED_TABLE),
   18002                 :             :                                          errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   18003                 :             :                                                         RelationGetRelationName(child_rel),
   18004                 :             :                                                         RelationGetRelationName(parent_rel))));
   18005                 :             :                 else
   18006   [ +  -  +  - ]:           1 :                         ereport(ERROR,
   18007                 :             :                                         (errcode(ERRCODE_UNDEFINED_TABLE),
   18008                 :             :                                          errmsg("relation \"%s\" is not a parent of relation \"%s\"",
   18009                 :             :                                                         RelationGetRelationName(parent_rel),
   18010                 :             :                                                         RelationGetRelationName(child_rel))));
   18011                 :           0 :         }
   18012                 :             : 
   18013                 :             :         /*
   18014                 :             :          * Search through child columns looking for ones matching parent rel
   18015                 :             :          */
   18016                 :         162 :         catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
   18017                 :         324 :         ScanKeyInit(&key[0],
   18018                 :             :                                 Anum_pg_attribute_attrelid,
   18019                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   18020                 :         162 :                                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   18021                 :         324 :         scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
   18022                 :         162 :                                                           true, NULL, 1, key);
   18023         [ +  + ]:        1513 :         while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   18024                 :             :         {
   18025                 :        1351 :                 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   18026                 :             : 
   18027                 :             :                 /* Ignore if dropped or not inherited */
   18028         [ +  + ]:        1351 :                 if (att->attisdropped)
   18029                 :           7 :                         continue;
   18030         [ +  + ]:        1344 :                 if (att->attinhcount <= 0)
   18031                 :         977 :                         continue;
   18032                 :             : 
   18033   [ +  +  +  + ]:         734 :                 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
   18034                 :         367 :                                                                                 NameStr(att->attname)))
   18035                 :             :                 {
   18036                 :             :                         /* Decrement inhcount and possibly set islocal to true */
   18037                 :         358 :                         HeapTuple       copyTuple = heap_copytuple(attributeTuple);
   18038                 :         358 :                         Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
   18039                 :             : 
   18040                 :         358 :                         copy_att->attinhcount--;
   18041         [ +  + ]:         358 :                         if (copy_att->attinhcount == 0)
   18042                 :         353 :                                 copy_att->attislocal = true;
   18043                 :             : 
   18044                 :         358 :                         CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   18045                 :         358 :                         heap_freetuple(copyTuple);
   18046                 :         358 :                 }
   18047         [ +  + ]:        1351 :         }
   18048                 :         162 :         systable_endscan(scan);
   18049                 :         162 :         table_close(catalogRelation, RowExclusiveLock);
   18050                 :             : 
   18051                 :             :         /*
   18052                 :             :          * Likewise, find inherited check and not-null constraints and disinherit
   18053                 :             :          * them. To do this, we first need a list of the names of the parent's
   18054                 :             :          * check constraints.  (We cheat a bit by only checking for name matches,
   18055                 :             :          * assuming that the expressions will match.)
   18056                 :             :          *
   18057                 :             :          * For NOT NULL columns, we store column numbers to match, mapping them in
   18058                 :             :          * to the child rel's attribute numbers.
   18059                 :             :          */
   18060                 :         324 :         attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
   18061                 :         162 :                                                                    RelationGetDescr(parent_rel),
   18062                 :             :                                                                    false);
   18063                 :             : 
   18064                 :         162 :         catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
   18065                 :         324 :         ScanKeyInit(&key[0],
   18066                 :             :                                 Anum_pg_constraint_conrelid,
   18067                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   18068                 :         162 :                                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   18069                 :         324 :         scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   18070                 :         162 :                                                           true, NULL, 1, key);
   18071                 :             : 
   18072                 :         162 :         connames = NIL;
   18073                 :         162 :         nncolumns = NIL;
   18074                 :             : 
   18075         [ +  + ]:         326 :         while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   18076                 :             :         {
   18077                 :         164 :                 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   18078                 :             : 
   18079         [ +  + ]:         164 :                 if (con->connoinherit)
   18080                 :          32 :                         continue;
   18081                 :             : 
   18082         [ +  + ]:         132 :                 if (con->contype == CONSTRAINT_CHECK)
   18083                 :          19 :                         connames = lappend(connames, pstrdup(NameStr(con->conname)));
   18084         [ +  + ]:         132 :                 if (con->contype == CONSTRAINT_NOTNULL)
   18085                 :             :                 {
   18086                 :          58 :                         AttrNumber      parent_attno = extractNotNullColumn(constraintTuple);
   18087                 :             : 
   18088                 :          58 :                         nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
   18089                 :          58 :                 }
   18090         [ +  + ]:         164 :         }
   18091                 :             : 
   18092                 :         162 :         systable_endscan(scan);
   18093                 :             : 
   18094                 :             :         /* Now scan the child's constraints to find matches */
   18095                 :         324 :         ScanKeyInit(&key[0],
   18096                 :             :                                 Anum_pg_constraint_conrelid,
   18097                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   18098                 :         162 :                                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   18099                 :         324 :         scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   18100                 :         162 :                                                           true, NULL, 1, key);
   18101                 :             : 
   18102         [ +  + ]:         324 :         while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   18103                 :             :         {
   18104                 :         162 :                 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   18105                 :         162 :                 bool            match = false;
   18106                 :             : 
   18107                 :             :                 /*
   18108                 :             :                  * Match CHECK constraints by name, not-null constraints by column
   18109                 :             :                  * number, and ignore all others.
   18110                 :             :                  */
   18111         [ +  + ]:         162 :                 if (con->contype == CONSTRAINT_CHECK)
   18112                 :             :                 {
   18113   [ +  +  +  +  :          74 :                         foreach_ptr(char, chkname, connames)
             +  +  +  + ]
   18114                 :             :                         {
   18115   [ +  -  +  + ]:          20 :                                 if (con->contype == CONSTRAINT_CHECK &&
   18116                 :          20 :                                         strcmp(NameStr(con->conname), chkname) == 0)
   18117                 :             :                                 {
   18118                 :          19 :                                         match = true;
   18119                 :          19 :                                         connames = foreach_delete_current(connames, chkname);
   18120                 :          19 :                                         break;
   18121                 :             :                                 }
   18122                 :          28 :                         }
   18123                 :          27 :                 }
   18124         [ +  + ]:         135 :                 else if (con->contype == CONSTRAINT_NOTNULL)
   18125                 :             :                 {
   18126                 :          68 :                         AttrNumber      child_attno = extractNotNullColumn(constraintTuple);
   18127                 :             : 
   18128   [ +  +  +  +  :         195 :                         foreach_int(prevattno, nncolumns)
             +  +  +  + ]
   18129                 :             :                         {
   18130         [ +  + ]:          59 :                                 if (prevattno == child_attno)
   18131                 :             :                                 {
   18132                 :          58 :                                         match = true;
   18133                 :          58 :                                         nncolumns = foreach_delete_current(nncolumns, prevattno);
   18134                 :          58 :                                         break;
   18135                 :             :                                 }
   18136                 :          69 :                         }
   18137                 :          68 :                 }
   18138                 :             :                 else
   18139                 :          67 :                         continue;
   18140                 :             : 
   18141         [ +  + ]:          95 :                 if (match)
   18142                 :             :                 {
   18143                 :             :                         /* Decrement inhcount and possibly set islocal to true */
   18144                 :          77 :                         HeapTuple       copyTuple = heap_copytuple(constraintTuple);
   18145                 :          77 :                         Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   18146                 :             : 
   18147         [ +  - ]:          77 :                         if (copy_con->coninhcount <= 0) /* shouldn't happen */
   18148   [ #  #  #  # ]:           0 :                                 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   18149                 :             :                                          RelationGetRelid(child_rel), NameStr(copy_con->conname));
   18150                 :             : 
   18151                 :          77 :                         copy_con->coninhcount--;
   18152         [ +  + ]:          77 :                         if (copy_con->coninhcount == 0)
   18153                 :          74 :                                 copy_con->conislocal = true;
   18154                 :             : 
   18155                 :          77 :                         CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   18156                 :          77 :                         heap_freetuple(copyTuple);
   18157                 :          77 :                 }
   18158         [ +  + ]:         162 :         }
   18159                 :             : 
   18160                 :             :         /* We should have matched all constraints */
   18161         [ +  - ]:         162 :         if (connames != NIL || nncolumns != NIL)
   18162   [ #  #  #  # ]:           0 :                 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
   18163                 :             :                          list_length(connames) + list_length(nncolumns),
   18164                 :             :                          RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
   18165                 :             : 
   18166                 :         162 :         systable_endscan(scan);
   18167                 :         162 :         table_close(catalogRelation, RowExclusiveLock);
   18168                 :             : 
   18169                 :         324 :         drop_parent_dependency(RelationGetRelid(child_rel),
   18170                 :             :                                                    RelationRelationId,
   18171                 :         162 :                                                    RelationGetRelid(parent_rel),
   18172                 :         162 :                                                    child_dependency_type(is_partitioning));
   18173                 :             : 
   18174                 :             :         /*
   18175                 :             :          * Post alter hook of this inherits. Since object_access_hook doesn't take
   18176                 :             :          * multiple object identifiers, we relay oid of parent relation using
   18177                 :             :          * auxiliary_id argument.
   18178                 :             :          */
   18179         [ -  + ]:         162 :         InvokeObjectPostAlterHookArg(InheritsRelationId,
   18180                 :             :                                                                  RelationGetRelid(child_rel), 0,
   18181                 :             :                                                                  RelationGetRelid(parent_rel), false);
   18182                 :         162 : }
   18183                 :             : 
   18184                 :             : /*
   18185                 :             :  * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
   18186                 :             :  * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
   18187                 :             :  * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
   18188                 :             :  * be TypeRelationId).  There's no convenient way to do this, so go trawling
   18189                 :             :  * through pg_depend.
   18190                 :             :  */
   18191                 :             : static void
   18192                 :         164 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
   18193                 :             :                                            DependencyType deptype)
   18194                 :             : {
   18195                 :         164 :         Relation        catalogRelation;
   18196                 :         164 :         SysScanDesc scan;
   18197                 :         164 :         ScanKeyData key[3];
   18198                 :         164 :         HeapTuple       depTuple;
   18199                 :             : 
   18200                 :         164 :         catalogRelation = table_open(DependRelationId, RowExclusiveLock);
   18201                 :             : 
   18202                 :         328 :         ScanKeyInit(&key[0],
   18203                 :             :                                 Anum_pg_depend_classid,
   18204                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   18205                 :         164 :                                 ObjectIdGetDatum(RelationRelationId));
   18206                 :         328 :         ScanKeyInit(&key[1],
   18207                 :             :                                 Anum_pg_depend_objid,
   18208                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   18209                 :         164 :                                 ObjectIdGetDatum(relid));
   18210                 :         328 :         ScanKeyInit(&key[2],
   18211                 :             :                                 Anum_pg_depend_objsubid,
   18212                 :             :                                 BTEqualStrategyNumber, F_INT4EQ,
   18213                 :         164 :                                 Int32GetDatum(0));
   18214                 :             : 
   18215                 :         328 :         scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
   18216                 :         164 :                                                           NULL, 3, key);
   18217                 :             : 
   18218         [ +  + ]:         504 :         while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
   18219                 :             :         {
   18220                 :         340 :                 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
   18221                 :             : 
   18222         [ +  + ]:         340 :                 if (dep->refclassid == refclassid &&
   18223         [ +  + ]:         171 :                         dep->refobjid == refobjid &&
   18224   [ +  -  -  + ]:         164 :                         dep->refobjsubid == 0 &&
   18225                 :         164 :                         dep->deptype == deptype)
   18226                 :         164 :                         CatalogTupleDelete(catalogRelation, &depTuple->t_self);
   18227                 :         340 :         }
   18228                 :             : 
   18229                 :         164 :         systable_endscan(scan);
   18230                 :         164 :         table_close(catalogRelation, RowExclusiveLock);
   18231                 :         164 : }
   18232                 :             : 
   18233                 :             : /*
   18234                 :             :  * ALTER TABLE OF
   18235                 :             :  *
   18236                 :             :  * Attach a table to a composite type, as though it had been created with CREATE
   18237                 :             :  * TABLE OF.  All attname, atttypid, atttypmod and attcollation must match.  The
   18238                 :             :  * subject table must not have inheritance parents.  These restrictions ensure
   18239                 :             :  * that you cannot create a configuration impossible with CREATE TABLE OF alone.
   18240                 :             :  *
   18241                 :             :  * The address of the type is returned.
   18242                 :             :  */
   18243                 :             : static ObjectAddress
   18244                 :           9 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
   18245                 :             : {
   18246                 :           9 :         Oid                     relid = RelationGetRelid(rel);
   18247                 :           9 :         Type            typetuple;
   18248                 :           9 :         Form_pg_type typeform;
   18249                 :           9 :         Oid                     typeid;
   18250                 :           9 :         Relation        inheritsRelation,
   18251                 :             :                                 relationRelation;
   18252                 :           9 :         SysScanDesc scan;
   18253                 :           9 :         ScanKeyData key;
   18254                 :           9 :         AttrNumber      table_attno,
   18255                 :             :                                 type_attno;
   18256                 :           9 :         TupleDesc       typeTupleDesc,
   18257                 :             :                                 tableTupleDesc;
   18258                 :           9 :         ObjectAddress tableobj,
   18259                 :             :                                 typeobj;
   18260                 :           9 :         HeapTuple       classtuple;
   18261                 :             : 
   18262                 :             :         /* Validate the type. */
   18263                 :           9 :         typetuple = typenameType(NULL, ofTypename, NULL);
   18264                 :           9 :         check_of_type(typetuple);
   18265                 :           9 :         typeform = (Form_pg_type) GETSTRUCT(typetuple);
   18266                 :           9 :         typeid = typeform->oid;
   18267                 :             : 
   18268                 :             :         /* Fail if the table has any inheritance parents. */
   18269                 :           9 :         inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
   18270                 :           9 :         ScanKeyInit(&key,
   18271                 :             :                                 Anum_pg_inherits_inhrelid,
   18272                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   18273                 :           9 :                                 ObjectIdGetDatum(relid));
   18274                 :           9 :         scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
   18275                 :             :                                                           true, NULL, 1, &key);
   18276         [ +  + ]:           9 :         if (HeapTupleIsValid(systable_getnext(scan)))
   18277   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   18278                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18279                 :             :                                  errmsg("typed tables cannot inherit")));
   18280                 :           8 :         systable_endscan(scan);
   18281                 :           8 :         table_close(inheritsRelation, AccessShareLock);
   18282                 :             : 
   18283                 :             :         /*
   18284                 :             :          * Check the tuple descriptors for compatibility.  Unlike inheritance, we
   18285                 :             :          * require that the order also match.  However, attnotnull need not match.
   18286                 :             :          */
   18287                 :           8 :         typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
   18288                 :           8 :         tableTupleDesc = RelationGetDescr(rel);
   18289                 :           8 :         table_attno = 1;
   18290         [ +  + ]:          26 :         for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
   18291                 :             :         {
   18292                 :          22 :                 Form_pg_attribute type_attr,
   18293                 :             :                                         table_attr;
   18294                 :          22 :                 const char *type_attname,
   18295                 :             :                                    *table_attname;
   18296                 :             : 
   18297                 :             :                 /* Get the next non-dropped type attribute. */
   18298                 :          22 :                 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
   18299         [ +  + ]:          22 :                 if (type_attr->attisdropped)
   18300                 :           7 :                         continue;
   18301                 :          15 :                 type_attname = NameStr(type_attr->attname);
   18302                 :             : 
   18303                 :             :                 /* Get the next non-dropped table attribute. */
   18304                 :          15 :                 do
   18305                 :             :                 {
   18306         [ +  + ]:          17 :                         if (table_attno > tableTupleDesc->natts)
   18307   [ +  -  +  - ]:           1 :                                 ereport(ERROR,
   18308                 :             :                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
   18309                 :             :                                                  errmsg("table is missing column \"%s\"",
   18310                 :             :                                                                 type_attname)));
   18311                 :          16 :                         table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
   18312                 :          16 :                         table_attno++;
   18313         [ +  + ]:          16 :                 } while (table_attr->attisdropped);
   18314                 :          14 :                 table_attname = NameStr(table_attr->attname);
   18315                 :             : 
   18316                 :             :                 /* Compare name. */
   18317         [ +  + ]:          14 :                 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
   18318   [ -  +  +  - ]:           1 :                         ereport(ERROR,
   18319                 :             :                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18320                 :             :                                          errmsg("table has column \"%s\" where type requires \"%s\"",
   18321                 :             :                                                         table_attname, type_attname)));
   18322                 :             : 
   18323                 :             :                 /* Compare type. */
   18324         [ +  + ]:          13 :                 if (table_attr->atttypid != type_attr->atttypid ||
   18325                 :          11 :                         table_attr->atttypmod != type_attr->atttypmod ||
   18326                 :          11 :                         table_attr->attcollation != type_attr->attcollation)
   18327   [ +  -  +  - ]:           2 :                         ereport(ERROR,
   18328                 :             :                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18329                 :             :                                          errmsg("table \"%s\" has different type for column \"%s\"",
   18330                 :             :                                                         RelationGetRelationName(rel), type_attname)));
   18331      [ -  +  + ]:          18 :         }
   18332         [ -  + ]:           4 :         ReleaseTupleDesc(typeTupleDesc);
   18333                 :             : 
   18334                 :             :         /* Any remaining columns at the end of the table had better be dropped. */
   18335         [ +  + ]:           4 :         for (; table_attno <= tableTupleDesc->natts; table_attno++)
   18336                 :             :         {
   18337                 :           2 :                 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
   18338                 :           1 :                                                                                                          table_attno - 1);
   18339                 :             : 
   18340         [ -  + ]:           1 :                 if (!table_attr->attisdropped)
   18341   [ +  -  +  - ]:           1 :                         ereport(ERROR,
   18342                 :             :                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18343                 :             :                                          errmsg("table has extra column \"%s\"",
   18344                 :             :                                                         NameStr(table_attr->attname))));
   18345                 :           0 :         }
   18346                 :             : 
   18347                 :             :         /* If the table was already typed, drop the existing dependency. */
   18348         [ +  + ]:           3 :         if (rel->rd_rel->reloftype)
   18349                 :           1 :                 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   18350                 :             :                                                            DEPENDENCY_NORMAL);
   18351                 :             : 
   18352                 :             :         /* Record a dependency on the new type. */
   18353                 :           3 :         tableobj.classId = RelationRelationId;
   18354                 :           3 :         tableobj.objectId = relid;
   18355                 :           3 :         tableobj.objectSubId = 0;
   18356                 :           3 :         typeobj.classId = TypeRelationId;
   18357                 :           3 :         typeobj.objectId = typeid;
   18358                 :           3 :         typeobj.objectSubId = 0;
   18359                 :           3 :         recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
   18360                 :             : 
   18361                 :             :         /* Update pg_class.reloftype */
   18362                 :           3 :         relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   18363                 :           3 :         classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   18364         [ +  - ]:           3 :         if (!HeapTupleIsValid(classtuple))
   18365   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", relid);
   18366                 :           3 :         ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
   18367                 :           3 :         CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
   18368                 :             : 
   18369         [ +  - ]:           3 :         InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   18370                 :             : 
   18371                 :           3 :         heap_freetuple(classtuple);
   18372                 :           3 :         table_close(relationRelation, RowExclusiveLock);
   18373                 :             : 
   18374                 :           3 :         ReleaseSysCache(typetuple);
   18375                 :             : 
   18376                 :             :         return typeobj;
   18377                 :           3 : }
   18378                 :             : 
   18379                 :             : /*
   18380                 :             :  * ALTER TABLE NOT OF
   18381                 :             :  *
   18382                 :             :  * Detach a typed table from its originating type.  Just clear reloftype and
   18383                 :             :  * remove the dependency.
   18384                 :             :  */
   18385                 :             : static void
   18386                 :           1 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
   18387                 :             : {
   18388                 :           1 :         Oid                     relid = RelationGetRelid(rel);
   18389                 :           1 :         Relation        relationRelation;
   18390                 :           1 :         HeapTuple       tuple;
   18391                 :             : 
   18392         [ +  - ]:           1 :         if (!OidIsValid(rel->rd_rel->reloftype))
   18393   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   18394                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18395                 :             :                                  errmsg("\"%s\" is not a typed table",
   18396                 :             :                                                 RelationGetRelationName(rel))));
   18397                 :             : 
   18398                 :             :         /*
   18399                 :             :          * We don't bother to check ownership of the type --- ownership of the
   18400                 :             :          * table is presumed enough rights.  No lock required on the type, either.
   18401                 :             :          */
   18402                 :             : 
   18403                 :           1 :         drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   18404                 :             :                                                    DEPENDENCY_NORMAL);
   18405                 :             : 
   18406                 :             :         /* Clear pg_class.reloftype */
   18407                 :           1 :         relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   18408                 :           1 :         tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   18409         [ +  - ]:           1 :         if (!HeapTupleIsValid(tuple))
   18410   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", relid);
   18411                 :           1 :         ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
   18412                 :           1 :         CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
   18413                 :             : 
   18414         [ +  - ]:           1 :         InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   18415                 :             : 
   18416                 :           1 :         heap_freetuple(tuple);
   18417                 :           1 :         table_close(relationRelation, RowExclusiveLock);
   18418                 :           1 : }
   18419                 :             : 
   18420                 :             : /*
   18421                 :             :  * relation_mark_replica_identity: Update a table's replica identity
   18422                 :             :  *
   18423                 :             :  * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
   18424                 :             :  * index. Otherwise, it must be InvalidOid.
   18425                 :             :  *
   18426                 :             :  * Caller had better hold an exclusive lock on the relation, as the results
   18427                 :             :  * of running two of these concurrently wouldn't be pretty.
   18428                 :             :  */
   18429                 :             : static void
   18430                 :          49 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
   18431                 :             :                                                            bool is_internal)
   18432                 :             : {
   18433                 :          49 :         Relation        pg_index;
   18434                 :          49 :         Relation        pg_class;
   18435                 :          49 :         HeapTuple       pg_class_tuple;
   18436                 :          49 :         HeapTuple       pg_index_tuple;
   18437                 :          49 :         Form_pg_class pg_class_form;
   18438                 :          49 :         Form_pg_index pg_index_form;
   18439                 :          49 :         ListCell   *index;
   18440                 :             : 
   18441                 :             :         /*
   18442                 :             :          * Check whether relreplident has changed, and update it if so.
   18443                 :             :          */
   18444                 :          49 :         pg_class = table_open(RelationRelationId, RowExclusiveLock);
   18445                 :          49 :         pg_class_tuple = SearchSysCacheCopy1(RELOID,
   18446                 :             :                                                                                  ObjectIdGetDatum(RelationGetRelid(rel)));
   18447         [ +  - ]:          49 :         if (!HeapTupleIsValid(pg_class_tuple))
   18448   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation \"%s\"",
   18449                 :             :                          RelationGetRelationName(rel));
   18450                 :          49 :         pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
   18451         [ +  + ]:          49 :         if (pg_class_form->relreplident != ri_type)
   18452                 :             :         {
   18453                 :          41 :                 pg_class_form->relreplident = ri_type;
   18454                 :          41 :                 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
   18455                 :          41 :         }
   18456                 :          49 :         table_close(pg_class, RowExclusiveLock);
   18457                 :          49 :         heap_freetuple(pg_class_tuple);
   18458                 :             : 
   18459                 :             :         /*
   18460                 :             :          * Update the per-index indisreplident flags correctly.
   18461                 :             :          */
   18462                 :          49 :         pg_index = table_open(IndexRelationId, RowExclusiveLock);
   18463   [ +  +  +  +  :         153 :         foreach(index, RelationGetIndexList(rel))
                   +  + ]
   18464                 :             :         {
   18465                 :         104 :                 Oid                     thisIndexOid = lfirst_oid(index);
   18466                 :         104 :                 bool            dirty = false;
   18467                 :             : 
   18468                 :         104 :                 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
   18469                 :             :                                                                                          ObjectIdGetDatum(thisIndexOid));
   18470         [ +  - ]:         104 :                 if (!HeapTupleIsValid(pg_index_tuple))
   18471   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
   18472                 :         104 :                 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
   18473                 :             : 
   18474         [ +  + ]:         104 :                 if (thisIndexOid == indexOid)
   18475                 :             :                 {
   18476                 :             :                         /* Set the bit if not already set. */
   18477         [ +  + ]:          30 :                         if (!pg_index_form->indisreplident)
   18478                 :             :                         {
   18479                 :          27 :                                 dirty = true;
   18480                 :          27 :                                 pg_index_form->indisreplident = true;
   18481                 :          27 :                         }
   18482                 :          30 :                 }
   18483                 :             :                 else
   18484                 :             :                 {
   18485                 :             :                         /* Unset the bit if set. */
   18486         [ +  + ]:          74 :                         if (pg_index_form->indisreplident)
   18487                 :             :                         {
   18488                 :           8 :                                 dirty = true;
   18489                 :           8 :                                 pg_index_form->indisreplident = false;
   18490                 :           8 :                         }
   18491                 :             :                 }
   18492                 :             : 
   18493         [ +  + ]:         104 :                 if (dirty)
   18494                 :             :                 {
   18495                 :          35 :                         CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
   18496         [ +  - ]:          35 :                         InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
   18497                 :             :                                                                                  InvalidOid, is_internal);
   18498                 :             : 
   18499                 :             :                         /*
   18500                 :             :                          * Invalidate the relcache for the table, so that after we commit
   18501                 :             :                          * all sessions will refresh the table's replica identity index
   18502                 :             :                          * before attempting any UPDATE or DELETE on the table.  (If we
   18503                 :             :                          * changed the table's pg_class row above, then a relcache inval
   18504                 :             :                          * is already queued due to that; but we might not have.)
   18505                 :             :                          */
   18506                 :          35 :                         CacheInvalidateRelcache(rel);
   18507                 :          35 :                 }
   18508                 :         104 :                 heap_freetuple(pg_index_tuple);
   18509                 :         104 :         }
   18510                 :             : 
   18511                 :          49 :         table_close(pg_index, RowExclusiveLock);
   18512                 :          49 : }
   18513                 :             : 
   18514                 :             : /*
   18515                 :             :  * ALTER TABLE <name> REPLICA IDENTITY ...
   18516                 :             :  */
   18517                 :             : static void
   18518                 :          57 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
   18519                 :             : {
   18520                 :          57 :         Oid                     indexOid;
   18521                 :          57 :         Relation        indexRel;
   18522                 :          57 :         int                     key;
   18523                 :             : 
   18524         [ +  + ]:          57 :         if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
   18525                 :             :         {
   18526                 :           1 :                 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   18527                 :           1 :                 return;
   18528                 :             :         }
   18529         [ +  + ]:          56 :         else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
   18530                 :             :         {
   18531                 :          13 :                 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   18532                 :          13 :                 return;
   18533                 :             :         }
   18534         [ +  + ]:          43 :         else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
   18535                 :             :         {
   18536                 :           5 :                 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   18537                 :           5 :                 return;
   18538                 :             :         }
   18539         [ +  - ]:          38 :         else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
   18540                 :             :         {
   18541                 :             :                  /* fallthrough */ ;
   18542                 :          38 :         }
   18543                 :             :         else
   18544   [ #  #  #  # ]:           0 :                 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
   18545                 :             : 
   18546                 :             :         /* Check that the index exists */
   18547                 :          38 :         indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
   18548         [ +  - ]:          38 :         if (!OidIsValid(indexOid))
   18549   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   18550                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   18551                 :             :                                  errmsg("index \"%s\" for table \"%s\" does not exist",
   18552                 :             :                                                 stmt->name, RelationGetRelationName(rel))));
   18553                 :             : 
   18554                 :          38 :         indexRel = index_open(indexOid, ShareLock);
   18555                 :             : 
   18556                 :             :         /* Check that the index is on the relation we're altering. */
   18557         [ +  + ]:          38 :         if (indexRel->rd_index == NULL ||
   18558                 :          37 :                 indexRel->rd_index->indrelid != RelationGetRelid(rel))
   18559   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   18560                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18561                 :             :                                  errmsg("\"%s\" is not an index for table \"%s\"",
   18562                 :             :                                                 RelationGetRelationName(indexRel),
   18563                 :             :                                                 RelationGetRelationName(rel))));
   18564                 :             : 
   18565                 :             :         /*
   18566                 :             :          * The AM must support uniqueness, and the index must in fact be unique.
   18567                 :             :          * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
   18568                 :             :          * exclusion), we can use that too.
   18569                 :             :          */
   18570         [ +  + ]:          37 :         if ((!indexRel->rd_indam->amcanunique ||
   18571         [ +  + ]:          36 :                  !indexRel->rd_index->indisunique) &&
   18572         [ +  + ]:           3 :                 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
   18573   [ +  -  +  - ]:           2 :                 ereport(ERROR,
   18574                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18575                 :             :                                  errmsg("cannot use non-unique index \"%s\" as replica identity",
   18576                 :             :                                                 RelationGetRelationName(indexRel))));
   18577                 :             :         /* Deferred indexes are not guaranteed to be always unique. */
   18578         [ +  + ]:          35 :         if (!indexRel->rd_index->indimmediate)
   18579   [ +  -  +  - ]:           2 :                 ereport(ERROR,
   18580                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   18581                 :             :                                  errmsg("cannot use non-immediate index \"%s\" as replica identity",
   18582                 :             :                                                 RelationGetRelationName(indexRel))));
   18583                 :             :         /* Expression indexes aren't supported. */
   18584         [ +  + ]:          33 :         if (RelationGetIndexExpressions(indexRel) != NIL)
   18585   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   18586                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   18587                 :             :                                  errmsg("cannot use expression index \"%s\" as replica identity",
   18588                 :             :                                                 RelationGetRelationName(indexRel))));
   18589                 :             :         /* Predicate indexes aren't supported. */
   18590         [ +  + ]:          32 :         if (RelationGetIndexPredicate(indexRel) != NIL)
   18591   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   18592                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   18593                 :             :                                  errmsg("cannot use partial index \"%s\" as replica identity",
   18594                 :             :                                                 RelationGetRelationName(indexRel))));
   18595                 :             : 
   18596                 :             :         /* Check index for nullable columns. */
   18597         [ +  + ]:          70 :         for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
   18598                 :             :         {
   18599                 :          40 :                 int16           attno = indexRel->rd_index->indkey.values[key];
   18600                 :          40 :                 Form_pg_attribute attr;
   18601                 :             : 
   18602                 :             :                 /*
   18603                 :             :                  * Reject any other system columns.  (Going forward, we'll disallow
   18604                 :             :                  * indexes containing such columns in the first place, but they might
   18605                 :             :                  * exist in older branches.)
   18606                 :             :                  */
   18607         [ +  - ]:          40 :                 if (attno <= 0)
   18608   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   18609                 :             :                                         (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   18610                 :             :                                          errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
   18611                 :             :                                                         RelationGetRelationName(indexRel), attno)));
   18612                 :             : 
   18613                 :          40 :                 attr = TupleDescAttr(rel->rd_att, attno - 1);
   18614         [ +  + ]:          40 :                 if (!attr->attnotnull)
   18615   [ +  -  +  - ]:           1 :                         ereport(ERROR,
   18616                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18617                 :             :                                          errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
   18618                 :             :                                                         RelationGetRelationName(indexRel),
   18619                 :             :                                                         NameStr(attr->attname))));
   18620                 :          39 :         }
   18621                 :             : 
   18622                 :             :         /* This index is suitable for use as a replica identity. Mark it. */
   18623                 :          30 :         relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
   18624                 :             : 
   18625                 :          30 :         index_close(indexRel, NoLock);
   18626         [ -  + ]:          49 : }
   18627                 :             : 
   18628                 :             : /*
   18629                 :             :  * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
   18630                 :             :  */
   18631                 :             : static void
   18632                 :          54 : ATExecSetRowSecurity(Relation rel, bool rls)
   18633                 :             : {
   18634                 :          54 :         Relation        pg_class;
   18635                 :          54 :         Oid                     relid;
   18636                 :          54 :         HeapTuple       tuple;
   18637                 :             : 
   18638                 :          54 :         relid = RelationGetRelid(rel);
   18639                 :             : 
   18640                 :             :         /* Pull the record for this relation and update it */
   18641                 :          54 :         pg_class = table_open(RelationRelationId, RowExclusiveLock);
   18642                 :             : 
   18643                 :          54 :         tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   18644                 :             : 
   18645         [ +  - ]:          54 :         if (!HeapTupleIsValid(tuple))
   18646   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", relid);
   18647                 :             : 
   18648                 :          54 :         ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
   18649                 :          54 :         CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   18650                 :             : 
   18651         [ +  - ]:          54 :         InvokeObjectPostAlterHook(RelationRelationId,
   18652                 :             :                                                           RelationGetRelid(rel), 0);
   18653                 :             : 
   18654                 :          54 :         table_close(pg_class, RowExclusiveLock);
   18655                 :          54 :         heap_freetuple(tuple);
   18656                 :          54 : }
   18657                 :             : 
   18658                 :             : /*
   18659                 :             :  * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
   18660                 :             :  */
   18661                 :             : static void
   18662                 :          19 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
   18663                 :             : {
   18664                 :          19 :         Relation        pg_class;
   18665                 :          19 :         Oid                     relid;
   18666                 :          19 :         HeapTuple       tuple;
   18667                 :             : 
   18668                 :          19 :         relid = RelationGetRelid(rel);
   18669                 :             : 
   18670                 :          19 :         pg_class = table_open(RelationRelationId, RowExclusiveLock);
   18671                 :             : 
   18672                 :          19 :         tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   18673                 :             : 
   18674         [ +  - ]:          19 :         if (!HeapTupleIsValid(tuple))
   18675   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", relid);
   18676                 :             : 
   18677                 :          19 :         ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
   18678                 :          19 :         CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   18679                 :             : 
   18680         [ +  - ]:          19 :         InvokeObjectPostAlterHook(RelationRelationId,
   18681                 :             :                                                           RelationGetRelid(rel), 0);
   18682                 :             : 
   18683                 :          19 :         table_close(pg_class, RowExclusiveLock);
   18684                 :          19 :         heap_freetuple(tuple);
   18685                 :          19 : }
   18686                 :             : 
   18687                 :             : /*
   18688                 :             :  * ALTER FOREIGN TABLE <name> OPTIONS (...)
   18689                 :             :  */
   18690                 :             : static void
   18691                 :           1 : ATExecGenericOptions(Relation rel, List *options)
   18692                 :             : {
   18693                 :           1 :         Relation        ftrel;
   18694                 :           1 :         ForeignServer *server;
   18695                 :           1 :         ForeignDataWrapper *fdw;
   18696                 :           1 :         HeapTuple       tuple;
   18697                 :           1 :         bool            isnull;
   18698                 :           1 :         Datum           repl_val[Natts_pg_foreign_table];
   18699                 :           1 :         bool            repl_null[Natts_pg_foreign_table];
   18700                 :           1 :         bool            repl_repl[Natts_pg_foreign_table];
   18701                 :           1 :         Datum           datum;
   18702                 :           1 :         Form_pg_foreign_table tableform;
   18703                 :             : 
   18704         [ +  - ]:           1 :         if (options == NIL)
   18705                 :           0 :                 return;
   18706                 :             : 
   18707                 :           1 :         ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
   18708                 :             : 
   18709                 :           1 :         tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
   18710                 :             :                                                                 ObjectIdGetDatum(rel->rd_id));
   18711         [ +  - ]:           1 :         if (!HeapTupleIsValid(tuple))
   18712   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   18713                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   18714                 :             :                                  errmsg("foreign table \"%s\" does not exist",
   18715                 :             :                                                 RelationGetRelationName(rel))));
   18716                 :           1 :         tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   18717                 :           1 :         server = GetForeignServer(tableform->ftserver);
   18718                 :           1 :         fdw = GetForeignDataWrapper(server->fdwid);
   18719                 :             : 
   18720                 :           1 :         memset(repl_val, 0, sizeof(repl_val));
   18721                 :           1 :         memset(repl_null, false, sizeof(repl_null));
   18722                 :           1 :         memset(repl_repl, false, sizeof(repl_repl));
   18723                 :             : 
   18724                 :             :         /* Extract the current options */
   18725                 :           1 :         datum = SysCacheGetAttr(FOREIGNTABLEREL,
   18726                 :           1 :                                                         tuple,
   18727                 :             :                                                         Anum_pg_foreign_table_ftoptions,
   18728                 :             :                                                         &isnull);
   18729         [ +  - ]:           1 :         if (isnull)
   18730                 :           0 :                 datum = PointerGetDatum(NULL);
   18731                 :             : 
   18732                 :             :         /* Transform the options */
   18733                 :           1 :         datum = transformGenericOptions(ForeignTableRelationId,
   18734                 :           1 :                                                                         datum,
   18735                 :           1 :                                                                         options,
   18736                 :           1 :                                                                         fdw->fdwvalidator);
   18737                 :             : 
   18738         [ +  - ]:           1 :         if (DatumGetPointer(datum) != NULL)
   18739                 :           1 :                 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
   18740                 :             :         else
   18741                 :           0 :                 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
   18742                 :             : 
   18743                 :           1 :         repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
   18744                 :             : 
   18745                 :             :         /* Everything looks good - update the tuple */
   18746                 :             : 
   18747                 :           2 :         tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
   18748                 :           1 :                                                           repl_val, repl_null, repl_repl);
   18749                 :             : 
   18750                 :           1 :         CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
   18751                 :             : 
   18752                 :             :         /*
   18753                 :             :          * Invalidate relcache so that all sessions will refresh any cached plans
   18754                 :             :          * that might depend on the old options.
   18755                 :             :          */
   18756                 :           1 :         CacheInvalidateRelcache(rel);
   18757                 :             : 
   18758         [ +  - ]:           1 :         InvokeObjectPostAlterHook(ForeignTableRelationId,
   18759                 :             :                                                           RelationGetRelid(rel), 0);
   18760                 :             : 
   18761                 :           1 :         table_close(ftrel, RowExclusiveLock);
   18762                 :             : 
   18763                 :           1 :         heap_freetuple(tuple);
   18764         [ -  + ]:           1 : }
   18765                 :             : 
   18766                 :             : /*
   18767                 :             :  * ALTER TABLE ALTER COLUMN SET COMPRESSION
   18768                 :             :  *
   18769                 :             :  * Return value is the address of the modified column
   18770                 :             :  */
   18771                 :             : static ObjectAddress
   18772                 :           7 : ATExecSetCompression(Relation rel,
   18773                 :             :                                          const char *column,
   18774                 :             :                                          Node *newValue,
   18775                 :             :                                          LOCKMODE lockmode)
   18776                 :             : {
   18777                 :           7 :         Relation        attrel;
   18778                 :           7 :         HeapTuple       tuple;
   18779                 :           7 :         Form_pg_attribute atttableform;
   18780                 :           7 :         AttrNumber      attnum;
   18781                 :           7 :         char       *compression;
   18782                 :           7 :         char            cmethod;
   18783                 :             :         ObjectAddress address;
   18784                 :             : 
   18785                 :           7 :         compression = strVal(newValue);
   18786                 :             : 
   18787                 :           7 :         attrel = table_open(AttributeRelationId, RowExclusiveLock);
   18788                 :             : 
   18789                 :             :         /* copy the cache entry so we can scribble on it below */
   18790                 :           7 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
   18791         [ +  - ]:           7 :         if (!HeapTupleIsValid(tuple))
   18792   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   18793                 :             :                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   18794                 :             :                                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   18795                 :             :                                                 column, RelationGetRelationName(rel))));
   18796                 :             : 
   18797                 :             :         /* prevent them from altering a system attribute */
   18798                 :           7 :         atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   18799                 :           7 :         attnum = atttableform->attnum;
   18800         [ +  - ]:           7 :         if (attnum <= 0)
   18801   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   18802                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   18803                 :             :                                  errmsg("cannot alter system column \"%s\"", column)));
   18804                 :             : 
   18805                 :             :         /*
   18806                 :             :          * Check that column type is compressible, then get the attribute
   18807                 :             :          * compression method code
   18808                 :             :          */
   18809                 :           7 :         cmethod = GetAttributeCompression(atttableform->atttypid, compression);
   18810                 :             : 
   18811                 :             :         /* update pg_attribute entry */
   18812                 :           7 :         atttableform->attcompression = cmethod;
   18813                 :           7 :         CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
   18814                 :             : 
   18815         [ +  - ]:           7 :         InvokeObjectPostAlterHook(RelationRelationId,
   18816                 :             :                                                           RelationGetRelid(rel),
   18817                 :             :                                                           attnum);
   18818                 :             : 
   18819                 :             :         /*
   18820                 :             :          * Apply the change to indexes as well (only for simple index columns,
   18821                 :             :          * matching behavior of index.c ConstructTupleDescriptor()).
   18822                 :             :          */
   18823                 :          14 :         SetIndexStorageProperties(rel, attrel, attnum,
   18824                 :             :                                                           false, 0,
   18825                 :           7 :                                                           true, cmethod,
   18826                 :           7 :                                                           lockmode);
   18827                 :             : 
   18828                 :           7 :         heap_freetuple(tuple);
   18829                 :             : 
   18830                 :           7 :         table_close(attrel, RowExclusiveLock);
   18831                 :             : 
   18832                 :             :         /* make changes visible */
   18833                 :           7 :         CommandCounterIncrement();
   18834                 :             : 
   18835                 :           7 :         ObjectAddressSubSet(address, RelationRelationId,
   18836                 :             :                                                 RelationGetRelid(rel), attnum);
   18837                 :             :         return address;
   18838                 :           7 : }
   18839                 :             : 
   18840                 :             : 
   18841                 :             : /*
   18842                 :             :  * Preparation phase for SET LOGGED/UNLOGGED
   18843                 :             :  *
   18844                 :             :  * This verifies that we're not trying to change a temp table.  Also,
   18845                 :             :  * existing foreign key constraints are checked to avoid ending up with
   18846                 :             :  * permanent tables referencing unlogged tables.
   18847                 :             :  */
   18848                 :             : static void
   18849                 :          16 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
   18850                 :             : {
   18851                 :          16 :         Relation        pg_constraint;
   18852                 :          16 :         HeapTuple       tuple;
   18853                 :          16 :         SysScanDesc scan;
   18854                 :          16 :         ScanKeyData skey[1];
   18855                 :             : 
   18856                 :             :         /*
   18857                 :             :          * Disallow changing status for a temp table.  Also verify whether we can
   18858                 :             :          * get away with doing nothing; in such cases we don't need to run the
   18859                 :             :          * checks below, either.
   18860                 :             :          */
   18861   [ -  +  +  - ]:          16 :         switch (rel->rd_rel->relpersistence)
   18862                 :             :         {
   18863                 :             :                 case RELPERSISTENCE_TEMP:
   18864   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   18865                 :             :                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   18866                 :             :                                          errmsg("cannot change logged status of table \"%s\" because it is temporary",
   18867                 :             :                                                         RelationGetRelationName(rel)),
   18868                 :             :                                          errtable(rel)));
   18869                 :           0 :                         break;
   18870                 :             :                 case RELPERSISTENCE_PERMANENT:
   18871         [ +  + ]:           9 :                         if (toLogged)
   18872                 :             :                                 /* nothing to do */
   18873                 :           1 :                                 return;
   18874                 :           8 :                         break;
   18875                 :             :                 case RELPERSISTENCE_UNLOGGED:
   18876         [ +  + ]:           7 :                         if (!toLogged)
   18877                 :             :                                 /* nothing to do */
   18878                 :           1 :                                 return;
   18879                 :           6 :                         break;
   18880                 :             :         }
   18881                 :             : 
   18882                 :             :         /*
   18883                 :             :          * Check that the table is not part of any publication when changing to
   18884                 :             :          * UNLOGGED, as UNLOGGED tables can't be published.
   18885                 :             :          */
   18886   [ +  +  +  - ]:          14 :         if (!toLogged &&
   18887                 :           8 :                 GetRelationPublications(RelationGetRelid(rel)) != NIL)
   18888   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   18889                 :             :                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   18890                 :             :                                  errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
   18891                 :             :                                                 RelationGetRelationName(rel)),
   18892                 :             :                                  errdetail("Unlogged relations cannot be replicated.")));
   18893                 :             : 
   18894                 :             :         /*
   18895                 :             :          * Check existing foreign key constraints to preserve the invariant that
   18896                 :             :          * permanent tables cannot reference unlogged ones.  Self-referencing
   18897                 :             :          * foreign keys can safely be ignored.
   18898                 :             :          */
   18899                 :          14 :         pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   18900                 :             : 
   18901                 :             :         /*
   18902                 :             :          * Scan conrelid if changing to permanent, else confrelid.  This also
   18903                 :             :          * determines whether a useful index exists.
   18904                 :             :          */
   18905                 :          28 :         ScanKeyInit(&skey[0],
   18906                 :          14 :                                 toLogged ? Anum_pg_constraint_conrelid :
   18907                 :             :                                 Anum_pg_constraint_confrelid,
   18908                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   18909                 :          14 :                                 ObjectIdGetDatum(RelationGetRelid(rel)));
   18910                 :          28 :         scan = systable_beginscan(pg_constraint,
   18911                 :          14 :                                                           toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
   18912                 :          14 :                                                           true, NULL, 1, skey);
   18913                 :             : 
   18914         [ +  + ]:          23 :         while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   18915                 :             :         {
   18916                 :          11 :                 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
   18917                 :             : 
   18918         [ +  + ]:          11 :                 if (con->contype == CONSTRAINT_FOREIGN)
   18919                 :             :                 {
   18920                 :           5 :                         Oid                     foreignrelid;
   18921                 :           5 :                         Relation        foreignrel;
   18922                 :             : 
   18923                 :             :                         /* the opposite end of what we used as scankey */
   18924         [ +  + ]:           5 :                         foreignrelid = toLogged ? con->confrelid : con->conrelid;
   18925                 :             : 
   18926                 :             :                         /* ignore if self-referencing */
   18927         [ +  + ]:           5 :                         if (RelationGetRelid(rel) == foreignrelid)
   18928                 :           2 :                                 continue;
   18929                 :             : 
   18930                 :           3 :                         foreignrel = relation_open(foreignrelid, AccessShareLock);
   18931                 :             : 
   18932         [ +  + ]:           3 :                         if (toLogged)
   18933                 :             :                         {
   18934         [ -  + ]:           1 :                                 if (!RelationIsPermanent(foreignrel))
   18935   [ +  -  +  - ]:           1 :                                         ereport(ERROR,
   18936                 :             :                                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   18937                 :             :                                                          errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
   18938                 :             :                                                                         RelationGetRelationName(rel),
   18939                 :             :                                                                         RelationGetRelationName(foreignrel)),
   18940                 :             :                                                          errtableconstraint(rel, NameStr(con->conname))));
   18941                 :           0 :                         }
   18942                 :             :                         else
   18943                 :             :                         {
   18944         [ +  + ]:           2 :                                 if (RelationIsPermanent(foreignrel))
   18945   [ +  -  +  - ]:           1 :                                         ereport(ERROR,
   18946                 :             :                                                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   18947                 :             :                                                          errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
   18948                 :             :                                                                         RelationGetRelationName(rel),
   18949                 :             :                                                                         RelationGetRelationName(foreignrel)),
   18950                 :             :                                                          errtableconstraint(rel, NameStr(con->conname))));
   18951                 :             :                         }
   18952                 :             : 
   18953                 :           1 :                         relation_close(foreignrel, AccessShareLock);
   18954         [ +  + ]:           3 :                 }
   18955         [ +  + ]:           9 :         }
   18956                 :             : 
   18957                 :          12 :         systable_endscan(scan);
   18958                 :             : 
   18959                 :          12 :         table_close(pg_constraint, AccessShareLock);
   18960                 :             : 
   18961                 :             :         /* force rewrite if necessary; see comment in ATRewriteTables */
   18962                 :          12 :         tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
   18963         [ +  + ]:          12 :         if (toLogged)
   18964                 :           5 :                 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
   18965                 :             :         else
   18966                 :           7 :                 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
   18967                 :          12 :         tab->chgPersistence = true;
   18968                 :          14 : }
   18969                 :             : 
   18970                 :             : /*
   18971                 :             :  * Execute ALTER TABLE SET SCHEMA
   18972                 :             :  */
   18973                 :             : ObjectAddress
   18974                 :          16 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
   18975                 :             : {
   18976                 :          16 :         Relation        rel;
   18977                 :          16 :         Oid                     relid;
   18978                 :          16 :         Oid                     oldNspOid;
   18979                 :          16 :         Oid                     nspOid;
   18980                 :          16 :         RangeVar   *newrv;
   18981                 :          16 :         ObjectAddresses *objsMoved;
   18982                 :          16 :         ObjectAddress myself;
   18983                 :             : 
   18984                 :          32 :         relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
   18985                 :          16 :                                                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
   18986                 :             :                                                                          RangeVarCallbackForAlterRelation,
   18987                 :          16 :                                                                          stmt);
   18988                 :             : 
   18989         [ +  + ]:          16 :         if (!OidIsValid(relid))
   18990                 :             :         {
   18991   [ -  +  +  - ]:           2 :                 ereport(NOTICE,
   18992                 :             :                                 (errmsg("relation \"%s\" does not exist, skipping",
   18993                 :             :                                                 stmt->relation->relname)));
   18994                 :           2 :                 return InvalidObjectAddress;
   18995                 :             :         }
   18996                 :             : 
   18997                 :          14 :         rel = relation_open(relid, NoLock);
   18998                 :             : 
   18999                 :          14 :         oldNspOid = RelationGetNamespace(rel);
   19000                 :             : 
   19001                 :             :         /* If it's an owned sequence, disallow moving it by itself. */
   19002         [ +  + ]:          14 :         if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
   19003                 :             :         {
   19004                 :           1 :                 Oid                     tableId;
   19005                 :           1 :                 int32           colId;
   19006                 :             : 
   19007         [ -  + ]:           1 :                 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
   19008                 :           0 :                         sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
   19009   [ +  -  +  - ]:           1 :                         ereport(ERROR,
   19010                 :             :                                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   19011                 :             :                                          errmsg("cannot move an owned sequence into another schema"),
   19012                 :             :                                          errdetail("Sequence \"%s\" is linked to table \"%s\".",
   19013                 :             :                                                            RelationGetRelationName(rel),
   19014                 :             :                                                            get_rel_name(tableId))));
   19015                 :           0 :         }
   19016                 :             : 
   19017                 :             :         /* Get and lock schema OID and check its permissions. */
   19018                 :          13 :         newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
   19019                 :          13 :         nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
   19020                 :             : 
   19021                 :             :         /* common checks on switching namespaces */
   19022                 :          13 :         CheckSetNamespace(oldNspOid, nspOid);
   19023                 :             : 
   19024                 :          13 :         objsMoved = new_object_addresses();
   19025                 :          13 :         AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
   19026                 :          13 :         free_object_addresses(objsMoved);
   19027                 :             : 
   19028                 :          13 :         ObjectAddressSet(myself, RelationRelationId, relid);
   19029                 :             : 
   19030         [ -  + ]:          13 :         if (oldschema)
   19031                 :          13 :                 *oldschema = oldNspOid;
   19032                 :             : 
   19033                 :             :         /* close rel, but keep lock until commit */
   19034                 :          13 :         relation_close(rel, NoLock);
   19035                 :             : 
   19036                 :          13 :         return myself;
   19037                 :          15 : }
   19038                 :             : 
   19039                 :             : /*
   19040                 :             :  * The guts of relocating a table or materialized view to another namespace:
   19041                 :             :  * besides moving the relation itself, its dependent objects are relocated to
   19042                 :             :  * the new schema.
   19043                 :             :  */
   19044                 :             : void
   19045                 :          13 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
   19046                 :             :                                                         ObjectAddresses *objsMoved)
   19047                 :             : {
   19048                 :          13 :         Relation        classRel;
   19049                 :             : 
   19050         [ +  - ]:          13 :         Assert(objsMoved != NULL);
   19051                 :             : 
   19052                 :             :         /* OK, modify the pg_class row and pg_depend entry */
   19053                 :          13 :         classRel = table_open(RelationRelationId, RowExclusiveLock);
   19054                 :             : 
   19055                 :          26 :         AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
   19056                 :          13 :                                                                    nspOid, true, objsMoved);
   19057                 :             : 
   19058                 :             :         /* Fix the table's row type too, if it has one */
   19059         [ -  + ]:          13 :         if (OidIsValid(rel->rd_rel->reltype))
   19060                 :          26 :                 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
   19061                 :             :                                                                    false,       /* isImplicitArray */
   19062                 :             :                                                                    false,       /* ignoreDependent */
   19063                 :             :                                                                    false,       /* errorOnTableType */
   19064                 :          13 :                                                                    objsMoved);
   19065                 :             : 
   19066                 :             :         /* Fix other dependent stuff */
   19067                 :          13 :         AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
   19068                 :          26 :         AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
   19069                 :          13 :                                            objsMoved, AccessExclusiveLock);
   19070                 :          26 :         AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
   19071                 :          13 :                                                           false, objsMoved);
   19072                 :             : 
   19073                 :          13 :         table_close(classRel, RowExclusiveLock);
   19074                 :          13 : }
   19075                 :             : 
   19076                 :             : /*
   19077                 :             :  * The guts of relocating a relation to another namespace: fix the pg_class
   19078                 :             :  * entry, and the pg_depend entry if any.  Caller must already have
   19079                 :             :  * opened and write-locked pg_class.
   19080                 :             :  */
   19081                 :             : void
   19082                 :          29 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
   19083                 :             :                                                            Oid oldNspOid, Oid newNspOid,
   19084                 :             :                                                            bool hasDependEntry,
   19085                 :             :                                                            ObjectAddresses *objsMoved)
   19086                 :             : {
   19087                 :          29 :         HeapTuple       classTup;
   19088                 :          29 :         Form_pg_class classForm;
   19089                 :          29 :         ObjectAddress thisobj;
   19090                 :          29 :         bool            already_done = false;
   19091                 :             : 
   19092                 :             :         /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
   19093                 :          29 :         classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
   19094         [ +  - ]:          29 :         if (!HeapTupleIsValid(classTup))
   19095   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", relOid);
   19096                 :          29 :         classForm = (Form_pg_class) GETSTRUCT(classTup);
   19097                 :             : 
   19098         [ +  - ]:          29 :         Assert(classForm->relnamespace == oldNspOid);
   19099                 :             : 
   19100                 :          29 :         thisobj.classId = RelationRelationId;
   19101                 :          29 :         thisobj.objectId = relOid;
   19102                 :          29 :         thisobj.objectSubId = 0;
   19103                 :             : 
   19104                 :             :         /*
   19105                 :             :          * If the object has already been moved, don't move it again.  If it's
   19106                 :             :          * already in the right place, don't move it, but still fire the object
   19107                 :             :          * access hook.
   19108                 :             :          */
   19109                 :          29 :         already_done = object_address_present(&thisobj, objsMoved);
   19110   [ +  -  +  + ]:          29 :         if (!already_done && oldNspOid != newNspOid)
   19111                 :             :         {
   19112                 :          22 :                 ItemPointerData otid = classTup->t_self;
   19113                 :             : 
   19114                 :             :                 /* check for duplicate name (more friendly than unique-index failure) */
   19115                 :          44 :                 if (get_relname_relid(NameStr(classForm->relname),
   19116   [ +  -  +  - ]:          44 :                                                           newNspOid) != InvalidOid)
   19117   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   19118                 :             :                                         (errcode(ERRCODE_DUPLICATE_TABLE),
   19119                 :             :                                          errmsg("relation \"%s\" already exists in schema \"%s\"",
   19120                 :             :                                                         NameStr(classForm->relname),
   19121                 :             :                                                         get_namespace_name(newNspOid))));
   19122                 :             : 
   19123                 :             :                 /* classTup is a copy, so OK to scribble on */
   19124                 :          22 :                 classForm->relnamespace = newNspOid;
   19125                 :             : 
   19126                 :          22 :                 CatalogTupleUpdate(classRel, &otid, classTup);
   19127                 :          22 :                 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
   19128                 :             : 
   19129                 :             : 
   19130                 :             :                 /* Update dependency on schema if caller said so */
   19131   [ +  +  +  - ]:          22 :                 if (hasDependEntry &&
   19132                 :          16 :                         changeDependencyFor(RelationRelationId,
   19133                 :          16 :                                                                 relOid,
   19134                 :             :                                                                 NamespaceRelationId,
   19135                 :          16 :                                                                 oldNspOid,
   19136                 :          32 :                                                                 newNspOid) != 1)
   19137   [ #  #  #  # ]:           0 :                         elog(ERROR, "could not change schema dependency for relation \"%s\"",
   19138                 :             :                                  NameStr(classForm->relname));
   19139                 :          22 :         }
   19140                 :             :         else
   19141                 :           7 :                 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
   19142         [ -  + ]:          29 :         if (!already_done)
   19143                 :             :         {
   19144                 :          29 :                 add_exact_object_address(&thisobj, objsMoved);
   19145                 :             : 
   19146         [ +  - ]:          29 :                 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
   19147                 :          29 :         }
   19148                 :             : 
   19149                 :          29 :         heap_freetuple(classTup);
   19150                 :          29 : }
   19151                 :             : 
   19152                 :             : /*
   19153                 :             :  * Move all indexes for the specified relation to another namespace.
   19154                 :             :  *
   19155                 :             :  * Note: we assume adequate permission checking was done by the caller,
   19156                 :             :  * and that the caller has a suitable lock on the owning relation.
   19157                 :             :  */
   19158                 :             : static void
   19159                 :          13 : AlterIndexNamespaces(Relation classRel, Relation rel,
   19160                 :             :                                          Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
   19161                 :             : {
   19162                 :          13 :         List       *indexList;
   19163                 :          13 :         ListCell   *l;
   19164                 :             : 
   19165                 :          13 :         indexList = RelationGetIndexList(rel);
   19166                 :             : 
   19167   [ +  +  +  +  :          20 :         foreach(l, indexList)
                   +  + ]
   19168                 :             :         {
   19169                 :           7 :                 Oid                     indexOid = lfirst_oid(l);
   19170                 :           7 :                 ObjectAddress thisobj;
   19171                 :             : 
   19172                 :           7 :                 thisobj.classId = RelationRelationId;
   19173                 :           7 :                 thisobj.objectId = indexOid;
   19174                 :           7 :                 thisobj.objectSubId = 0;
   19175                 :             : 
   19176                 :             :                 /*
   19177                 :             :                  * Note: currently, the index will not have its own dependency on the
   19178                 :             :                  * namespace, so we don't need to do changeDependencyFor(). There's no
   19179                 :             :                  * row type in pg_type, either.
   19180                 :             :                  *
   19181                 :             :                  * XXX this objsMoved test may be pointless -- surely we have a single
   19182                 :             :                  * dependency link from a relation to each index?
   19183                 :             :                  */
   19184         [ -  + ]:           7 :                 if (!object_address_present(&thisobj, objsMoved))
   19185                 :             :                 {
   19186                 :          14 :                         AlterRelationNamespaceInternal(classRel, indexOid,
   19187                 :           7 :                                                                                    oldNspOid, newNspOid,
   19188                 :           7 :                                                                                    false, objsMoved);
   19189                 :           7 :                         add_exact_object_address(&thisobj, objsMoved);
   19190                 :           7 :                 }
   19191                 :           7 :         }
   19192                 :             : 
   19193                 :          13 :         list_free(indexList);
   19194                 :          13 : }
   19195                 :             : 
   19196                 :             : /*
   19197                 :             :  * Move all identity and SERIAL-column sequences of the specified relation to another
   19198                 :             :  * namespace.
   19199                 :             :  *
   19200                 :             :  * Note: we assume adequate permission checking was done by the caller,
   19201                 :             :  * and that the caller has a suitable lock on the owning relation.
   19202                 :             :  */
   19203                 :             : static void
   19204                 :          13 : AlterSeqNamespaces(Relation classRel, Relation rel,
   19205                 :             :                                    Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
   19206                 :             :                                    LOCKMODE lockmode)
   19207                 :             : {
   19208                 :          13 :         Relation        depRel;
   19209                 :          13 :         SysScanDesc scan;
   19210                 :          13 :         ScanKeyData key[2];
   19211                 :          13 :         HeapTuple       tup;
   19212                 :             : 
   19213                 :             :         /*
   19214                 :             :          * SERIAL sequences are those having an auto dependency on one of the
   19215                 :             :          * table's columns (we don't care *which* column, exactly).
   19216                 :             :          */
   19217                 :          13 :         depRel = table_open(DependRelationId, AccessShareLock);
   19218                 :             : 
   19219                 :          26 :         ScanKeyInit(&key[0],
   19220                 :             :                                 Anum_pg_depend_refclassid,
   19221                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   19222                 :          13 :                                 ObjectIdGetDatum(RelationRelationId));
   19223                 :          26 :         ScanKeyInit(&key[1],
   19224                 :             :                                 Anum_pg_depend_refobjid,
   19225                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   19226                 :          13 :                                 ObjectIdGetDatum(RelationGetRelid(rel)));
   19227                 :             :         /* we leave refobjsubid unspecified */
   19228                 :             : 
   19229                 :          26 :         scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   19230                 :          13 :                                                           NULL, 2, key);
   19231                 :             : 
   19232         [ +  + ]:          97 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
   19233                 :             :         {
   19234                 :          84 :                 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   19235                 :          84 :                 Relation        seqRel;
   19236                 :             : 
   19237                 :             :                 /* skip dependencies other than auto dependencies on columns */
   19238         [ +  + ]:          84 :                 if (depForm->refobjsubid == 0 ||
   19239         [ +  + ]:          61 :                         depForm->classid != RelationRelationId ||
   19240   [ +  -  #  # ]:           7 :                         depForm->objsubid != 0 ||
   19241         [ -  + ]:           7 :                         !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   19242                 :          77 :                         continue;
   19243                 :             : 
   19244                 :             :                 /* Use relation_open just in case it's an index */
   19245                 :           7 :                 seqRel = relation_open(depForm->objid, lockmode);
   19246                 :             : 
   19247                 :             :                 /* skip non-sequence relations */
   19248         [ -  + ]:           7 :                 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   19249                 :             :                 {
   19250                 :             :                         /* No need to keep the lock */
   19251                 :           0 :                         relation_close(seqRel, lockmode);
   19252                 :           0 :                         continue;
   19253                 :             :                 }
   19254                 :             : 
   19255                 :             :                 /* Fix the pg_class and pg_depend entries */
   19256                 :          14 :                 AlterRelationNamespaceInternal(classRel, depForm->objid,
   19257                 :           7 :                                                                            oldNspOid, newNspOid,
   19258                 :           7 :                                                                            true, objsMoved);
   19259                 :             : 
   19260                 :             :                 /*
   19261                 :             :                  * Sequences used to have entries in pg_type, but no longer do.  If we
   19262                 :             :                  * ever re-instate that, we'll need to move the pg_type entry to the
   19263                 :             :                  * new namespace, too (using AlterTypeNamespaceInternal).
   19264                 :             :                  */
   19265         [ -  + ]:           7 :                 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
   19266                 :             : 
   19267                 :             :                 /* Now we can close it.  Keep the lock till end of transaction. */
   19268                 :           7 :                 relation_close(seqRel, NoLock);
   19269      [ -  +  + ]:          84 :         }
   19270                 :             : 
   19271                 :          13 :         systable_endscan(scan);
   19272                 :             : 
   19273                 :          13 :         relation_close(depRel, AccessShareLock);
   19274                 :          13 : }
   19275                 :             : 
   19276                 :             : 
   19277                 :             : /*
   19278                 :             :  * This code supports
   19279                 :             :  *      CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
   19280                 :             :  *
   19281                 :             :  * Because we only support this for TEMP tables, it's sufficient to remember
   19282                 :             :  * the state in a backend-local data structure.
   19283                 :             :  */
   19284                 :             : 
   19285                 :             : /*
   19286                 :             :  * Register a newly-created relation's ON COMMIT action.
   19287                 :             :  */
   19288                 :             : void
   19289                 :          29 : register_on_commit_action(Oid relid, OnCommitAction action)
   19290                 :             : {
   19291                 :          29 :         OnCommitItem *oc;
   19292                 :          29 :         MemoryContext oldcxt;
   19293                 :             : 
   19294                 :             :         /*
   19295                 :             :          * We needn't bother registering the relation unless there is an ON COMMIT
   19296                 :             :          * action we need to take.
   19297                 :             :          */
   19298   [ +  -  +  + ]:          29 :         if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
   19299                 :           4 :                 return;
   19300                 :             : 
   19301                 :          25 :         oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
   19302                 :             : 
   19303                 :          25 :         oc = palloc_object(OnCommitItem);
   19304                 :          25 :         oc->relid = relid;
   19305                 :          25 :         oc->oncommit = action;
   19306                 :          25 :         oc->creating_subid = GetCurrentSubTransactionId();
   19307                 :          25 :         oc->deleting_subid = InvalidSubTransactionId;
   19308                 :             : 
   19309                 :             :         /*
   19310                 :             :          * We use lcons() here so that ON COMMIT actions are processed in reverse
   19311                 :             :          * order of registration.  That might not be essential but it seems
   19312                 :             :          * reasonable.
   19313                 :             :          */
   19314                 :          25 :         on_commits = lcons(oc, on_commits);
   19315                 :             : 
   19316                 :          25 :         MemoryContextSwitchTo(oldcxt);
   19317         [ -  + ]:          29 : }
   19318                 :             : 
   19319                 :             : /*
   19320                 :             :  * Unregister any ON COMMIT action when a relation is deleted.
   19321                 :             :  *
   19322                 :             :  * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
   19323                 :             :  */
   19324                 :             : void
   19325                 :        5546 : remove_on_commit_action(Oid relid)
   19326                 :             : {
   19327                 :        5546 :         ListCell   *l;
   19328                 :             : 
   19329   [ +  +  +  +  :        5596 :         foreach(l, on_commits)
                   +  + ]
   19330                 :             :         {
   19331                 :          50 :                 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   19332                 :             : 
   19333         [ +  + ]:          50 :                 if (oc->relid == relid)
   19334                 :             :                 {
   19335                 :          22 :                         oc->deleting_subid = GetCurrentSubTransactionId();
   19336                 :          22 :                         break;
   19337                 :             :                 }
   19338         [ +  + ]:          50 :         }
   19339                 :        5546 : }
   19340                 :             : 
   19341                 :             : /*
   19342                 :             :  * Perform ON COMMIT actions.
   19343                 :             :  *
   19344                 :             :  * This is invoked just before actually committing, since it's possible
   19345                 :             :  * to encounter errors.
   19346                 :             :  */
   19347                 :             : void
   19348                 :       50929 : PreCommit_on_commit_actions(void)
   19349                 :             : {
   19350                 :       50929 :         ListCell   *l;
   19351                 :       50929 :         List       *oids_to_truncate = NIL;
   19352                 :       50929 :         List       *oids_to_drop = NIL;
   19353                 :             : 
   19354   [ +  +  +  +  :       51064 :         foreach(l, on_commits)
                   +  + ]
   19355                 :             :         {
   19356                 :         135 :                 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   19357                 :             : 
   19358                 :             :                 /* Ignore entry if already dropped in this xact */
   19359         [ +  + ]:         135 :                 if (oc->deleting_subid != InvalidSubTransactionId)
   19360                 :          12 :                         continue;
   19361                 :             : 
   19362   [ -  -  +  + ]:         123 :                 switch (oc->oncommit)
   19363                 :             :                 {
   19364                 :             :                         case ONCOMMIT_NOOP:
   19365                 :             :                         case ONCOMMIT_PRESERVE_ROWS:
   19366                 :             :                                 /* Do nothing (there shouldn't be such entries, actually) */
   19367                 :           0 :                                 break;
   19368                 :             :                         case ONCOMMIT_DELETE_ROWS:
   19369                 :             : 
   19370                 :             :                                 /*
   19371                 :             :                                  * If this transaction hasn't accessed any temporary
   19372                 :             :                                  * relations, we can skip truncating ON COMMIT DELETE ROWS
   19373                 :             :                                  * tables, as they must still be empty.
   19374                 :             :                                  */
   19375         [ +  + ]:         115 :                                 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
   19376                 :          74 :                                         oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
   19377                 :         115 :                                 break;
   19378                 :             :                         case ONCOMMIT_DROP:
   19379                 :           8 :                                 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
   19380                 :           8 :                                 break;
   19381                 :             :                 }
   19382         [ +  + ]:         135 :         }
   19383                 :             : 
   19384                 :             :         /*
   19385                 :             :          * Truncate relations before dropping so that all dependencies between
   19386                 :             :          * relations are removed after they are worked on.  Doing it like this
   19387                 :             :          * might be a waste as it is possible that a relation being truncated will
   19388                 :             :          * be dropped anyway due to its parent being dropped, but this makes the
   19389                 :             :          * code more robust because of not having to re-check that the relation
   19390                 :             :          * exists at truncation time.
   19391                 :             :          */
   19392         [ +  + ]:       50929 :         if (oids_to_truncate != NIL)
   19393                 :          63 :                 heap_truncate(oids_to_truncate);
   19394                 :             : 
   19395         [ +  + ]:       50929 :         if (oids_to_drop != NIL)
   19396                 :             :         {
   19397                 :           7 :                 ObjectAddresses *targetObjects = new_object_addresses();
   19398                 :             : 
   19399   [ +  -  +  +  :          15 :                 foreach(l, oids_to_drop)
                   +  + ]
   19400                 :             :                 {
   19401                 :           8 :                         ObjectAddress object;
   19402                 :             : 
   19403                 :           8 :                         object.classId = RelationRelationId;
   19404                 :           8 :                         object.objectId = lfirst_oid(l);
   19405                 :           8 :                         object.objectSubId = 0;
   19406                 :             : 
   19407         [ +  - ]:           8 :                         Assert(!object_address_present(&object, targetObjects));
   19408                 :             : 
   19409                 :           8 :                         add_exact_object_address(&object, targetObjects);
   19410                 :           8 :                 }
   19411                 :             : 
   19412                 :             :                 /*
   19413                 :             :                  * Object deletion might involve toast table access (to clean up
   19414                 :             :                  * toasted catalog entries), so ensure we have a valid snapshot.
   19415                 :             :                  */
   19416                 :           7 :                 PushActiveSnapshot(GetTransactionSnapshot());
   19417                 :             : 
   19418                 :             :                 /*
   19419                 :             :                  * Since this is an automatic drop, rather than one directly initiated
   19420                 :             :                  * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
   19421                 :             :                  */
   19422                 :           7 :                 performMultipleDeletions(targetObjects, DROP_CASCADE,
   19423                 :             :                                                                  PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
   19424                 :             : 
   19425                 :           7 :                 PopActiveSnapshot();
   19426                 :             : 
   19427                 :             : #ifdef USE_ASSERT_CHECKING
   19428                 :             : 
   19429                 :             :                 /*
   19430                 :             :                  * Note that table deletion will call remove_on_commit_action, so the
   19431                 :             :                  * entry should get marked as deleted.
   19432                 :             :                  */
   19433   [ +  -  +  +  :          23 :                 foreach(l, on_commits)
                   +  + ]
   19434                 :             :                 {
   19435                 :          16 :                         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   19436                 :             : 
   19437         [ +  + ]:          16 :                         if (oc->oncommit != ONCOMMIT_DROP)
   19438                 :           8 :                                 continue;
   19439                 :             : 
   19440         [ -  + ]:           8 :                         Assert(oc->deleting_subid != InvalidSubTransactionId);
   19441         [ +  + ]:          16 :                 }
   19442                 :             : #endif
   19443                 :           7 :         }
   19444                 :       50929 : }
   19445                 :             : 
   19446                 :             : /*
   19447                 :             :  * Post-commit or post-abort cleanup for ON COMMIT management.
   19448                 :             :  *
   19449                 :             :  * All we do here is remove no-longer-needed OnCommitItem entries.
   19450                 :             :  *
   19451                 :             :  * During commit, remove entries that were deleted during this transaction;
   19452                 :             :  * during abort, remove those created during this transaction.
   19453                 :             :  */
   19454                 :             : void
   19455                 :       57917 : AtEOXact_on_commit_actions(bool isCommit)
   19456                 :             : {
   19457                 :       57917 :         ListCell   *cur_item;
   19458                 :             : 
   19459   [ +  +  +  +  :       58058 :         foreach(cur_item, on_commits)
                   +  + ]
   19460                 :             :         {
   19461                 :         141 :                 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   19462                 :             : 
   19463   [ +  +  +  + ]:         141 :                 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
   19464                 :          18 :                         oc->creating_subid != InvalidSubTransactionId)
   19465                 :             :                 {
   19466                 :             :                         /* cur_item must be removed */
   19467                 :          25 :                         on_commits = foreach_delete_current(on_commits, cur_item);
   19468                 :          25 :                         pfree(oc);
   19469                 :          25 :                 }
   19470                 :             :                 else
   19471                 :             :                 {
   19472                 :             :                         /* cur_item must be preserved */
   19473                 :         116 :                         oc->creating_subid = InvalidSubTransactionId;
   19474                 :         116 :                         oc->deleting_subid = InvalidSubTransactionId;
   19475                 :             :                 }
   19476                 :         141 :         }
   19477                 :       57917 : }
   19478                 :             : 
   19479                 :             : /*
   19480                 :             :  * Post-subcommit or post-subabort cleanup for ON COMMIT management.
   19481                 :             :  *
   19482                 :             :  * During subabort, we can immediately remove entries created during this
   19483                 :             :  * subtransaction.  During subcommit, just relabel entries marked during
   19484                 :             :  * this subtransaction as being the parent's responsibility.
   19485                 :             :  */
   19486                 :             : void
   19487                 :        1665 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
   19488                 :             :                                                           SubTransactionId parentSubid)
   19489                 :             : {
   19490                 :        1665 :         ListCell   *cur_item;
   19491                 :             : 
   19492   [ -  +  #  #  :        1665 :         foreach(cur_item, on_commits)
                   +  - ]
   19493                 :             :         {
   19494                 :           0 :                 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   19495                 :             : 
   19496   [ #  #  #  # ]:           0 :                 if (!isCommit && oc->creating_subid == mySubid)
   19497                 :             :                 {
   19498                 :             :                         /* cur_item must be removed */
   19499                 :           0 :                         on_commits = foreach_delete_current(on_commits, cur_item);
   19500                 :           0 :                         pfree(oc);
   19501                 :           0 :                 }
   19502                 :             :                 else
   19503                 :             :                 {
   19504                 :             :                         /* cur_item must be preserved */
   19505         [ #  # ]:           0 :                         if (oc->creating_subid == mySubid)
   19506                 :           0 :                                 oc->creating_subid = parentSubid;
   19507         [ #  # ]:           0 :                         if (oc->deleting_subid == mySubid)
   19508         [ #  # ]:           0 :                                 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
   19509                 :             :                 }
   19510                 :           0 :         }
   19511                 :        1665 : }
   19512                 :             : 
   19513                 :             : /*
   19514                 :             :  * This is intended as a callback for RangeVarGetRelidExtended().  It allows
   19515                 :             :  * the relation to be locked only if (1) it's a plain or partitioned table,
   19516                 :             :  * materialized view, or TOAST table and (2) the current user is the owner (or
   19517                 :             :  * the superuser) or has been granted MAINTAIN.  This meets the
   19518                 :             :  * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
   19519                 :             :  * MATERIALIZED VIEW; we expose it here so that it can be used by all.
   19520                 :             :  */
   19521                 :             : void
   19522                 :         131 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
   19523                 :             :                                                            Oid relId, Oid oldRelId, void *arg)
   19524                 :             : {
   19525                 :         131 :         char            relkind;
   19526                 :         131 :         AclResult       aclresult;
   19527                 :             : 
   19528                 :             :         /* Nothing to do if the relation was not found. */
   19529         [ +  - ]:         131 :         if (!OidIsValid(relId))
   19530                 :           0 :                 return;
   19531                 :             : 
   19532                 :             :         /*
   19533                 :             :          * If the relation does exist, check whether it's an index.  But note that
   19534                 :             :          * the relation might have been dropped between the time we did the name
   19535                 :             :          * lookup and now.  In that case, there's nothing to do.
   19536                 :             :          */
   19537                 :         131 :         relkind = get_rel_relkind(relId);
   19538         [ +  - ]:         131 :         if (!relkind)
   19539                 :           0 :                 return;
   19540   [ +  +  +  + ]:         131 :         if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
   19541   [ +  +  +  + ]:          63 :                 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
   19542   [ +  -  +  - ]:           4 :                 ereport(ERROR,
   19543                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19544                 :             :                                  errmsg("\"%s\" is not a table or materialized view", relation->relname)));
   19545                 :             : 
   19546                 :             :         /* Check permissions */
   19547                 :         127 :         aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
   19548         [ +  + ]:         127 :         if (aclresult != ACLCHECK_OK)
   19549                 :          10 :                 aclcheck_error(aclresult,
   19550                 :           5 :                                            get_relkind_objtype(get_rel_relkind(relId)),
   19551                 :           5 :                                            relation->relname);
   19552         [ -  + ]:         127 : }
   19553                 :             : 
   19554                 :             : /*
   19555                 :             :  * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
   19556                 :             :  */
   19557                 :             : static void
   19558                 :         276 : RangeVarCallbackForTruncate(const RangeVar *relation,
   19559                 :             :                                                         Oid relId, Oid oldRelId, void *arg)
   19560                 :             : {
   19561                 :         276 :         HeapTuple       tuple;
   19562                 :             : 
   19563                 :             :         /* Nothing to do if the relation was not found. */
   19564         [ +  - ]:         276 :         if (!OidIsValid(relId))
   19565                 :           0 :                 return;
   19566                 :             : 
   19567                 :         276 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   19568         [ +  - ]:         276 :         if (!HeapTupleIsValid(tuple))   /* should not happen */
   19569   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", relId);
   19570                 :             : 
   19571                 :         276 :         truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
   19572                 :         276 :         truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
   19573                 :             : 
   19574                 :         276 :         ReleaseSysCache(tuple);
   19575         [ -  + ]:         276 : }
   19576                 :             : 
   19577                 :             : /*
   19578                 :             :  * Callback for RangeVarGetRelidExtended().  Checks that the current user is
   19579                 :             :  * the owner of the relation, or superuser.
   19580                 :             :  */
   19581                 :             : void
   19582                 :        1903 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
   19583                 :             :                                                          Oid relId, Oid oldRelId, void *arg)
   19584                 :             : {
   19585                 :        1903 :         HeapTuple       tuple;
   19586                 :             : 
   19587                 :             :         /* Nothing to do if the relation was not found. */
   19588         [ +  + ]:        1903 :         if (!OidIsValid(relId))
   19589                 :           3 :                 return;
   19590                 :             : 
   19591                 :        1900 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   19592         [ +  - ]:        1900 :         if (!HeapTupleIsValid(tuple))   /* should not happen */
   19593   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u", relId);
   19594                 :             : 
   19595         [ +  + ]:        1900 :         if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
   19596                 :           8 :                 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
   19597                 :           4 :                                            relation->relname);
   19598                 :             : 
   19599   [ +  -  +  - ]:        1900 :         if (!allowSystemTableMods &&
   19600                 :        1900 :                 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
   19601   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   19602                 :             :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   19603                 :             :                                  errmsg("permission denied: \"%s\" is a system catalog",
   19604                 :             :                                                 relation->relname)));
   19605                 :             : 
   19606                 :        1900 :         ReleaseSysCache(tuple);
   19607         [ -  + ]:        1903 : }
   19608                 :             : 
   19609                 :             : /*
   19610                 :             :  * Common RangeVarGetRelid callback for rename, set schema, and alter table
   19611                 :             :  * processing.
   19612                 :             :  */
   19613                 :             : static void
   19614                 :        3080 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
   19615                 :             :                                                                  void *arg)
   19616                 :             : {
   19617                 :        3080 :         Node       *stmt = (Node *) arg;
   19618                 :        3080 :         ObjectType      reltype;
   19619                 :        3080 :         HeapTuple       tuple;
   19620                 :        3080 :         Form_pg_class classform;
   19621                 :        3080 :         AclResult       aclresult;
   19622                 :        3080 :         char            relkind;
   19623                 :             : 
   19624                 :        3080 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   19625         [ +  + ]:        3080 :         if (!HeapTupleIsValid(tuple))
   19626                 :          37 :                 return;                                 /* concurrently dropped */
   19627                 :        3043 :         classform = (Form_pg_class) GETSTRUCT(tuple);
   19628                 :        3043 :         relkind = classform->relkind;
   19629                 :             : 
   19630                 :             :         /* Must own relation. */
   19631         [ +  + ]:        3043 :         if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
   19632                 :          12 :                 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
   19633                 :             : 
   19634                 :             :         /* No system table modifications unless explicitly allowed. */
   19635   [ +  +  +  + ]:        3043 :         if (!allowSystemTableMods && IsSystemClass(relid, classform))
   19636   [ +  -  +  - ]:           3 :                 ereport(ERROR,
   19637                 :             :                                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   19638                 :             :                                  errmsg("permission denied: \"%s\" is a system catalog",
   19639                 :             :                                                 rv->relname)));
   19640                 :             : 
   19641                 :             :         /*
   19642                 :             :          * Extract the specified relation type from the statement parse tree.
   19643                 :             :          *
   19644                 :             :          * Also, for ALTER .. RENAME, check permissions: the user must (still)
   19645                 :             :          * have CREATE rights on the containing namespace.
   19646                 :             :          */
   19647         [ +  + ]:        3040 :         if (IsA(stmt, RenameStmt))
   19648                 :             :         {
   19649                 :          86 :                 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
   19650                 :          43 :                                                                         GetUserId(), ACL_CREATE);
   19651         [ +  - ]:          43 :                 if (aclresult != ACLCHECK_OK)
   19652                 :           0 :                         aclcheck_error(aclresult, OBJECT_SCHEMA,
   19653                 :           0 :                                                    get_namespace_name(classform->relnamespace));
   19654                 :          43 :                 reltype = ((RenameStmt *) stmt)->renameType;
   19655                 :          43 :         }
   19656         [ +  + ]:        2997 :         else if (IsA(stmt, AlterObjectSchemaStmt))
   19657                 :          15 :                 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
   19658                 :             : 
   19659         [ +  - ]:        2982 :         else if (IsA(stmt, AlterTableStmt))
   19660                 :        2982 :                 reltype = ((AlterTableStmt *) stmt)->objtype;
   19661                 :             :         else
   19662                 :             :         {
   19663   [ #  #  #  # ]:           0 :                 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
   19664                 :           0 :                 reltype = OBJECT_TABLE; /* placate compiler */
   19665                 :             :         }
   19666                 :             : 
   19667                 :             :         /*
   19668                 :             :          * For compatibility with prior releases, we allow ALTER TABLE to be used
   19669                 :             :          * with most other types of relations (but not composite types). We allow
   19670                 :             :          * similar flexibility for ALTER INDEX in the case of RENAME, but not
   19671                 :             :          * otherwise.  Otherwise, the user must select the correct form of the
   19672                 :             :          * command for the relation at issue.
   19673                 :             :          */
   19674   [ +  +  +  - ]:        3040 :         if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
   19675   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   19676                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19677                 :             :                                  errmsg("\"%s\" is not a sequence", rv->relname)));
   19678                 :             : 
   19679   [ +  +  +  - ]:        3040 :         if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
   19680   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   19681                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19682                 :             :                                  errmsg("\"%s\" is not a view", rv->relname)));
   19683                 :             : 
   19684   [ +  +  +  - ]:        3040 :         if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
   19685   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   19686                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19687                 :             :                                  errmsg("\"%s\" is not a materialized view", rv->relname)));
   19688                 :             : 
   19689   [ +  +  +  - ]:        3040 :         if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
   19690   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   19691                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19692                 :             :                                  errmsg("\"%s\" is not a foreign table", rv->relname)));
   19693                 :             : 
   19694   [ +  +  +  - ]:        3040 :         if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
   19695   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   19696                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19697                 :             :                                  errmsg("\"%s\" is not a composite type", rv->relname)));
   19698                 :             : 
   19699   [ +  +  +  + ]:        3040 :         if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
   19700                 :          62 :                 relkind != RELKIND_PARTITIONED_INDEX
   19701   [ +  +  +  + ]:          62 :                 && !IsA(stmt, RenameStmt))
   19702   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   19703                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19704                 :             :                                  errmsg("\"%s\" is not an index", rv->relname)));
   19705                 :             : 
   19706                 :             :         /*
   19707                 :             :          * Don't allow ALTER TABLE on composite types. We want people to use ALTER
   19708                 :             :          * TYPE for that.
   19709                 :             :          */
   19710   [ +  +  +  - ]:        3039 :         if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
   19711   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   19712                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19713                 :             :                                  errmsg("\"%s\" is a composite type", rv->relname),
   19714                 :             :                 /* translator: %s is an SQL ALTER command */
   19715                 :             :                                  errhint("Use %s instead.",
   19716                 :             :                                                  "ALTER TYPE")));
   19717                 :             : 
   19718                 :             :         /*
   19719                 :             :          * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
   19720                 :             :          * to a different schema, such as indexes and TOAST tables.
   19721                 :             :          */
   19722         [ +  + ]:        3039 :         if (IsA(stmt, AlterObjectSchemaStmt))
   19723                 :             :         {
   19724         [ +  - ]:          15 :                 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
   19725   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   19726                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19727                 :             :                                          errmsg("cannot change schema of index \"%s\"",
   19728                 :             :                                                         rv->relname),
   19729                 :             :                                          errhint("Change the schema of the table instead.")));
   19730         [ +  - ]:          15 :                 else if (relkind == RELKIND_COMPOSITE_TYPE)
   19731   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   19732                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19733                 :             :                                          errmsg("cannot change schema of composite type \"%s\"",
   19734                 :             :                                                         rv->relname),
   19735                 :             :                         /* translator: %s is an SQL ALTER command */
   19736                 :             :                                          errhint("Use %s instead.",
   19737                 :             :                                                          "ALTER TYPE")));
   19738         [ +  - ]:          15 :                 else if (relkind == RELKIND_TOASTVALUE)
   19739   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   19740                 :             :                                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19741                 :             :                                          errmsg("cannot change schema of TOAST table \"%s\"",
   19742                 :             :                                                         rv->relname),
   19743                 :             :                                          errhint("Change the schema of the table instead.")));
   19744                 :          15 :         }
   19745                 :             : 
   19746                 :        3039 :         ReleaseSysCache(tuple);
   19747         [ -  + ]:        3076 : }
   19748                 :             : 
   19749                 :             : /*
   19750                 :             :  * Transform any expressions present in the partition key
   19751                 :             :  *
   19752                 :             :  * Returns a transformed PartitionSpec.
   19753                 :             :  */
   19754                 :             : static PartitionSpec *
   19755                 :         718 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
   19756                 :             : {
   19757                 :         718 :         PartitionSpec *newspec;
   19758                 :         718 :         ParseState *pstate;
   19759                 :         718 :         ParseNamespaceItem *nsitem;
   19760                 :         718 :         ListCell   *l;
   19761                 :             : 
   19762                 :         718 :         newspec = makeNode(PartitionSpec);
   19763                 :             : 
   19764                 :         718 :         newspec->strategy = partspec->strategy;
   19765                 :         718 :         newspec->partParams = NIL;
   19766                 :         718 :         newspec->location = partspec->location;
   19767                 :             : 
   19768                 :             :         /* Check valid number of columns for strategy */
   19769   [ +  +  +  + ]:         718 :         if (partspec->strategy == PARTITION_STRATEGY_LIST &&
   19770                 :         285 :                 list_length(partspec->partParams) != 1)
   19771   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   19772                 :             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19773                 :             :                                  errmsg("cannot use \"list\" partition strategy with more than one column")));
   19774                 :             : 
   19775                 :             :         /*
   19776                 :             :          * Create a dummy ParseState and insert the target relation as its sole
   19777                 :             :          * rangetable entry.  We need a ParseState for transformExpr.
   19778                 :             :          */
   19779                 :         717 :         pstate = make_parsestate(NULL);
   19780                 :         717 :         nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
   19781                 :             :                                                                                    NULL, false, true);
   19782                 :         717 :         addNSItemToQuery(pstate, nsitem, true, true, true);
   19783                 :             : 
   19784                 :             :         /* take care of any partition expressions */
   19785   [ +  -  +  +  :        1516 :         foreach(l, partspec->partParams)
                   +  + ]
   19786                 :             :         {
   19787                 :         799 :                 PartitionElem *pelem = lfirst_node(PartitionElem, l);
   19788                 :             : 
   19789         [ +  + ]:         799 :                 if (pelem->expr)
   19790                 :             :                 {
   19791                 :             :                         /* Copy, to avoid scribbling on the input */
   19792                 :          56 :                         pelem = copyObject(pelem);
   19793                 :             : 
   19794                 :             :                         /* Now do parse transformation of the expression */
   19795                 :          56 :                         pelem->expr = transformExpr(pstate, pelem->expr,
   19796                 :             :                                                                                 EXPR_KIND_PARTITION_EXPRESSION);
   19797                 :             : 
   19798                 :             :                         /* we have to fix its collations too */
   19799                 :          56 :                         assign_expr_collations(pstate, pelem->expr);
   19800                 :          56 :                 }
   19801                 :             : 
   19802                 :         799 :                 newspec->partParams = lappend(newspec->partParams, pelem);
   19803                 :         799 :         }
   19804                 :             : 
   19805                 :        1434 :         return newspec;
   19806                 :         717 : }
   19807                 :             : 
   19808                 :             : /*
   19809                 :             :  * Compute per-partition-column information from a list of PartitionElems.
   19810                 :             :  * Expressions in the PartitionElems must be parse-analyzed already.
   19811                 :             :  */
   19812                 :             : static void
   19813                 :         717 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
   19814                 :             :                                           List **partexprs, Oid *partopclass, Oid *partcollation,
   19815                 :             :                                           PartitionStrategy strategy)
   19816                 :             : {
   19817                 :         717 :         int                     attn;
   19818                 :         717 :         ListCell   *lc;
   19819                 :         717 :         Oid                     am_oid;
   19820                 :             : 
   19821                 :         717 :         attn = 0;
   19822   [ +  +  +  +  :        1492 :         foreach(lc, partParams)
                   +  + ]
   19823                 :             :         {
   19824                 :         793 :                 PartitionElem *pelem = lfirst_node(PartitionElem, lc);
   19825                 :         793 :                 Oid                     atttype;
   19826                 :         793 :                 Oid                     attcollation;
   19827                 :             : 
   19828         [ +  + ]:         793 :                 if (pelem->name != NULL)
   19829                 :             :                 {
   19830                 :             :                         /* Simple attribute reference */
   19831                 :         743 :                         HeapTuple       atttuple;
   19832                 :         743 :                         Form_pg_attribute attform;
   19833                 :             : 
   19834                 :        1486 :                         atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
   19835                 :         743 :                                                                                          pelem->name);
   19836         [ +  + ]:         743 :                         if (!HeapTupleIsValid(atttuple))
   19837   [ +  -  +  - ]:           2 :                                 ereport(ERROR,
   19838                 :             :                                                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   19839                 :             :                                                  errmsg("column \"%s\" named in partition key does not exist",
   19840                 :             :                                                                 pelem->name),
   19841                 :             :                                                  parser_errposition(pstate, pelem->location)));
   19842                 :         741 :                         attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   19843                 :             : 
   19844         [ +  + ]:         741 :                         if (attform->attnum <= 0)
   19845   [ +  -  +  - ]:           1 :                                 ereport(ERROR,
   19846                 :             :                                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19847                 :             :                                                  errmsg("cannot use system column \"%s\" in partition key",
   19848                 :             :                                                                 pelem->name),
   19849                 :             :                                                  parser_errposition(pstate, pelem->location)));
   19850                 :             : 
   19851                 :             :                         /*
   19852                 :             :                          * Stored generated columns cannot work: They are computed after
   19853                 :             :                          * BEFORE triggers, but partition routing is done before all
   19854                 :             :                          * triggers.  Maybe virtual generated columns could be made to
   19855                 :             :                          * work, but then they would need to be handled as an expression
   19856                 :             :                          * below.
   19857                 :             :                          */
   19858         [ +  + ]:         740 :                         if (attform->attgenerated)
   19859   [ +  -  +  - ]:           2 :                                 ereport(ERROR,
   19860                 :             :                                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19861                 :             :                                                  errmsg("cannot use generated column in partition key"),
   19862                 :             :                                                  errdetail("Column \"%s\" is a generated column.",
   19863                 :             :                                                                    pelem->name),
   19864                 :             :                                                  parser_errposition(pstate, pelem->location)));
   19865                 :             : 
   19866                 :         738 :                         partattrs[attn] = attform->attnum;
   19867                 :         738 :                         atttype = attform->atttypid;
   19868                 :         738 :                         attcollation = attform->attcollation;
   19869                 :         738 :                         ReleaseSysCache(atttuple);
   19870                 :         738 :                 }
   19871                 :             :                 else
   19872                 :             :                 {
   19873                 :             :                         /* Expression */
   19874                 :          50 :                         Node       *expr = pelem->expr;
   19875                 :          50 :                         char            partattname[16];
   19876                 :          50 :                         Bitmapset  *expr_attrs = NULL;
   19877                 :          50 :                         int                     i;
   19878                 :             : 
   19879         [ -  + ]:          50 :                         Assert(expr != NULL);
   19880                 :          50 :                         atttype = exprType(expr);
   19881                 :          50 :                         attcollation = exprCollation(expr);
   19882                 :             : 
   19883                 :             :                         /*
   19884                 :             :                          * The expression must be of a storable type (e.g., not RECORD).
   19885                 :             :                          * The test is the same as for whether a table column is of a safe
   19886                 :             :                          * type (which is why we needn't check for the non-expression
   19887                 :             :                          * case).
   19888                 :             :                          */
   19889                 :          50 :                         snprintf(partattname, sizeof(partattname), "%d", attn + 1);
   19890                 :         100 :                         CheckAttributeType(partattname,
   19891                 :          50 :                                                            atttype, attcollation,
   19892                 :             :                                                            NIL, CHKATYPE_IS_PARTKEY);
   19893                 :             : 
   19894                 :             :                         /*
   19895                 :             :                          * Strip any top-level COLLATE clause.  This ensures that we treat
   19896                 :             :                          * "x COLLATE y" and "(x COLLATE y)" alike.
   19897                 :             :                          */
   19898         [ -  + ]:          50 :                         while (IsA(expr, CollateExpr))
   19899                 :           0 :                                 expr = (Node *) ((CollateExpr *) expr)->arg;
   19900                 :             : 
   19901                 :             :                         /*
   19902                 :             :                          * Examine all the columns in the partition key expression. When
   19903                 :             :                          * the whole-row reference is present, examine all the columns of
   19904                 :             :                          * the partitioned table.
   19905                 :             :                          */
   19906                 :          50 :                         pull_varattnos(expr, 1, &expr_attrs);
   19907         [ +  + ]:          50 :                         if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs))
   19908                 :             :                         {
   19909                 :          10 :                                 expr_attrs = bms_add_range(expr_attrs,
   19910                 :             :                                                                                    1 - FirstLowInvalidHeapAttributeNumber,
   19911                 :           5 :                                                                                    RelationGetNumberOfAttributes(rel) - FirstLowInvalidHeapAttributeNumber);
   19912                 :           5 :                                 expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
   19913                 :           5 :                         }
   19914                 :             : 
   19915                 :          50 :                         i = -1;
   19916         [ +  + ]:         109 :                         while ((i = bms_next_member(expr_attrs, i)) >= 0)
   19917                 :             :                         {
   19918                 :          67 :                                 AttrNumber      attno = i + FirstLowInvalidHeapAttributeNumber;
   19919                 :             : 
   19920         [ -  + ]:          67 :                                 Assert(attno != 0);
   19921                 :             : 
   19922                 :             :                                 /*
   19923                 :             :                                  * Cannot allow system column references, since that would
   19924                 :             :                                  * make partition routing impossible: their values won't be
   19925                 :             :                                  * known yet when we need to do that.
   19926                 :             :                                  */
   19927         [ +  - ]:          67 :                                 if (attno < 0)
   19928   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
   19929                 :             :                                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19930                 :             :                                                          errmsg("partition key expressions cannot contain system column references")));
   19931                 :             : 
   19932                 :             :                                 /*
   19933                 :             :                                  * Stored generated columns cannot work: They are computed
   19934                 :             :                                  * after BEFORE triggers, but partition routing is done before
   19935                 :             :                                  * all triggers.  Virtual generated columns could probably
   19936                 :             :                                  * work, but it would require more work elsewhere (for example
   19937                 :             :                                  * SET EXPRESSION would need to check whether the column is
   19938                 :             :                                  * used in partition keys).  Seems safer to prohibit for now.
   19939                 :             :                                  */
   19940         [ +  + ]:          67 :                                 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
   19941   [ +  -  +  - ]:           8 :                                         ereport(ERROR,
   19942                 :             :                                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19943                 :             :                                                          errmsg("cannot use generated column in partition key"),
   19944                 :             :                                                          errdetail("Column \"%s\" is a generated column.",
   19945                 :             :                                                                            get_attname(RelationGetRelid(rel), attno, false)),
   19946                 :             :                                                          parser_errposition(pstate, pelem->location)));
   19947                 :          59 :                         }
   19948                 :             : 
   19949   [ +  +  +  + ]:          42 :                         if (IsA(expr, Var) &&
   19950                 :           2 :                                 ((Var *) expr)->varattno > 0)
   19951                 :             :                         {
   19952                 :             : 
   19953                 :             :                                 /*
   19954                 :             :                                  * User wrote "(column)" or "(column COLLATE something)".
   19955                 :             :                                  * Treat it like simple attribute anyway.
   19956                 :             :                                  */
   19957                 :           1 :                                 partattrs[attn] = ((Var *) expr)->varattno;
   19958                 :           1 :                         }
   19959                 :             :                         else
   19960                 :             :                         {
   19961                 :          41 :                                 partattrs[attn] = 0;    /* marks the column as expression */
   19962                 :          41 :                                 *partexprs = lappend(*partexprs, expr);
   19963                 :             : 
   19964                 :             :                                 /*
   19965                 :             :                                  * transformPartitionSpec() should have already rejected
   19966                 :             :                                  * subqueries, aggregates, window functions, and SRFs, based
   19967                 :             :                                  * on the EXPR_KIND_ for partition expressions.
   19968                 :             :                                  */
   19969                 :             : 
   19970                 :             :                                 /*
   19971                 :             :                                  * Preprocess the expression before checking for mutability.
   19972                 :             :                                  * This is essential for the reasons described in
   19973                 :             :                                  * contain_mutable_functions_after_planning.  However, we call
   19974                 :             :                                  * expression_planner for ourselves rather than using that
   19975                 :             :                                  * function, because if constant-folding reduces the
   19976                 :             :                                  * expression to a constant, we'd like to know that so we can
   19977                 :             :                                  * complain below.
   19978                 :             :                                  *
   19979                 :             :                                  * Like contain_mutable_functions_after_planning, assume that
   19980                 :             :                                  * expression_planner won't scribble on its input, so this
   19981                 :             :                                  * won't affect the partexprs entry we saved above.
   19982                 :             :                                  */
   19983                 :          41 :                                 expr = (Node *) expression_planner((Expr *) expr);
   19984                 :             : 
   19985                 :             :                                 /*
   19986                 :             :                                  * Partition expressions cannot contain mutable functions,
   19987                 :             :                                  * because a given row must always map to the same partition
   19988                 :             :                                  * as long as there is no change in the partition boundary
   19989                 :             :                                  * structure.
   19990                 :             :                                  */
   19991         [ +  + ]:          41 :                                 if (contain_mutable_functions(expr))
   19992   [ +  -  +  - ]:           1 :                                         ereport(ERROR,
   19993                 :             :                                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   19994                 :             :                                                          errmsg("functions in partition key expression must be marked IMMUTABLE")));
   19995                 :             : 
   19996                 :             :                                 /*
   19997                 :             :                                  * While it is not exactly *wrong* for a partition expression
   19998                 :             :                                  * to be a constant, it seems better to reject such keys.
   19999                 :             :                                  */
   20000         [ +  + ]:          40 :                                 if (IsA(expr, Const))
   20001   [ +  -  +  - ]:           2 :                                         ereport(ERROR,
   20002                 :             :                                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20003                 :             :                                                          errmsg("cannot use constant expression as partition key")));
   20004                 :             :                         }
   20005                 :          39 :                 }
   20006                 :             : 
   20007                 :             :                 /*
   20008                 :             :                  * Apply collation override if any
   20009                 :             :                  */
   20010         [ +  + ]:         777 :                 if (pelem->collation)
   20011                 :           9 :                         attcollation = get_collation_oid(pelem->collation, false);
   20012                 :             : 
   20013                 :             :                 /*
   20014                 :             :                  * Check we have a collation iff it's a collatable type.  The only
   20015                 :             :                  * expected failures here are (1) COLLATE applied to a noncollatable
   20016                 :             :                  * type, or (2) partition expression had an unresolved collation. But
   20017                 :             :                  * we might as well code this to be a complete consistency check.
   20018                 :             :                  */
   20019         [ +  + ]:         777 :                 if (type_is_collatable(atttype))
   20020                 :             :                 {
   20021         [ +  - ]:         101 :                         if (!OidIsValid(attcollation))
   20022   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   20023                 :             :                                                 (errcode(ERRCODE_INDETERMINATE_COLLATION),
   20024                 :             :                                                  errmsg("could not determine which collation to use for partition expression"),
   20025                 :             :                                                  errhint("Use the COLLATE clause to set the collation explicitly.")));
   20026                 :         101 :                 }
   20027                 :             :                 else
   20028                 :             :                 {
   20029         [ +  - ]:         676 :                         if (OidIsValid(attcollation))
   20030   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   20031                 :             :                                                 (errcode(ERRCODE_DATATYPE_MISMATCH),
   20032                 :             :                                                  errmsg("collations are not supported by type %s",
   20033                 :             :                                                                 format_type_be(atttype))));
   20034                 :             :                 }
   20035                 :             : 
   20036                 :         777 :                 partcollation[attn] = attcollation;
   20037                 :             : 
   20038                 :             :                 /*
   20039                 :             :                  * Identify the appropriate operator class.  For list and range
   20040                 :             :                  * partitioning, we use a btree operator class; hash partitioning uses
   20041                 :             :                  * a hash operator class.
   20042                 :             :                  */
   20043         [ +  + ]:         777 :                 if (strategy == PARTITION_STRATEGY_HASH)
   20044                 :          49 :                         am_oid = HASH_AM_OID;
   20045                 :             :                 else
   20046                 :         728 :                         am_oid = BTREE_AM_OID;
   20047                 :             : 
   20048         [ +  + ]:         777 :                 if (!pelem->opclass)
   20049                 :             :                 {
   20050                 :         754 :                         partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
   20051                 :             : 
   20052         [ +  + ]:         754 :                         if (!OidIsValid(partopclass[attn]))
   20053                 :             :                         {
   20054         [ -  + ]:           2 :                                 if (strategy == PARTITION_STRATEGY_HASH)
   20055   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
   20056                 :             :                                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
   20057                 :             :                                                          errmsg("data type %s has no default operator class for access method \"%s\"",
   20058                 :             :                                                                         format_type_be(atttype), "hash"),
   20059                 :             :                                                          errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
   20060                 :             :                                 else
   20061   [ +  -  +  - ]:           2 :                                         ereport(ERROR,
   20062                 :             :                                                         (errcode(ERRCODE_UNDEFINED_OBJECT),
   20063                 :             :                                                          errmsg("data type %s has no default operator class for access method \"%s\"",
   20064                 :             :                                                                         format_type_be(atttype), "btree"),
   20065                 :             :                                                          errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
   20066                 :           0 :                         }
   20067                 :         752 :                 }
   20068                 :             :                 else
   20069                 :          46 :                         partopclass[attn] = ResolveOpClass(pelem->opclass,
   20070                 :          23 :                                                                                            atttype,
   20071                 :          23 :                                                                                            am_oid == HASH_AM_OID ? "hash" : "btree",
   20072                 :          23 :                                                                                            am_oid);
   20073                 :             : 
   20074                 :         775 :                 attn++;
   20075                 :         775 :         }
   20076                 :         699 : }
   20077                 :             : 
   20078                 :             : /*
   20079                 :             :  * PartConstraintImpliedByRelConstraint
   20080                 :             :  *              Do scanrel's existing constraints imply the partition constraint?
   20081                 :             :  *
   20082                 :             :  * "Existing constraints" include its check constraints and column-level
   20083                 :             :  * not-null constraints.  partConstraint describes the partition constraint,
   20084                 :             :  * in implicit-AND form.
   20085                 :             :  */
   20086                 :             : bool
   20087                 :         417 : PartConstraintImpliedByRelConstraint(Relation scanrel,
   20088                 :             :                                                                          List *partConstraint)
   20089                 :             : {
   20090                 :         417 :         List       *existConstraint = NIL;
   20091                 :         417 :         TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   20092                 :         417 :         int                     i;
   20093                 :             : 
   20094   [ +  +  +  + ]:         417 :         if (constr && constr->has_not_null)
   20095                 :             :         {
   20096                 :         109 :                 int                     natts = scanrel->rd_att->natts;
   20097                 :             : 
   20098         [ +  + ]:         377 :                 for (i = 1; i <= natts; i++)
   20099                 :             :                 {
   20100                 :         268 :                         CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
   20101                 :             : 
   20102                 :             :                         /* invalid not-null constraint must be ignored here */
   20103   [ +  +  -  + ]:         268 :                         if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
   20104                 :             :                         {
   20105                 :         154 :                                 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
   20106                 :         154 :                                 NullTest   *ntest = makeNode(NullTest);
   20107                 :             : 
   20108                 :         154 :                                 ntest->arg = (Expr *) makeVar(1,
   20109                 :         154 :                                                                                           i,
   20110                 :         154 :                                                                                           wholeatt->atttypid,
   20111                 :         154 :                                                                                           wholeatt->atttypmod,
   20112                 :         154 :                                                                                           wholeatt->attcollation,
   20113                 :             :                                                                                           0);
   20114                 :         154 :                                 ntest->nulltesttype = IS_NOT_NULL;
   20115                 :             : 
   20116                 :             :                                 /*
   20117                 :             :                                  * argisrow=false is correct even for a composite column,
   20118                 :             :                                  * because attnotnull does not represent a SQL-spec IS NOT
   20119                 :             :                                  * NULL test in such a case, just IS DISTINCT FROM NULL.
   20120                 :             :                                  */
   20121                 :         154 :                                 ntest->argisrow = false;
   20122                 :         154 :                                 ntest->location = -1;
   20123                 :         154 :                                 existConstraint = lappend(existConstraint, ntest);
   20124                 :         154 :                         }
   20125                 :         268 :                 }
   20126                 :         109 :         }
   20127                 :             : 
   20128                 :         834 :         return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
   20129                 :         417 : }
   20130                 :             : 
   20131                 :             : /*
   20132                 :             :  * ConstraintImpliedByRelConstraint
   20133                 :             :  *              Do scanrel's existing constraints imply the given constraint?
   20134                 :             :  *
   20135                 :             :  * testConstraint is the constraint to validate. provenConstraint is a
   20136                 :             :  * caller-provided list of conditions which this function may assume
   20137                 :             :  * to be true. Both provenConstraint and testConstraint must be in
   20138                 :             :  * implicit-AND form, must only contain immutable clauses, and must
   20139                 :             :  * contain only Vars with varno = 1.
   20140                 :             :  */
   20141                 :             : bool
   20142                 :         606 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
   20143                 :             : {
   20144                 :         606 :         List       *existConstraint = list_copy(provenConstraint);
   20145                 :         606 :         TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   20146                 :         606 :         int                     num_check,
   20147                 :             :                                 i;
   20148                 :             : 
   20149         [ +  + ]:         606 :         num_check = (constr != NULL) ? constr->num_check : 0;
   20150         [ +  + ]:         676 :         for (i = 0; i < num_check; i++)
   20151                 :             :         {
   20152                 :          70 :                 Node       *cexpr;
   20153                 :             : 
   20154                 :             :                 /*
   20155                 :             :                  * If this constraint hasn't been fully validated yet, we must ignore
   20156                 :             :                  * it here.
   20157                 :             :                  */
   20158         [ +  + ]:          70 :                 if (!constr->check[i].ccvalid)
   20159                 :           1 :                         continue;
   20160                 :             : 
   20161                 :             :                 /*
   20162                 :             :                  * NOT ENFORCED constraints are always marked as invalid, which should
   20163                 :             :                  * have been ignored.
   20164                 :             :                  */
   20165         [ -  + ]:          69 :                 Assert(constr->check[i].ccenforced);
   20166                 :             : 
   20167                 :          69 :                 cexpr = stringToNode(constr->check[i].ccbin);
   20168                 :             : 
   20169                 :             :                 /*
   20170                 :             :                  * Run each expression through const-simplification and
   20171                 :             :                  * canonicalization.  It is necessary, because we will be comparing it
   20172                 :             :                  * to similarly-processed partition constraint expressions, and may
   20173                 :             :                  * fail to detect valid matches without this.
   20174                 :             :                  */
   20175                 :          69 :                 cexpr = eval_const_expressions(NULL, cexpr);
   20176                 :          69 :                 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
   20177                 :             : 
   20178                 :         138 :                 existConstraint = list_concat(existConstraint,
   20179                 :          69 :                                                                           make_ands_implicit((Expr *) cexpr));
   20180      [ -  +  + ]:          70 :         }
   20181                 :             : 
   20182                 :             :         /*
   20183                 :             :          * Try to make the proof.  Since we are comparing CHECK constraints, we
   20184                 :             :          * need to use weak implication, i.e., we assume existConstraint is
   20185                 :             :          * not-false and try to prove the same for testConstraint.
   20186                 :             :          *
   20187                 :             :          * Note that predicate_implied_by assumes its first argument is known
   20188                 :             :          * immutable.  That should always be true for both NOT NULL and partition
   20189                 :             :          * constraints, so we don't test it here.
   20190                 :             :          */
   20191                 :        1212 :         return predicate_implied_by(testConstraint, existConstraint, true);
   20192                 :         606 : }
   20193                 :             : 
   20194                 :             : /*
   20195                 :             :  * QueuePartitionConstraintValidation
   20196                 :             :  *
   20197                 :             :  * Add an entry to wqueue to have the given partition constraint validated by
   20198                 :             :  * Phase 3, for the given relation, and all its children.
   20199                 :             :  *
   20200                 :             :  * We first verify whether the given constraint is implied by pre-existing
   20201                 :             :  * relation constraints; if it is, there's no need to scan the table to
   20202                 :             :  * validate, so don't queue in that case.
   20203                 :             :  */
   20204                 :             : static void
   20205                 :         337 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
   20206                 :             :                                                                    List *partConstraint,
   20207                 :             :                                                                    bool validate_default)
   20208                 :             : {
   20209                 :             :         /*
   20210                 :             :          * Based on the table's existing constraints, determine whether or not we
   20211                 :             :          * may skip scanning the table.
   20212                 :             :          */
   20213         [ +  + ]:         337 :         if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
   20214                 :             :         {
   20215         [ +  + ]:          11 :                 if (!validate_default)
   20216   [ -  +  -  + ]:           8 :                         ereport(DEBUG1,
   20217                 :             :                                         (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
   20218                 :             :                                                                          RelationGetRelationName(scanrel))));
   20219                 :             :                 else
   20220   [ -  +  -  + ]:           3 :                         ereport(DEBUG1,
   20221                 :             :                                         (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
   20222                 :             :                                                                          RelationGetRelationName(scanrel))));
   20223                 :          11 :                 return;
   20224                 :             :         }
   20225                 :             : 
   20226                 :             :         /*
   20227                 :             :          * Constraints proved insufficient. For plain relations, queue a
   20228                 :             :          * validation item now; for partitioned tables, recurse to process each
   20229                 :             :          * partition.
   20230                 :             :          */
   20231         [ +  + ]:         326 :         if (scanrel->rd_rel->relkind == RELKIND_RELATION)
   20232                 :             :         {
   20233                 :         270 :                 AlteredTableInfo *tab;
   20234                 :             : 
   20235                 :             :                 /* Grab a work queue entry. */
   20236                 :         270 :                 tab = ATGetQueueEntry(wqueue, scanrel);
   20237         [ +  - ]:         270 :                 Assert(tab->partition_constraint == NULL);
   20238                 :         270 :                 tab->partition_constraint = (Expr *) linitial(partConstraint);
   20239                 :         270 :                 tab->validate_default = validate_default;
   20240                 :         270 :         }
   20241         [ +  + ]:          56 :         else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   20242                 :             :         {
   20243                 :          51 :                 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
   20244                 :          51 :                 int                     i;
   20245                 :             : 
   20246         [ +  + ]:         118 :                 for (i = 0; i < partdesc->nparts; i++)
   20247                 :             :                 {
   20248                 :          67 :                         Relation        part_rel;
   20249                 :          67 :                         List       *thisPartConstraint;
   20250                 :             : 
   20251                 :             :                         /*
   20252                 :             :                          * This is the minimum lock we need to prevent deadlocks.
   20253                 :             :                          */
   20254                 :          67 :                         part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
   20255                 :             : 
   20256                 :             :                         /*
   20257                 :             :                          * Adjust the constraint for scanrel so that it matches this
   20258                 :             :                          * partition's attribute numbers.
   20259                 :             :                          */
   20260                 :          67 :                         thisPartConstraint =
   20261                 :         134 :                                 map_partition_varattnos(partConstraint, 1,
   20262                 :          67 :                                                                                 part_rel, scanrel);
   20263                 :             : 
   20264                 :         134 :                         QueuePartitionConstraintValidation(wqueue, part_rel,
   20265                 :          67 :                                                                                            thisPartConstraint,
   20266                 :          67 :                                                                                            validate_default);
   20267                 :          67 :                         table_close(part_rel, NoLock);  /* keep lock till commit */
   20268                 :          67 :                 }
   20269                 :          51 :         }
   20270                 :         337 : }
   20271                 :             : 
   20272                 :             : /*
   20273                 :             :  * attachPartitionTable: attach a new partition to the partitioned table
   20274                 :             :  *
   20275                 :             :  * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
   20276                 :             :  *   of an ALTER TABLE sequence.
   20277                 :             :  * rel: partitioned relation;
   20278                 :             :  * attachrel: relation of attached partition;
   20279                 :             :  * bound: bounds of attached relation.
   20280                 :             :  */
   20281                 :             : static void
   20282                 :         377 : attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
   20283                 :             : {
   20284                 :             :         /*
   20285                 :             :          * Create an inheritance; the relevant checks are performed inside the
   20286                 :             :          * function.
   20287                 :             :          */
   20288                 :         377 :         CreateInheritance(attachrel, rel, true);
   20289                 :             : 
   20290                 :             :         /* Update the pg_class entry. */
   20291                 :         377 :         StorePartitionBound(attachrel, rel, bound);
   20292                 :             : 
   20293                 :             :         /* Ensure there exists a correct set of indexes in the partition. */
   20294                 :         377 :         AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
   20295                 :             : 
   20296                 :             :         /* and triggers */
   20297                 :         377 :         CloneRowTriggersToPartition(rel, attachrel);
   20298                 :             : 
   20299                 :             :         /*
   20300                 :             :          * Clone foreign key constraints.  Callee is responsible for setting up
   20301                 :             :          * for phase 3 constraint verification.
   20302                 :             :          */
   20303                 :         377 :         CloneForeignKeyConstraints(wqueue, rel, attachrel);
   20304                 :         377 : }
   20305                 :             : 
   20306                 :             : /*
   20307                 :             :  * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
   20308                 :             :  *
   20309                 :             :  * Return the address of the newly attached partition.
   20310                 :             :  */
   20311                 :             : static ObjectAddress
   20312                 :         324 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
   20313                 :             :                                           AlterTableUtilityContext *context)
   20314                 :             : {
   20315                 :         324 :         Relation        attachrel,
   20316                 :             :                                 catalog;
   20317                 :         324 :         List       *attachrel_children;
   20318                 :         324 :         List       *partConstraint;
   20319                 :         324 :         SysScanDesc scan;
   20320                 :         324 :         ScanKeyData skey;
   20321                 :         324 :         AttrNumber      attno;
   20322                 :         324 :         int                     natts;
   20323                 :         324 :         TupleDesc       tupleDesc;
   20324                 :             :         ObjectAddress address;
   20325                 :         324 :         const char *trigger_name;
   20326                 :         324 :         Oid                     defaultPartOid;
   20327                 :         324 :         List       *partBoundConstraint;
   20328                 :         324 :         ParseState *pstate = make_parsestate(NULL);
   20329                 :             : 
   20330                 :         324 :         pstate->p_sourcetext = context->queryString;
   20331                 :             : 
   20332                 :             :         /*
   20333                 :             :          * We must lock the default partition if one exists, because attaching a
   20334                 :             :          * new partition will change its partition constraint.
   20335                 :             :          */
   20336                 :         324 :         defaultPartOid =
   20337                 :         324 :                 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   20338         [ +  + ]:         324 :         if (OidIsValid(defaultPartOid))
   20339                 :          26 :                 LockRelationOid(defaultPartOid, AccessExclusiveLock);
   20340                 :             : 
   20341                 :         324 :         attachrel = table_openrv(cmd->name, AccessExclusiveLock);
   20342                 :             : 
   20343                 :             :         /*
   20344                 :             :          * XXX I think it'd be a good idea to grab locks on all tables referenced
   20345                 :             :          * by FKs at this point also.
   20346                 :             :          */
   20347                 :             : 
   20348                 :             :         /*
   20349                 :             :          * Must be owner of both parent and source table -- parent was checked by
   20350                 :             :          * ATSimplePermissions call in ATPrepCmd
   20351                 :             :          */
   20352                 :         324 :         ATSimplePermissions(AT_AttachPartition, attachrel,
   20353                 :             :                                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   20354                 :             : 
   20355                 :             :         /* A partition can only have one parent */
   20356         [ +  + ]:         324 :         if (attachrel->rd_rel->relispartition)
   20357   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   20358                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20359                 :             :                                  errmsg("\"%s\" is already a partition",
   20360                 :             :                                                 RelationGetRelationName(attachrel))));
   20361                 :             : 
   20362         [ +  + ]:         323 :         if (OidIsValid(attachrel->rd_rel->reloftype))
   20363   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   20364                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20365                 :             :                                  errmsg("cannot attach a typed table as partition")));
   20366                 :             : 
   20367                 :             :         /*
   20368                 :             :          * Table being attached should not already be part of inheritance; either
   20369                 :             :          * as a child table...
   20370                 :             :          */
   20371                 :         322 :         catalog = table_open(InheritsRelationId, AccessShareLock);
   20372                 :         322 :         ScanKeyInit(&skey,
   20373                 :             :                                 Anum_pg_inherits_inhrelid,
   20374                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   20375                 :         322 :                                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   20376                 :         322 :         scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
   20377                 :             :                                                           NULL, 1, &skey);
   20378         [ +  + ]:         322 :         if (HeapTupleIsValid(systable_getnext(scan)))
   20379   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   20380                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20381                 :             :                                  errmsg("cannot attach inheritance child as partition")));
   20382                 :         321 :         systable_endscan(scan);
   20383                 :             : 
   20384                 :             :         /* ...or as a parent table (except the case when it is partitioned) */
   20385                 :         321 :         ScanKeyInit(&skey,
   20386                 :             :                                 Anum_pg_inherits_inhparent,
   20387                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   20388                 :         321 :                                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   20389                 :         321 :         scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
   20390                 :             :                                                           1, &skey);
   20391   [ +  +  +  + ]:         321 :         if (HeapTupleIsValid(systable_getnext(scan)) &&
   20392                 :          44 :                 attachrel->rd_rel->relkind == RELKIND_RELATION)
   20393   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   20394                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20395                 :             :                                  errmsg("cannot attach inheritance parent as partition")));
   20396                 :         320 :         systable_endscan(scan);
   20397                 :         320 :         table_close(catalog, AccessShareLock);
   20398                 :             : 
   20399                 :             :         /*
   20400                 :             :          * Prevent circularity by seeing if rel is a partition of attachrel. (In
   20401                 :             :          * particular, this disallows making a rel a partition of itself.)
   20402                 :             :          *
   20403                 :             :          * We do that by checking if rel is a member of the list of attachrel's
   20404                 :             :          * partitions provided the latter is partitioned at all.  We want to avoid
   20405                 :             :          * having to construct this list again, so we request the strongest lock
   20406                 :             :          * on all partitions.  We need the strongest lock, because we may decide
   20407                 :             :          * to scan them if we find out that the table being attached (or its leaf
   20408                 :             :          * partitions) may contain rows that violate the partition constraint. If
   20409                 :             :          * the table has a constraint that would prevent such rows, which by
   20410                 :             :          * definition is present in all the partitions, we need not scan the
   20411                 :             :          * table, nor its partitions.  But we cannot risk a deadlock by taking a
   20412                 :             :          * weaker lock now and the stronger one only when needed.
   20413                 :             :          */
   20414                 :         320 :         attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
   20415                 :             :                                                                                          AccessExclusiveLock, NULL);
   20416         [ +  + ]:         320 :         if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
   20417   [ +  -  +  - ]:           2 :                 ereport(ERROR,
   20418                 :             :                                 (errcode(ERRCODE_DUPLICATE_TABLE),
   20419                 :             :                                  errmsg("circular inheritance not allowed"),
   20420                 :             :                                  errdetail("\"%s\" is already a child of \"%s\".",
   20421                 :             :                                                    RelationGetRelationName(rel),
   20422                 :             :                                                    RelationGetRelationName(attachrel))));
   20423                 :             : 
   20424                 :             :         /* If the parent is permanent, so must be all of its partitions. */
   20425   [ +  +  +  + ]:         318 :         if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
   20426                 :         292 :                 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
   20427   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   20428                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20429                 :             :                                  errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
   20430                 :             :                                                 RelationGetRelationName(rel))));
   20431                 :             : 
   20432                 :             :         /* Temp parent cannot have a partition that is itself not a temp */
   20433   [ +  +  +  + ]:         317 :         if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   20434                 :           7 :                 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   20435   [ +  -  +  - ]:           3 :                 ereport(ERROR,
   20436                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20437                 :             :                                  errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
   20438                 :             :                                                 RelationGetRelationName(rel))));
   20439                 :             : 
   20440                 :             :         /* If the parent is temp, it must belong to this session */
   20441   [ +  +  +  - ]:         262 :         if (RELATION_IS_OTHER_TEMP(rel))
   20442   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   20443                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20444                 :             :                                  errmsg("cannot attach as partition of temporary relation of another session")));
   20445                 :             : 
   20446                 :             :         /* Ditto for the partition */
   20447   [ +  +  +  - ]:         262 :         if (RELATION_IS_OTHER_TEMP(attachrel))
   20448   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   20449                 :             :                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20450                 :             :                                  errmsg("cannot attach temporary relation of another session as partition")));
   20451                 :             : 
   20452                 :             :         /*
   20453                 :             :          * Check if attachrel has any identity columns or any columns that aren't
   20454                 :             :          * in the parent.
   20455                 :             :          */
   20456                 :         262 :         tupleDesc = RelationGetDescr(attachrel);
   20457                 :         262 :         natts = tupleDesc->natts;
   20458         [ +  + ]:         983 :         for (attno = 1; attno <= natts; attno++)
   20459                 :             :         {
   20460                 :         728 :                 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
   20461                 :         728 :                 char       *attributeName = NameStr(attribute->attname);
   20462                 :             : 
   20463                 :             :                 /* Ignore dropped */
   20464         [ +  + ]:         728 :                 if (attribute->attisdropped)
   20465                 :          80 :                         continue;
   20466                 :             : 
   20467         [ +  + ]:         648 :                 if (attribute->attidentity)
   20468   [ +  -  +  - ]:           4 :                         ereport(ERROR,
   20469                 :             :                                         errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   20470                 :             :                                         errmsg("table \"%s\" being attached contains an identity column \"%s\"",
   20471                 :             :                                                    RelationGetRelationName(attachrel), attributeName),
   20472                 :             :                                         errdetail("The new partition may not contain an identity column."));
   20473                 :             : 
   20474                 :             :                 /* Try to find the column in parent (matching on column name) */
   20475         [ +  + ]:         644 :                 if (!SearchSysCacheExists2(ATTNAME,
   20476                 :             :                                                                    ObjectIdGetDatum(RelationGetRelid(rel)),
   20477                 :             :                                                                    CStringGetDatum(attributeName)))
   20478   [ +  -  +  - ]:           3 :                         ereport(ERROR,
   20479                 :             :                                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   20480                 :             :                                          errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
   20481                 :             :                                                         RelationGetRelationName(attachrel), attributeName,
   20482                 :             :                                                         RelationGetRelationName(rel)),
   20483                 :             :                                          errdetail("The new partition may contain only the columns present in parent.")));
   20484      [ -  +  + ]:         721 :         }
   20485                 :             : 
   20486                 :             :         /*
   20487                 :             :          * If child_rel has row-level triggers with transition tables, we
   20488                 :             :          * currently don't allow it to become a partition.  See also prohibitions
   20489                 :             :          * in ATExecAddInherit() and CreateTrigger().
   20490                 :             :          */
   20491                 :         255 :         trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
   20492         [ +  + ]:         255 :         if (trigger_name != NULL)
   20493   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   20494                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   20495                 :             :                                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
   20496                 :             :                                                 trigger_name, RelationGetRelationName(attachrel)),
   20497                 :             :                                  errdetail("ROW triggers with transition tables are not supported on partitions.")));
   20498                 :             : 
   20499                 :             :         /*
   20500                 :             :          * Check that the new partition's bound is valid and does not overlap any
   20501                 :             :          * of existing partitions of the parent - note that it does not return on
   20502                 :             :          * error.
   20503                 :             :          */
   20504                 :         508 :         check_new_partition_bound(RelationGetRelationName(attachrel), rel,
   20505                 :         254 :                                                           cmd->bound, pstate);
   20506                 :             : 
   20507                 :         254 :         attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
   20508                 :             : 
   20509                 :             :         /*
   20510                 :             :          * Generate a partition constraint from the partition bound specification.
   20511                 :             :          * If the parent itself is a partition, make sure to include its
   20512                 :             :          * constraint as well.
   20513                 :             :          */
   20514                 :         254 :         partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
   20515                 :             : 
   20516                 :             :         /*
   20517                 :             :          * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
   20518                 :             :          * since it's needed later to construct the constraint expression for
   20519                 :             :          * validating against the default partition, if any.
   20520                 :             :          */
   20521                 :         508 :         partConstraint = list_concat_copy(partBoundConstraint,
   20522                 :         254 :                                                                           RelationGetPartitionQual(rel));
   20523                 :             : 
   20524                 :             :         /* Skip validation if there are no constraints to validate. */
   20525         [ +  + ]:         254 :         if (partConstraint)
   20526                 :             :         {
   20527                 :             :                 /*
   20528                 :             :                  * Run the partition quals through const-simplification similar to
   20529                 :             :                  * check constraints.  We skip canonicalize_qual, though, because
   20530                 :             :                  * partition quals should be in canonical form already.
   20531                 :             :                  */
   20532                 :         250 :                 partConstraint =
   20533                 :         250 :                         (List *) eval_const_expressions(NULL,
   20534                 :         250 :                                                                                         (Node *) partConstraint);
   20535                 :             : 
   20536                 :             :                 /* XXX this sure looks wrong */
   20537                 :         250 :                 partConstraint = list_make1(make_ands_explicit(partConstraint));
   20538                 :             : 
   20539                 :             :                 /*
   20540                 :             :                  * Adjust the generated constraint to match this partition's attribute
   20541                 :             :                  * numbers.
   20542                 :             :                  */
   20543                 :         500 :                 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
   20544                 :         250 :                                                                                                  rel);
   20545                 :             : 
   20546                 :             :                 /* Validate partition constraints against the table being attached. */
   20547                 :         250 :                 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
   20548                 :             :                                                                                    false);
   20549                 :         250 :         }
   20550                 :             : 
   20551                 :             :         /*
   20552                 :             :          * If we're attaching a partition other than the default partition and a
   20553                 :             :          * default one exists, then that partition's partition constraint changes,
   20554                 :             :          * so add an entry to the work queue to validate it, too.  (We must not do
   20555                 :             :          * this when the partition being attached is the default one; we already
   20556                 :             :          * did it above!)
   20557                 :             :          */
   20558         [ +  + ]:         254 :         if (OidIsValid(defaultPartOid))
   20559                 :             :         {
   20560                 :          20 :                 Relation        defaultrel;
   20561                 :          20 :                 List       *defPartConstraint;
   20562                 :             : 
   20563         [ +  - ]:          20 :                 Assert(!cmd->bound->is_default);
   20564                 :             : 
   20565                 :             :                 /* we already hold a lock on the default partition */
   20566                 :          20 :                 defaultrel = table_open(defaultPartOid, NoLock);
   20567                 :          20 :                 defPartConstraint =
   20568                 :          20 :                         get_proposed_default_constraint(partBoundConstraint);
   20569                 :             : 
   20570                 :             :                 /*
   20571                 :             :                  * Map the Vars in the constraint expression from rel's attnos to
   20572                 :             :                  * defaultrel's.
   20573                 :             :                  */
   20574                 :          20 :                 defPartConstraint =
   20575                 :          40 :                         map_partition_varattnos(defPartConstraint,
   20576                 :          20 :                                                                         1, defaultrel, rel);
   20577                 :          40 :                 QueuePartitionConstraintValidation(wqueue, defaultrel,
   20578                 :          20 :                                                                                    defPartConstraint, true);
   20579                 :             : 
   20580                 :             :                 /* keep our lock until commit. */
   20581                 :          20 :                 table_close(defaultrel, NoLock);
   20582                 :          20 :         }
   20583                 :             : 
   20584                 :         254 :         ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
   20585                 :             : 
   20586                 :             :         /*
   20587                 :             :          * If the partition we just attached is partitioned itself, invalidate
   20588                 :             :          * relcache for all descendent partitions too to ensure that their
   20589                 :             :          * rd_partcheck expression trees are rebuilt; partitions already locked at
   20590                 :             :          * the beginning of this function.
   20591                 :             :          */
   20592         [ +  + ]:         254 :         if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   20593                 :             :         {
   20594                 :          48 :                 ListCell   *l;
   20595                 :             : 
   20596   [ +  -  +  +  :         157 :                 foreach(l, attachrel_children)
                   +  + ]
   20597                 :             :                 {
   20598                 :         109 :                         CacheInvalidateRelcacheByRelid(lfirst_oid(l));
   20599                 :         109 :                 }
   20600                 :          48 :         }
   20601                 :             : 
   20602                 :             :         /* keep our lock until commit */
   20603                 :         254 :         table_close(attachrel, NoLock);
   20604                 :             : 
   20605                 :             :         return address;
   20606                 :         254 : }
   20607                 :             : 
   20608                 :             : /*
   20609                 :             :  * AttachPartitionEnsureIndexes
   20610                 :             :  *              subroutine for ATExecAttachPartition to create/match indexes
   20611                 :             :  *
   20612                 :             :  * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
   20613                 :             :  * PARTITION: every partition must have an index attached to each index on the
   20614                 :             :  * partitioned table.
   20615                 :             :  */
   20616                 :             : static void
   20617                 :         356 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
   20618                 :             : {
   20619                 :         356 :         List       *idxes;
   20620                 :         356 :         List       *attachRelIdxs;
   20621                 :         356 :         Relation   *attachrelIdxRels;
   20622                 :         356 :         IndexInfo **attachInfos;
   20623                 :         356 :         ListCell   *cell;
   20624                 :         356 :         MemoryContext cxt;
   20625                 :         356 :         MemoryContext oldcxt;
   20626                 :             : 
   20627                 :         356 :         cxt = AllocSetContextCreate(CurrentMemoryContext,
   20628                 :             :                                                                 "AttachPartitionEnsureIndexes",
   20629                 :             :                                                                 ALLOCSET_DEFAULT_SIZES);
   20630                 :         356 :         oldcxt = MemoryContextSwitchTo(cxt);
   20631                 :             : 
   20632                 :         356 :         idxes = RelationGetIndexList(rel);
   20633                 :         356 :         attachRelIdxs = RelationGetIndexList(attachrel);
   20634                 :         356 :         attachrelIdxRels = palloc_array(Relation, list_length(attachRelIdxs));
   20635                 :         356 :         attachInfos = palloc_array(IndexInfo *, list_length(attachRelIdxs));
   20636                 :             : 
   20637                 :             :         /* Build arrays of all existing indexes and their IndexInfos */
   20638   [ +  +  +  +  :         781 :         foreach_oid(cldIdxId, attachRelIdxs)
             +  +  +  + ]
   20639                 :             :         {
   20640                 :          66 :                 int                     i = foreach_current_index(cldIdxId);
   20641                 :             : 
   20642                 :          66 :                 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
   20643                 :          66 :                 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
   20644                 :         425 :         }
   20645                 :             : 
   20646                 :             :         /*
   20647                 :             :          * If we're attaching a foreign table, we must fail if any of the indexes
   20648                 :             :          * is a constraint index; otherwise, there's nothing to do here.  Do this
   20649                 :             :          * before starting work, to avoid wasting the effort of building a few
   20650                 :             :          * non-unique indexes before coming across a unique one.
   20651                 :             :          */
   20652         [ +  + ]:         356 :         if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   20653                 :             :         {
   20654   [ +  +  +  +  :          11 :                 foreach(cell, idxes)
                   +  + ]
   20655                 :             :                 {
   20656                 :           6 :                         Oid                     idx = lfirst_oid(cell);
   20657                 :           6 :                         Relation        idxRel = index_open(idx, AccessShareLock);
   20658                 :             : 
   20659         [ +  + ]:           6 :                         if (idxRel->rd_index->indisunique ||
   20660                 :           4 :                                 idxRel->rd_index->indisprimary)
   20661   [ +  -  +  - ]:           2 :                                 ereport(ERROR,
   20662                 :             :                                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20663                 :             :                                                  errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
   20664                 :             :                                                                 RelationGetRelationName(attachrel),
   20665                 :             :                                                                 RelationGetRelationName(rel)),
   20666                 :             :                                                  errdetail("Partitioned table \"%s\" contains unique indexes.",
   20667                 :             :                                                                    RelationGetRelationName(rel))));
   20668                 :           4 :                         index_close(idxRel, AccessShareLock);
   20669                 :           4 :                 }
   20670                 :             : 
   20671                 :           5 :                 goto out;
   20672                 :             :         }
   20673                 :             : 
   20674                 :             :         /*
   20675                 :             :          * For each index on the partitioned table, find a matching one in the
   20676                 :             :          * partition-to-be; if one is not found, create one.
   20677                 :             :          */
   20678   [ +  +  +  +  :         460 :         foreach(cell, idxes)
                   +  + ]
   20679                 :             :         {
   20680                 :         111 :                 Oid                     idx = lfirst_oid(cell);
   20681                 :         111 :                 Relation        idxRel = index_open(idx, AccessShareLock);
   20682                 :         111 :                 IndexInfo  *info;
   20683                 :         111 :                 AttrMap    *attmap;
   20684                 :         111 :                 bool            found = false;
   20685                 :         111 :                 Oid                     constraintOid;
   20686                 :             : 
   20687                 :             :                 /*
   20688                 :             :                  * Ignore indexes in the partitioned table other than partitioned
   20689                 :             :                  * indexes.
   20690                 :             :                  */
   20691         [ -  + ]:         111 :                 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   20692                 :             :                 {
   20693                 :           0 :                         index_close(idxRel, AccessShareLock);
   20694                 :           0 :                         continue;
   20695                 :             :                 }
   20696                 :             : 
   20697                 :             :                 /* construct an indexinfo to compare existing indexes against */
   20698                 :         111 :                 info = BuildIndexInfo(idxRel);
   20699                 :         222 :                 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
   20700                 :         111 :                                                                            RelationGetDescr(rel),
   20701                 :             :                                                                            false);
   20702                 :         111 :                 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
   20703                 :             : 
   20704                 :             :                 /*
   20705                 :             :                  * Scan the list of existing indexes in the partition-to-be, and mark
   20706                 :             :                  * the first matching, valid, unattached one we find, if any, as
   20707                 :             :                  * partition of the parent index.  If we find one, we're done.
   20708                 :             :                  */
   20709         [ +  + ]:         159 :                 for (int i = 0; i < list_length(attachRelIdxs); i++)
   20710                 :             :                 {
   20711                 :          48 :                         Oid                     cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
   20712                 :          48 :                         Oid                     cldConstrOid = InvalidOid;
   20713                 :             : 
   20714                 :             :                         /* does this index have a parent?  if so, can't use it */
   20715         [ +  + ]:          48 :                         if (attachrelIdxRels[i]->rd_rel->relispartition)
   20716                 :           2 :                                 continue;
   20717                 :             : 
   20718                 :             :                         /* If this index is invalid, can't use it */
   20719         [ +  + ]:          46 :                         if (!attachrelIdxRels[i]->rd_index->indisvalid)
   20720                 :           1 :                                 continue;
   20721                 :             : 
   20722   [ +  +  +  + ]:          90 :                         if (CompareIndexInfo(attachInfos[i], info,
   20723                 :          45 :                                                                  attachrelIdxRels[i]->rd_indcollation,
   20724                 :          45 :                                                                  idxRel->rd_indcollation,
   20725                 :          45 :                                                                  attachrelIdxRels[i]->rd_opfamily,
   20726                 :          45 :                                                                  idxRel->rd_opfamily,
   20727                 :          45 :                                                                  attmap))
   20728                 :             :                         {
   20729                 :             :                                 /*
   20730                 :             :                                  * If this index is being created in the parent because of a
   20731                 :             :                                  * constraint, then the child needs to have a constraint also,
   20732                 :             :                                  * so look for one.  If there is no such constraint, this
   20733                 :             :                                  * index is no good, so keep looking.
   20734                 :             :                                  */
   20735         [ +  + ]:          39 :                                 if (OidIsValid(constraintOid))
   20736                 :             :                                 {
   20737                 :          23 :                                         cldConstrOid =
   20738                 :          46 :                                                 get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
   20739                 :          23 :                                                                                                                 cldIdxId);
   20740                 :             :                                         /* no dice */
   20741         [ +  + ]:          23 :                                         if (!OidIsValid(cldConstrOid))
   20742                 :           1 :                                                 continue;
   20743                 :             : 
   20744                 :             :                                         /* Ensure they're both the same type of constraint */
   20745   [ -  +  -  + ]:          44 :                                         if (get_constraint_type(constraintOid) !=
   20746                 :          22 :                                                 get_constraint_type(cldConstrOid))
   20747                 :           0 :                                                 continue;
   20748                 :          22 :                                 }
   20749                 :             : 
   20750                 :             :                                 /* bingo. */
   20751                 :          38 :                                 IndexSetParentIndex(attachrelIdxRels[i], idx);
   20752         [ +  + ]:          38 :                                 if (OidIsValid(constraintOid))
   20753                 :          44 :                                         ConstraintSetParentConstraint(cldConstrOid, constraintOid,
   20754                 :          22 :                                                                                                   RelationGetRelid(attachrel));
   20755                 :          38 :                                 found = true;
   20756                 :             : 
   20757                 :          38 :                                 CommandCounterIncrement();
   20758                 :          38 :                                 break;
   20759                 :             :                         }
   20760      [ +  +  + ]:          48 :                 }
   20761                 :             : 
   20762                 :             :                 /*
   20763                 :             :                  * If no suitable index was found in the partition-to-be, create one
   20764                 :             :                  * now.  Note that if this is a PK, not-null constraints must already
   20765                 :             :                  * exist.
   20766                 :             :                  */
   20767         [ +  + ]:         111 :                 if (!found)
   20768                 :             :                 {
   20769                 :          76 :                         IndexStmt  *stmt;
   20770                 :          76 :                         Oid                     conOid;
   20771                 :             : 
   20772                 :          76 :                         stmt = generateClonedIndexStmt(NULL,
   20773                 :          76 :                                                                                    idxRel, attmap,
   20774                 :             :                                                                                    &conOid);
   20775                 :          76 :                         DefineIndex(NULL,
   20776                 :          76 :                                                 RelationGetRelid(attachrel), stmt, InvalidOid,
   20777                 :          76 :                                                 RelationGetRelid(idxRel),
   20778                 :          76 :                                                 conOid,
   20779                 :             :                                                 -1,
   20780                 :             :                                                 true, false, false, false, false);
   20781                 :          76 :                 }
   20782                 :             : 
   20783                 :         111 :                 index_close(idxRel, AccessShareLock);
   20784      [ -  -  + ]:         460 :         }
   20785                 :             : 
   20786                 :             : out:
   20787                 :             :         /* Clean up. */
   20788         [ +  + ]:         418 :         for (int i = 0; i < list_length(attachRelIdxs); i++)
   20789                 :          64 :                 index_close(attachrelIdxRels[i], AccessShareLock);
   20790                 :         354 :         MemoryContextSwitchTo(oldcxt);
   20791                 :         354 :         MemoryContextDelete(cxt);
   20792                 :         354 : }
   20793                 :             : 
   20794                 :             : /*
   20795                 :             :  * CloneRowTriggersToPartition
   20796                 :             :  *              subroutine for ATExecAttachPartition/DefineRelation to create row
   20797                 :             :  *              triggers on partitions
   20798                 :             :  */
   20799                 :             : static void
   20800                 :         425 : CloneRowTriggersToPartition(Relation parent, Relation partition)
   20801                 :             : {
   20802                 :         425 :         Relation        pg_trigger;
   20803                 :         425 :         ScanKeyData key;
   20804                 :         425 :         SysScanDesc scan;
   20805                 :         425 :         HeapTuple       tuple;
   20806                 :         425 :         MemoryContext perTupCxt;
   20807                 :             : 
   20808                 :         425 :         ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   20809                 :         425 :                                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
   20810                 :         425 :         pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
   20811                 :         425 :         scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
   20812                 :             :                                                           true, NULL, 1, &key);
   20813                 :             : 
   20814                 :         425 :         perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   20815                 :             :                                                                           "clone trig", ALLOCSET_SMALL_SIZES);
   20816                 :             : 
   20817         [ +  + ]:         747 :         while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   20818                 :             :         {
   20819                 :         322 :                 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
   20820                 :         322 :                 CreateTrigStmt *trigStmt;
   20821                 :         322 :                 Node       *qual = NULL;
   20822                 :         322 :                 Datum           value;
   20823                 :         322 :                 bool            isnull;
   20824                 :         322 :                 List       *cols = NIL;
   20825                 :         322 :                 List       *trigargs = NIL;
   20826                 :         322 :                 MemoryContext oldcxt;
   20827                 :             : 
   20828                 :             :                 /*
   20829                 :             :                  * Ignore statement-level triggers; those are not cloned.
   20830                 :             :                  */
   20831         [ +  + ]:         322 :                 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
   20832                 :           7 :                         continue;
   20833                 :             : 
   20834                 :             :                 /*
   20835                 :             :                  * Don't clone internal triggers, because the constraint cloning code
   20836                 :             :                  * will.
   20837                 :             :                  */
   20838         [ +  + ]:         315 :                 if (trigForm->tgisinternal)
   20839                 :         283 :                         continue;
   20840                 :             : 
   20841                 :             :                 /*
   20842                 :             :                  * Complain if we find an unexpected trigger type.
   20843                 :             :                  */
   20844   [ +  +  +  - ]:          32 :                 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
   20845                 :          26 :                         !TRIGGER_FOR_AFTER(trigForm->tgtype))
   20846   [ #  #  #  # ]:           0 :                         elog(ERROR, "unexpected trigger \"%s\" found",
   20847                 :             :                                  NameStr(trigForm->tgname));
   20848                 :             : 
   20849                 :             :                 /* Use short-lived context for CREATE TRIGGER */
   20850                 :          32 :                 oldcxt = MemoryContextSwitchTo(perTupCxt);
   20851                 :             : 
   20852                 :             :                 /*
   20853                 :             :                  * If there is a WHEN clause, generate a 'cooked' version of it that's
   20854                 :             :                  * appropriate for the partition.
   20855                 :             :                  */
   20856                 :          64 :                 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
   20857                 :          32 :                                                          RelationGetDescr(pg_trigger), &isnull);
   20858         [ +  + ]:          32 :                 if (!isnull)
   20859                 :             :                 {
   20860                 :           1 :                         qual = stringToNode(TextDatumGetCString(value));
   20861                 :           2 :                         qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
   20862                 :           1 :                                                                                                         partition, parent);
   20863                 :           2 :                         qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
   20864                 :           1 :                                                                                                         partition, parent);
   20865                 :           1 :                 }
   20866                 :             : 
   20867                 :             :                 /*
   20868                 :             :                  * If there is a column list, transform it to a list of column names.
   20869                 :             :                  * Note we don't need to map this list in any way ...
   20870                 :             :                  */
   20871         [ +  + ]:          32 :                 if (trigForm->tgattr.dim1 > 0)
   20872                 :             :                 {
   20873                 :           1 :                         int                     i;
   20874                 :             : 
   20875         [ +  + ]:           2 :                         for (i = 0; i < trigForm->tgattr.dim1; i++)
   20876                 :             :                         {
   20877                 :           1 :                                 Form_pg_attribute col;
   20878                 :             : 
   20879                 :           2 :                                 col = TupleDescAttr(parent->rd_att,
   20880                 :           1 :                                                                         trigForm->tgattr.values[i] - 1);
   20881                 :           2 :                                 cols = lappend(cols,
   20882                 :           1 :                                                            makeString(pstrdup(NameStr(col->attname))));
   20883                 :           1 :                         }
   20884                 :           1 :                 }
   20885                 :             : 
   20886                 :             :                 /* Reconstruct trigger arguments list. */
   20887         [ +  + ]:          32 :                 if (trigForm->tgnargs > 0)
   20888                 :             :                 {
   20889                 :           9 :                         char       *p;
   20890                 :             : 
   20891                 :          18 :                         value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
   20892                 :           9 :                                                                  RelationGetDescr(pg_trigger), &isnull);
   20893         [ +  - ]:           9 :                         if (isnull)
   20894   [ #  #  #  # ]:           0 :                                 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
   20895                 :             :                                          NameStr(trigForm->tgname), RelationGetRelationName(partition));
   20896                 :             : 
   20897                 :           9 :                         p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
   20898                 :             : 
   20899         [ +  + ]:          20 :                         for (int i = 0; i < trigForm->tgnargs; i++)
   20900                 :             :                         {
   20901                 :          11 :                                 trigargs = lappend(trigargs, makeString(pstrdup(p)));
   20902                 :          11 :                                 p += strlen(p) + 1;
   20903                 :          11 :                         }
   20904                 :           9 :                 }
   20905                 :             : 
   20906                 :          32 :                 trigStmt = makeNode(CreateTrigStmt);
   20907                 :          32 :                 trigStmt->replace = false;
   20908                 :          32 :                 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
   20909                 :          32 :                 trigStmt->trigname = NameStr(trigForm->tgname);
   20910                 :          32 :                 trigStmt->relation = NULL;
   20911                 :          32 :                 trigStmt->funcname = NULL;   /* passed separately */
   20912                 :          32 :                 trigStmt->args = trigargs;
   20913                 :          32 :                 trigStmt->row = true;
   20914                 :          32 :                 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
   20915                 :          32 :                 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
   20916                 :          32 :                 trigStmt->columns = cols;
   20917                 :          32 :                 trigStmt->whenClause = NULL; /* passed separately */
   20918                 :          32 :                 trigStmt->transitionRels = NIL; /* not supported at present */
   20919                 :          32 :                 trigStmt->deferrable = trigForm->tgdeferrable;
   20920                 :          32 :                 trigStmt->initdeferred = trigForm->tginitdeferred;
   20921                 :          32 :                 trigStmt->constrrel = NULL; /* passed separately */
   20922                 :             : 
   20923                 :          64 :                 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
   20924                 :          32 :                                                           trigForm->tgconstrrelid, InvalidOid, InvalidOid,
   20925                 :          32 :                                                           trigForm->tgfoid, trigForm->oid, qual,
   20926                 :          32 :                                                           false, true, trigForm->tgenabled);
   20927                 :             : 
   20928                 :          32 :                 MemoryContextSwitchTo(oldcxt);
   20929                 :          32 :                 MemoryContextReset(perTupCxt);
   20930      [ -  +  + ]:         322 :         }
   20931                 :             : 
   20932                 :         425 :         MemoryContextDelete(perTupCxt);
   20933                 :             : 
   20934                 :         425 :         systable_endscan(scan);
   20935                 :         425 :         table_close(pg_trigger, RowExclusiveLock);
   20936                 :         425 : }
   20937                 :             : 
   20938                 :             : /*
   20939                 :             :  * ALTER TABLE DETACH PARTITION
   20940                 :             :  *
   20941                 :             :  * Return the address of the relation that is no longer a partition of rel.
   20942                 :             :  *
   20943                 :             :  * If concurrent mode is requested, we run in two transactions.  A side-
   20944                 :             :  * effect is that this command cannot run in a multi-part ALTER TABLE.
   20945                 :             :  * Currently, that's enforced by the grammar.
   20946                 :             :  *
   20947                 :             :  * The strategy for concurrency is to first modify the partition's
   20948                 :             :  * pg_inherit catalog row to make it visible to everyone that the
   20949                 :             :  * partition is detached, lock the partition against writes, and commit
   20950                 :             :  * the transaction; anyone who requests the partition descriptor from
   20951                 :             :  * that point onwards has to ignore such a partition.  In a second
   20952                 :             :  * transaction, we wait until all transactions that could have seen the
   20953                 :             :  * partition as attached are gone, then we remove the rest of partition
   20954                 :             :  * metadata (pg_inherits and pg_class.relpartbounds).
   20955                 :             :  */
   20956                 :             : static ObjectAddress
   20957                 :          71 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
   20958                 :             :                                           RangeVar *name, bool concurrent)
   20959                 :             : {
   20960                 :          71 :         Relation        partRel;
   20961                 :             :         ObjectAddress address;
   20962                 :          71 :         Oid                     defaultPartOid;
   20963                 :             : 
   20964                 :             :         /*
   20965                 :             :          * We must lock the default partition, because detaching this partition
   20966                 :             :          * will change its partition constraint.
   20967                 :             :          */
   20968                 :          71 :         defaultPartOid =
   20969                 :          71 :                 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   20970         [ +  + ]:          71 :         if (OidIsValid(defaultPartOid))
   20971                 :             :         {
   20972                 :             :                 /*
   20973                 :             :                  * Concurrent detaching when a default partition exists is not
   20974                 :             :                  * supported. The main problem is that the default partition
   20975                 :             :                  * constraint would change.  And there's a definitional problem: what
   20976                 :             :                  * should happen to the tuples that are being inserted that belong to
   20977                 :             :                  * the partition being detached?  Putting them on the partition being
   20978                 :             :                  * detached would be wrong, since they'd become "lost" after the
   20979                 :             :                  * detaching completes but we cannot put them in the default partition
   20980                 :             :                  * either until we alter its partition constraint.
   20981                 :             :                  *
   20982                 :             :                  * I think we could solve this problem if we effected the constraint
   20983                 :             :                  * change before committing the first transaction.  But the lock would
   20984                 :             :                  * have to remain AEL and it would cause concurrent query planning to
   20985                 :             :                  * be blocked, so changing it that way would be even worse.
   20986                 :             :                  */
   20987         [ +  + ]:          18 :                 if (concurrent)
   20988   [ +  -  +  - ]:           2 :                         ereport(ERROR,
   20989                 :             :                                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   20990                 :             :                                          errmsg("cannot detach partitions concurrently when a default partition exists")));
   20991                 :          16 :                 LockRelationOid(defaultPartOid, AccessExclusiveLock);
   20992                 :          16 :         }
   20993                 :             : 
   20994                 :             :         /*
   20995                 :             :          * In concurrent mode, the partition is locked with share-update-exclusive
   20996                 :             :          * in the first transaction.  This allows concurrent transactions to be
   20997                 :             :          * doing DML to the partition.
   20998                 :             :          */
   20999                 :          69 :         partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
   21000                 :             :                                                    AccessExclusiveLock);
   21001                 :             : 
   21002                 :             :         /*
   21003                 :             :          * Check inheritance conditions and either delete the pg_inherits row (in
   21004                 :             :          * non-concurrent mode) or just set the inhdetachpending flag.
   21005                 :             :          */
   21006         [ +  + ]:          69 :         if (!concurrent)
   21007                 :          67 :                 RemoveInheritance(partRel, rel, false);
   21008                 :             :         else
   21009                 :           2 :                 MarkInheritDetached(partRel, rel);
   21010                 :             : 
   21011                 :             :         /*
   21012                 :             :          * Ensure that foreign keys still hold after this detach.  This keeps
   21013                 :             :          * locks on the referencing tables, which prevents concurrent transactions
   21014                 :             :          * from adding rows that we wouldn't see.  For this to work in concurrent
   21015                 :             :          * mode, it is critical that the partition appears as no longer attached
   21016                 :             :          * for the RI queries as soon as the first transaction commits.
   21017                 :             :          */
   21018                 :          69 :         ATDetachCheckNoForeignKeyRefs(partRel);
   21019                 :             : 
   21020                 :             :         /*
   21021                 :             :          * Concurrent mode has to work harder; first we add a new constraint to
   21022                 :             :          * the partition that matches the partition constraint.  Then we close our
   21023                 :             :          * existing transaction, and in a new one wait for all processes to catch
   21024                 :             :          * up on the catalog updates we've done so far; at that point we can
   21025                 :             :          * complete the operation.
   21026                 :             :          */
   21027         [ +  + ]:          69 :         if (concurrent)
   21028                 :             :         {
   21029                 :           2 :                 Oid                     partrelid,
   21030                 :             :                                         parentrelid;
   21031                 :           2 :                 LOCKTAG         tag;
   21032                 :           2 :                 char       *parentrelname;
   21033                 :           2 :                 char       *partrelname;
   21034                 :             : 
   21035                 :             :                 /*
   21036                 :             :                  * We're almost done now; the only traces that remain are the
   21037                 :             :                  * pg_inherits tuple and the partition's relpartbounds.  Before we can
   21038                 :             :                  * remove those, we need to wait until all transactions that know that
   21039                 :             :                  * this is a partition are gone.
   21040                 :             :                  */
   21041                 :             : 
   21042                 :             :                 /*
   21043                 :             :                  * Remember relation OIDs to re-acquire them later; and relation names
   21044                 :             :                  * too, for error messages if something is dropped in between.
   21045                 :             :                  */
   21046                 :           2 :                 partrelid = RelationGetRelid(partRel);
   21047                 :           2 :                 parentrelid = RelationGetRelid(rel);
   21048                 :           4 :                 parentrelname = MemoryContextStrdup(PortalContext,
   21049                 :           2 :                                                                                         RelationGetRelationName(rel));
   21050                 :           4 :                 partrelname = MemoryContextStrdup(PortalContext,
   21051                 :           2 :                                                                                   RelationGetRelationName(partRel));
   21052                 :             : 
   21053                 :             :                 /* Invalidate relcache entries for the parent -- must be before close */
   21054                 :           2 :                 CacheInvalidateRelcache(rel);
   21055                 :             : 
   21056                 :           2 :                 table_close(partRel, NoLock);
   21057                 :           2 :                 table_close(rel, NoLock);
   21058                 :           2 :                 tab->rel = NULL;
   21059                 :             : 
   21060                 :             :                 /* Make updated catalog entry visible */
   21061                 :           2 :                 PopActiveSnapshot();
   21062                 :           2 :                 CommitTransactionCommand();
   21063                 :             : 
   21064                 :           2 :                 StartTransactionCommand();
   21065                 :             : 
   21066                 :             :                 /*
   21067                 :             :                  * Now wait.  This ensures that all queries that were planned
   21068                 :             :                  * including the partition are finished before we remove the rest of
   21069                 :             :                  * catalog entries.  We don't need or indeed want to acquire this
   21070                 :             :                  * lock, though -- that would block later queries.
   21071                 :             :                  *
   21072                 :             :                  * We don't need to concern ourselves with waiting for a lock on the
   21073                 :             :                  * partition itself, since we will acquire AccessExclusiveLock below.
   21074                 :             :                  */
   21075                 :           2 :                 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
   21076                 :           2 :                 WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
   21077                 :             : 
   21078                 :             :                 /*
   21079                 :             :                  * Now acquire locks in both relations again.  Note they may have been
   21080                 :             :                  * removed in the meantime, so care is required.
   21081                 :             :                  */
   21082                 :           2 :                 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
   21083                 :           2 :                 partRel = try_relation_open(partrelid, AccessExclusiveLock);
   21084                 :             : 
   21085                 :             :                 /* If the relations aren't there, something bad happened; bail out */
   21086         [ +  - ]:           2 :                 if (rel == NULL)
   21087                 :             :                 {
   21088         [ #  # ]:           0 :                         if (partRel != NULL)    /* shouldn't happen */
   21089   [ #  #  #  # ]:           0 :                                 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
   21090                 :             :                                          partrelname);
   21091   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   21092                 :             :                                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21093                 :             :                                          errmsg("partitioned table \"%s\" was removed concurrently",
   21094                 :             :                                                         parentrelname)));
   21095                 :           0 :                 }
   21096         [ +  - ]:           2 :                 if (partRel == NULL)
   21097   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   21098                 :             :                                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21099                 :             :                                          errmsg("partition \"%s\" was removed concurrently", partrelname)));
   21100                 :             : 
   21101                 :           2 :                 tab->rel = rel;
   21102                 :           2 :         }
   21103                 :             : 
   21104                 :             :         /*
   21105                 :             :          * Detaching the partition might involve TOAST table access, so ensure we
   21106                 :             :          * have a valid snapshot.
   21107                 :             :          */
   21108                 :          69 :         PushActiveSnapshot(GetTransactionSnapshot());
   21109                 :             : 
   21110                 :             :         /* Do the final part of detaching */
   21111                 :          69 :         DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
   21112                 :             : 
   21113                 :          69 :         PopActiveSnapshot();
   21114                 :             : 
   21115                 :          69 :         ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   21116                 :             : 
   21117                 :             :         /* keep our lock until commit */
   21118                 :          69 :         table_close(partRel, NoLock);
   21119                 :             : 
   21120                 :             :         return address;
   21121                 :          69 : }
   21122                 :             : 
   21123                 :             : /*
   21124                 :             :  * Second part of ALTER TABLE .. DETACH.
   21125                 :             :  *
   21126                 :             :  * This is separate so that it can be run independently when the second
   21127                 :             :  * transaction of the concurrent algorithm fails (crash or abort).
   21128                 :             :  */
   21129                 :             : static void
   21130                 :         144 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
   21131                 :             :                                                 Oid defaultPartOid)
   21132                 :             : {
   21133                 :         144 :         Relation        classRel;
   21134                 :         144 :         List       *fks;
   21135                 :         144 :         ListCell   *cell;
   21136                 :         144 :         List       *indexes;
   21137                 :         144 :         Datum           new_val[Natts_pg_class];
   21138                 :         144 :         bool            new_null[Natts_pg_class],
   21139                 :             :                                 new_repl[Natts_pg_class];
   21140                 :         144 :         HeapTuple       tuple,
   21141                 :             :                                 newtuple;
   21142                 :         144 :         Relation        trigrel = NULL;
   21143                 :         144 :         List       *fkoids = NIL;
   21144                 :             : 
   21145         [ +  + ]:         144 :         if (concurrent)
   21146                 :             :         {
   21147                 :             :                 /*
   21148                 :             :                  * We can remove the pg_inherits row now. (In the non-concurrent case,
   21149                 :             :                  * this was already done).
   21150                 :             :                  */
   21151                 :           2 :                 RemoveInheritance(partRel, rel, true);
   21152                 :           2 :         }
   21153                 :             : 
   21154                 :             :         /* Drop any triggers that were cloned on creation/attach. */
   21155                 :         144 :         DropClonedTriggersFromPartition(RelationGetRelid(partRel));
   21156                 :             : 
   21157                 :             :         /*
   21158                 :             :          * Detach any foreign keys that are inherited.  This includes creating
   21159                 :             :          * additional action triggers.
   21160                 :             :          */
   21161                 :         144 :         fks = copyObject(RelationGetFKeyList(partRel));
   21162         [ +  + ]:         144 :         if (fks != NIL)
   21163                 :          15 :                 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   21164                 :             : 
   21165                 :             :         /*
   21166                 :             :          * It's possible that the partition being detached has a foreign key that
   21167                 :             :          * references a partitioned table.  When that happens, there are multiple
   21168                 :             :          * pg_constraint rows for the partition: one points to the partitioned
   21169                 :             :          * table itself, while the others point to each of its partitions.  Only
   21170                 :             :          * the topmost one is to be considered here; the child constraints must be
   21171                 :             :          * left alone, because conceptually those aren't coming from our parent
   21172                 :             :          * partitioned table, but from this partition itself.
   21173                 :             :          *
   21174                 :             :          * We implement this by collecting all the constraint OIDs in a first scan
   21175                 :             :          * of the FK array, and skipping in the loop below those constraints whose
   21176                 :             :          * parents are listed here.
   21177                 :             :          */
   21178   [ +  +  +  +  :         317 :         foreach_node(ForeignKeyCacheInfo, fk, fks)
             +  +  +  + ]
   21179                 :         173 :                 fkoids = lappend_oid(fkoids, fk->conoid);
   21180                 :             : 
   21181   [ +  +  +  +  :         173 :         foreach(cell, fks)
                   +  + ]
   21182                 :             :         {
   21183                 :          29 :                 ForeignKeyCacheInfo *fk = lfirst(cell);
   21184                 :          29 :                 HeapTuple       contup;
   21185                 :          29 :                 Form_pg_constraint conform;
   21186                 :             : 
   21187                 :          29 :                 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
   21188         [ +  - ]:          29 :                 if (!HeapTupleIsValid(contup))
   21189   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   21190                 :          29 :                 conform = (Form_pg_constraint) GETSTRUCT(contup);
   21191                 :             : 
   21192                 :             :                 /*
   21193                 :             :                  * Consider only inherited foreign keys, and only if their parents
   21194                 :             :                  * aren't in the list.
   21195                 :             :                  */
   21196         [ +  - ]:          29 :                 if (conform->contype != CONSTRAINT_FOREIGN ||
   21197   [ +  +  +  + ]:          29 :                         !OidIsValid(conform->conparentid) ||
   21198                 :          25 :                         list_member_oid(fkoids, conform->conparentid))
   21199                 :             :                 {
   21200                 :          11 :                         ReleaseSysCache(contup);
   21201                 :          11 :                         continue;
   21202                 :             :                 }
   21203                 :             : 
   21204                 :             :                 /*
   21205                 :             :                  * The constraint on this table must be marked no longer a child of
   21206                 :             :                  * the parent's constraint, as do its check triggers.
   21207                 :             :                  */
   21208                 :          18 :                 ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
   21209                 :             : 
   21210                 :             :                 /*
   21211                 :             :                  * Also, look up the partition's "check" triggers corresponding to the
   21212                 :             :                  * ENFORCED constraint being detached and detach them from the parent
   21213                 :             :                  * triggers. NOT ENFORCED constraints do not have these triggers;
   21214                 :             :                  * therefore, this step is not needed.
   21215                 :             :                  */
   21216         [ -  + ]:          18 :                 if (fk->conenforced)
   21217                 :             :                 {
   21218                 :          18 :                         Oid                     insertTriggerOid,
   21219                 :             :                                                 updateTriggerOid;
   21220                 :             : 
   21221                 :          36 :                         GetForeignKeyCheckTriggers(trigrel,
   21222                 :          18 :                                                                            fk->conoid, fk->confrelid, fk->conrelid,
   21223                 :             :                                                                            &insertTriggerOid, &updateTriggerOid);
   21224         [ +  - ]:          18 :                         Assert(OidIsValid(insertTriggerOid));
   21225                 :          36 :                         TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
   21226                 :          18 :                                                                         RelationGetRelid(partRel));
   21227         [ +  - ]:          18 :                         Assert(OidIsValid(updateTriggerOid));
   21228                 :          36 :                         TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
   21229                 :          18 :                                                                         RelationGetRelid(partRel));
   21230                 :          18 :                 }
   21231                 :             : 
   21232                 :             :                 /*
   21233                 :             :                  * Lastly, create the action triggers on the referenced table, using
   21234                 :             :                  * addFkRecurseReferenced, which requires some elaborate setup (so put
   21235                 :             :                  * it in a separate block).  While at it, if the table is partitioned,
   21236                 :             :                  * that function will recurse to create the pg_constraint rows and
   21237                 :             :                  * action triggers for each partition.
   21238                 :             :                  *
   21239                 :             :                  * Note there's no need to do addFkConstraint() here, because the
   21240                 :             :                  * pg_constraint row already exists.
   21241                 :             :                  */
   21242                 :             :                 {
   21243                 :          18 :                         Constraint *fkconstraint;
   21244                 :          18 :                         int                     numfks;
   21245                 :          18 :                         AttrNumber      conkey[INDEX_MAX_KEYS];
   21246                 :          18 :                         AttrNumber      confkey[INDEX_MAX_KEYS];
   21247                 :          18 :                         Oid                     conpfeqop[INDEX_MAX_KEYS];
   21248                 :          18 :                         Oid                     conppeqop[INDEX_MAX_KEYS];
   21249                 :          18 :                         Oid                     conffeqop[INDEX_MAX_KEYS];
   21250                 :          18 :                         int                     numfkdelsetcols;
   21251                 :          18 :                         AttrNumber      confdelsetcols[INDEX_MAX_KEYS];
   21252                 :          18 :                         Relation        refdRel;
   21253                 :             : 
   21254                 :          36 :                         DeconstructFkConstraintRow(contup,
   21255                 :             :                                                                            &numfks,
   21256                 :          18 :                                                                            conkey,
   21257                 :          18 :                                                                            confkey,
   21258                 :          18 :                                                                            conpfeqop,
   21259                 :          18 :                                                                            conppeqop,
   21260                 :          18 :                                                                            conffeqop,
   21261                 :             :                                                                            &numfkdelsetcols,
   21262                 :          18 :                                                                            confdelsetcols);
   21263                 :             : 
   21264                 :             :                         /* Create a synthetic node we'll use throughout */
   21265                 :          18 :                         fkconstraint = makeNode(Constraint);
   21266                 :          18 :                         fkconstraint->contype = CONSTRAINT_FOREIGN;
   21267                 :          18 :                         fkconstraint->conname = pstrdup(NameStr(conform->conname));
   21268                 :          18 :                         fkconstraint->deferrable = conform->condeferrable;
   21269                 :          18 :                         fkconstraint->initdeferred = conform->condeferred;
   21270                 :          18 :                         fkconstraint->is_enforced = conform->conenforced;
   21271                 :          18 :                         fkconstraint->skip_validation = true;
   21272                 :          18 :                         fkconstraint->initially_valid = conform->convalidated;
   21273                 :             :                         /* a few irrelevant fields omitted here */
   21274                 :          18 :                         fkconstraint->pktable = NULL;
   21275                 :          18 :                         fkconstraint->fk_attrs = NIL;
   21276                 :          18 :                         fkconstraint->pk_attrs = NIL;
   21277                 :          18 :                         fkconstraint->fk_matchtype = conform->confmatchtype;
   21278                 :          18 :                         fkconstraint->fk_upd_action = conform->confupdtype;
   21279                 :          18 :                         fkconstraint->fk_del_action = conform->confdeltype;
   21280                 :          18 :                         fkconstraint->fk_del_set_cols = NIL;
   21281                 :          18 :                         fkconstraint->old_conpfeqop = NIL;
   21282                 :          18 :                         fkconstraint->old_pktable_oid = InvalidOid;
   21283                 :          18 :                         fkconstraint->location = -1;
   21284                 :             : 
   21285                 :             :                         /* set up colnames, used to generate the constraint name */
   21286         [ +  + ]:          44 :                         for (int i = 0; i < numfks; i++)
   21287                 :             :                         {
   21288                 :          26 :                                 Form_pg_attribute att;
   21289                 :             : 
   21290                 :          52 :                                 att = TupleDescAttr(RelationGetDescr(partRel),
   21291                 :          26 :                                                                         conkey[i] - 1);
   21292                 :             : 
   21293                 :          52 :                                 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   21294                 :          26 :                                                                                                  makeString(NameStr(att->attname)));
   21295                 :          26 :                         }
   21296                 :             : 
   21297                 :          18 :                         refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
   21298                 :             : 
   21299                 :          36 :                         addFkRecurseReferenced(fkconstraint, partRel,
   21300                 :          18 :                                                                    refdRel,
   21301                 :          18 :                                                                    conform->conindid,
   21302                 :          18 :                                                                    fk->conoid,
   21303                 :          18 :                                                                    numfks,
   21304                 :          18 :                                                                    confkey,
   21305                 :          18 :                                                                    conkey,
   21306                 :          18 :                                                                    conpfeqop,
   21307                 :          18 :                                                                    conppeqop,
   21308                 :          18 :                                                                    conffeqop,
   21309                 :          18 :                                                                    numfkdelsetcols,
   21310                 :          18 :                                                                    confdelsetcols,
   21311                 :             :                                                                    true,
   21312                 :             :                                                                    InvalidOid, InvalidOid,
   21313                 :          18 :                                                                    conform->conperiod);
   21314                 :          18 :                         table_close(refdRel, NoLock);   /* keep lock till end of xact */
   21315                 :          18 :                 }
   21316                 :             : 
   21317                 :          18 :                 ReleaseSysCache(contup);
   21318         [ +  + ]:          29 :         }
   21319                 :         144 :         list_free_deep(fks);
   21320         [ +  + ]:         144 :         if (trigrel)
   21321                 :          15 :                 table_close(trigrel, RowExclusiveLock);
   21322                 :             : 
   21323                 :             :         /*
   21324                 :             :          * Any sub-constraints that are in the referenced-side of a larger
   21325                 :             :          * constraint have to be removed.  This partition is no longer part of the
   21326                 :             :          * key space of the constraint.
   21327                 :             :          */
   21328   [ +  +  +  +  :         154 :         foreach(cell, GetParentedForeignKeyRefs(partRel))
                   +  + ]
   21329                 :             :         {
   21330                 :          10 :                 Oid                     constrOid = lfirst_oid(cell);
   21331                 :          10 :                 ObjectAddress constraint;
   21332                 :             : 
   21333                 :          10 :                 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   21334                 :          10 :                 deleteDependencyRecordsForClass(ConstraintRelationId,
   21335                 :          10 :                                                                                 constrOid,
   21336                 :             :                                                                                 ConstraintRelationId,
   21337                 :             :                                                                                 DEPENDENCY_INTERNAL);
   21338                 :          10 :                 CommandCounterIncrement();
   21339                 :             : 
   21340                 :          10 :                 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
   21341                 :          10 :                 performDeletion(&constraint, DROP_RESTRICT, 0);
   21342                 :          10 :         }
   21343                 :             : 
   21344                 :             :         /* Now we can detach indexes */
   21345                 :         144 :         indexes = RelationGetIndexList(partRel);
   21346   [ +  +  +  +  :         212 :         foreach(cell, indexes)
                   +  + ]
   21347                 :             :         {
   21348                 :          68 :                 Oid                     idxid = lfirst_oid(cell);
   21349                 :          68 :                 Oid                     parentidx;
   21350                 :          68 :                 Relation        idx;
   21351                 :          68 :                 Oid                     constrOid;
   21352                 :          68 :                 Oid                     parentConstrOid;
   21353                 :             : 
   21354         [ +  + ]:          68 :                 if (!has_superclass(idxid))
   21355                 :           2 :                         continue;
   21356                 :             : 
   21357                 :          66 :                 parentidx = get_partition_parent(idxid, false);
   21358         [ -  + ]:          66 :                 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
   21359                 :             : 
   21360                 :          66 :                 idx = index_open(idxid, AccessExclusiveLock);
   21361                 :          66 :                 IndexSetParentIndex(idx, InvalidOid);
   21362                 :             : 
   21363                 :             :                 /*
   21364                 :             :                  * If there's a constraint associated with the index, detach it too.
   21365                 :             :                  * Careful: it is possible for a constraint index in a partition to be
   21366                 :             :                  * the child of a non-constraint index, so verify whether the parent
   21367                 :             :                  * index does actually have a constraint.
   21368                 :             :                  */
   21369                 :         132 :                 constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
   21370                 :          66 :                                                                                                         idxid);
   21371                 :         132 :                 parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
   21372                 :          66 :                                                                                                                   parentidx);
   21373   [ +  +  +  - ]:          66 :                 if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
   21374                 :          28 :                         ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   21375                 :             : 
   21376                 :          66 :                 index_close(idx, NoLock);
   21377         [ +  + ]:          68 :         }
   21378                 :             : 
   21379                 :             :         /* Update pg_class tuple */
   21380                 :         144 :         classRel = table_open(RelationRelationId, RowExclusiveLock);
   21381                 :         144 :         tuple = SearchSysCacheCopy1(RELOID,
   21382                 :             :                                                                 ObjectIdGetDatum(RelationGetRelid(partRel)));
   21383         [ +  - ]:         144 :         if (!HeapTupleIsValid(tuple))
   21384   [ #  #  #  # ]:           0 :                 elog(ERROR, "cache lookup failed for relation %u",
   21385                 :             :                          RelationGetRelid(partRel));
   21386         [ +  - ]:         144 :         Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
   21387                 :             : 
   21388                 :             :         /* Clear relpartbound and reset relispartition */
   21389                 :         144 :         memset(new_val, 0, sizeof(new_val));
   21390                 :         144 :         memset(new_null, false, sizeof(new_null));
   21391                 :         144 :         memset(new_repl, false, sizeof(new_repl));
   21392                 :         144 :         new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
   21393                 :         144 :         new_null[Anum_pg_class_relpartbound - 1] = true;
   21394                 :         144 :         new_repl[Anum_pg_class_relpartbound - 1] = true;
   21395                 :         288 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
   21396                 :         144 :                                                                  new_val, new_null, new_repl);
   21397                 :             : 
   21398                 :         144 :         ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
   21399                 :         144 :         CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
   21400                 :         144 :         heap_freetuple(newtuple);
   21401                 :         144 :         table_close(classRel, RowExclusiveLock);
   21402                 :             : 
   21403                 :             :         /*
   21404                 :             :          * Drop identity property from all identity columns of partition.
   21405                 :             :          */
   21406         [ +  + ]:         481 :         for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
   21407                 :             :         {
   21408                 :         337 :                 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
   21409                 :             : 
   21410   [ +  +  +  + ]:         337 :                 if (!attr->attisdropped && attr->attidentity)
   21411                 :           5 :                         ATExecDropIdentity(partRel, NameStr(attr->attname), false,
   21412                 :             :                                                            AccessExclusiveLock, true, true);
   21413                 :         337 :         }
   21414                 :             : 
   21415         [ +  + ]:         144 :         if (OidIsValid(defaultPartOid))
   21416                 :             :         {
   21417                 :             :                 /*
   21418                 :             :                  * If the relation being detached is the default partition itself,
   21419                 :             :                  * remove it from the parent's pg_partitioned_table entry.
   21420                 :             :                  *
   21421                 :             :                  * If not, we must invalidate default partition's relcache entry, as
   21422                 :             :                  * in StorePartitionBound: its partition constraint depends on every
   21423                 :             :                  * other partition's partition constraint.
   21424                 :             :                  */
   21425         [ +  + ]:          31 :                 if (RelationGetRelid(partRel) == defaultPartOid)
   21426                 :           7 :                         update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
   21427                 :             :                 else
   21428                 :          24 :                         CacheInvalidateRelcacheByRelid(defaultPartOid);
   21429                 :          31 :         }
   21430                 :             : 
   21431                 :             :         /*
   21432                 :             :          * Invalidate the parent's relcache so that the partition is no longer
   21433                 :             :          * included in its partition descriptor.
   21434                 :             :          */
   21435                 :         144 :         CacheInvalidateRelcache(rel);
   21436                 :             : 
   21437                 :             :         /*
   21438                 :             :          * If the partition we just detached is partitioned itself, invalidate
   21439                 :             :          * relcache for all descendent partitions too to ensure that their
   21440                 :             :          * rd_partcheck expression trees are rebuilt; must lock partitions before
   21441                 :             :          * doing so, using the same lockmode as what partRel has been locked with
   21442                 :             :          * by the caller.
   21443                 :             :          */
   21444         [ +  + ]:         144 :         if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   21445                 :             :         {
   21446                 :          10 :                 List       *children;
   21447                 :             : 
   21448                 :          10 :                 children = find_all_inheritors(RelationGetRelid(partRel),
   21449                 :             :                                                                            AccessExclusiveLock, NULL);
   21450   [ +  -  +  +  :          33 :                 foreach(cell, children)
                   +  + ]
   21451                 :             :                 {
   21452                 :          23 :                         CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
   21453                 :          23 :                 }
   21454                 :          10 :         }
   21455                 :         144 : }
   21456                 :             : 
   21457                 :             : /*
   21458                 :             :  * ALTER TABLE ... DETACH PARTITION ... FINALIZE
   21459                 :             :  *
   21460                 :             :  * To use when a DETACH PARTITION command previously did not run to
   21461                 :             :  * completion; this completes the detaching process.
   21462                 :             :  */
   21463                 :             : static ObjectAddress
   21464                 :           0 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
   21465                 :             : {
   21466                 :           0 :         Relation        partRel;
   21467                 :             :         ObjectAddress address;
   21468                 :           0 :         Snapshot        snap = GetActiveSnapshot();
   21469                 :             : 
   21470                 :           0 :         partRel = table_openrv(name, AccessExclusiveLock);
   21471                 :             : 
   21472                 :             :         /*
   21473                 :             :          * Wait until existing snapshots are gone.  This is important if the
   21474                 :             :          * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
   21475                 :             :          * user could immediately run DETACH FINALIZE without actually waiting for
   21476                 :             :          * existing transactions.  We must not complete the detach action until
   21477                 :             :          * all such queries are complete (otherwise we would present them with an
   21478                 :             :          * inconsistent view of catalogs).
   21479                 :             :          */
   21480                 :           0 :         WaitForOlderSnapshots(snap->xmin, false);
   21481                 :             : 
   21482                 :           0 :         DetachPartitionFinalize(rel, partRel, true, InvalidOid);
   21483                 :             : 
   21484                 :           0 :         ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   21485                 :             : 
   21486                 :           0 :         table_close(partRel, NoLock);
   21487                 :             : 
   21488                 :             :         return address;
   21489                 :           0 : }
   21490                 :             : 
   21491                 :             : /*
   21492                 :             :  * DropClonedTriggersFromPartition
   21493                 :             :  *              subroutine for ATExecDetachPartition to remove any triggers that were
   21494                 :             :  *              cloned to the partition when it was created-as-partition or attached.
   21495                 :             :  *              This undoes what CloneRowTriggersToPartition did.
   21496                 :             :  */
   21497                 :             : static void
   21498                 :         144 : DropClonedTriggersFromPartition(Oid partitionId)
   21499                 :             : {
   21500                 :         144 :         ScanKeyData skey;
   21501                 :         144 :         SysScanDesc scan;
   21502                 :         144 :         HeapTuple       trigtup;
   21503                 :         144 :         Relation        tgrel;
   21504                 :         144 :         ObjectAddresses *objects;
   21505                 :             : 
   21506                 :         144 :         objects = new_object_addresses();
   21507                 :             : 
   21508                 :             :         /*
   21509                 :             :          * Scan pg_trigger to search for all triggers on this rel.
   21510                 :             :          */
   21511                 :         144 :         ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   21512                 :         144 :                                 F_OIDEQ, ObjectIdGetDatum(partitionId));
   21513                 :         144 :         tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   21514                 :         144 :         scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
   21515                 :             :                                                           true, NULL, 1, &skey);
   21516         [ +  + ]:         220 :         while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
   21517                 :             :         {
   21518                 :          76 :                 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
   21519                 :          76 :                 ObjectAddress trig;
   21520                 :             : 
   21521                 :             :                 /* Ignore triggers that weren't cloned */
   21522         [ +  + ]:          76 :                 if (!OidIsValid(pg_trigger->tgparentid))
   21523                 :          14 :                         continue;
   21524                 :             : 
   21525                 :             :                 /*
   21526                 :             :                  * Ignore internal triggers that are implementation objects of foreign
   21527                 :             :                  * keys, because these will be detached when the foreign keys
   21528                 :             :                  * themselves are.
   21529                 :             :                  */
   21530         [ +  + ]:          62 :                 if (OidIsValid(pg_trigger->tgconstrrelid))
   21531                 :          52 :                         continue;
   21532                 :             : 
   21533                 :             :                 /*
   21534                 :             :                  * This is ugly, but necessary: remove the dependency markings on the
   21535                 :             :                  * trigger so that it can be removed.
   21536                 :             :                  */
   21537                 :          10 :                 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   21538                 :             :                                                                                 TriggerRelationId,
   21539                 :             :                                                                                 DEPENDENCY_PARTITION_PRI);
   21540                 :          10 :                 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   21541                 :             :                                                                                 RelationRelationId,
   21542                 :             :                                                                                 DEPENDENCY_PARTITION_SEC);
   21543                 :             : 
   21544                 :             :                 /* remember this trigger to remove it below */
   21545                 :          10 :                 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
   21546                 :          10 :                 add_exact_object_address(&trig, objects);
   21547      [ -  +  + ]:          76 :         }
   21548                 :             : 
   21549                 :             :         /* make the dependency removal visible to the deletion below */
   21550                 :         144 :         CommandCounterIncrement();
   21551                 :         144 :         performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   21552                 :             : 
   21553                 :             :         /* done */
   21554                 :         144 :         free_object_addresses(objects);
   21555                 :         144 :         systable_endscan(scan);
   21556                 :         144 :         table_close(tgrel, RowExclusiveLock);
   21557                 :         144 : }
   21558                 :             : 
   21559                 :             : /*
   21560                 :             :  * Before acquiring lock on an index, acquire the same lock on the owning
   21561                 :             :  * table.
   21562                 :             :  */
   21563                 :             : struct AttachIndexCallbackState
   21564                 :             : {
   21565                 :             :         Oid                     partitionOid;
   21566                 :             :         Oid                     parentTblOid;
   21567                 :             :         bool            lockedParentTbl;
   21568                 :             : };
   21569                 :             : 
   21570                 :             : static void
   21571                 :          46 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
   21572                 :             :                                                            void *arg)
   21573                 :             : {
   21574                 :          46 :         struct AttachIndexCallbackState *state;
   21575                 :          46 :         Form_pg_class classform;
   21576                 :          46 :         HeapTuple       tuple;
   21577                 :             : 
   21578                 :          46 :         state = (struct AttachIndexCallbackState *) arg;
   21579                 :             : 
   21580         [ +  + ]:          46 :         if (!state->lockedParentTbl)
   21581                 :             :         {
   21582                 :          45 :                 LockRelationOid(state->parentTblOid, AccessShareLock);
   21583                 :          45 :                 state->lockedParentTbl = true;
   21584                 :          45 :         }
   21585                 :             : 
   21586                 :             :         /*
   21587                 :             :          * If we previously locked some other heap, and the name we're looking up
   21588                 :             :          * no longer refers to an index on that relation, release the now-useless
   21589                 :             :          * lock.  XXX maybe we should do *after* we verify whether the index does
   21590                 :             :          * not actually belong to the same relation ...
   21591                 :             :          */
   21592   [ +  +  +  - ]:          46 :         if (relOid != oldRelOid && OidIsValid(state->partitionOid))
   21593                 :             :         {
   21594                 :           0 :                 UnlockRelationOid(state->partitionOid, AccessShareLock);
   21595                 :           0 :                 state->partitionOid = InvalidOid;
   21596                 :           0 :         }
   21597                 :             : 
   21598                 :             :         /* Didn't find a relation, so no need for locking or permission checks. */
   21599         [ +  + ]:          46 :         if (!OidIsValid(relOid))
   21600                 :           1 :                 return;
   21601                 :             : 
   21602                 :          45 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
   21603         [ +  - ]:          45 :         if (!HeapTupleIsValid(tuple))
   21604                 :           0 :                 return;                                 /* concurrently dropped, so nothing to do */
   21605                 :          45 :         classform = (Form_pg_class) GETSTRUCT(tuple);
   21606   [ +  +  +  + ]:          45 :         if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
   21607                 :          34 :                 classform->relkind != RELKIND_INDEX)
   21608   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   21609                 :             :                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   21610                 :             :                                  errmsg("\"%s\" is not an index", rv->relname)));
   21611                 :          44 :         ReleaseSysCache(tuple);
   21612                 :             : 
   21613                 :             :         /*
   21614                 :             :          * Since we need only examine the heap's tupledesc, an access share lock
   21615                 :             :          * on it (preventing any DDL) is sufficient.
   21616                 :             :          */
   21617                 :          44 :         state->partitionOid = IndexGetRelation(relOid, false);
   21618                 :          44 :         LockRelationOid(state->partitionOid, AccessShareLock);
   21619         [ -  + ]:          45 : }
   21620                 :             : 
   21621                 :             : /*
   21622                 :             :  * ALTER INDEX i1 ATTACH PARTITION i2
   21623                 :             :  */
   21624                 :             : static ObjectAddress
   21625                 :          43 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
   21626                 :             : {
   21627                 :          43 :         Relation        partIdx;
   21628                 :          43 :         Relation        partTbl;
   21629                 :          43 :         Relation        parentTbl;
   21630                 :             :         ObjectAddress address;
   21631                 :          43 :         Oid                     partIdxId;
   21632                 :          43 :         Oid                     currParent;
   21633                 :          43 :         struct AttachIndexCallbackState state;
   21634                 :             : 
   21635                 :             :         /*
   21636                 :             :          * We need to obtain lock on the index 'name' to modify it, but we also
   21637                 :             :          * need to read its owning table's tuple descriptor -- so we need to lock
   21638                 :             :          * both.  To avoid deadlocks, obtain lock on the table before doing so on
   21639                 :             :          * the index.  Furthermore, we need to examine the parent table of the
   21640                 :             :          * partition, so lock that one too.
   21641                 :             :          */
   21642                 :          43 :         state.partitionOid = InvalidOid;
   21643                 :          43 :         state.parentTblOid = parentIdx->rd_index->indrelid;
   21644                 :          43 :         state.lockedParentTbl = false;
   21645                 :          43 :         partIdxId =
   21646                 :          43 :                 RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
   21647                 :             :                                                                  RangeVarCallbackForAttachIndex,
   21648                 :             :                                                                  &state);
   21649                 :             :         /* Not there? */
   21650         [ +  - ]:          43 :         if (!OidIsValid(partIdxId))
   21651   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   21652                 :             :                                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   21653                 :             :                                  errmsg("index \"%s\" does not exist", name->relname)));
   21654                 :             : 
   21655                 :             :         /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
   21656                 :          43 :         partIdx = relation_open(partIdxId, AccessExclusiveLock);
   21657                 :             : 
   21658                 :             :         /* we already hold locks on both tables, so this is safe: */
   21659                 :          43 :         parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
   21660                 :          43 :         partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
   21661                 :             : 
   21662                 :          43 :         ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
   21663                 :             : 
   21664                 :             :         /* Silently do nothing if already in the right state */
   21665         [ +  + ]:          43 :         currParent = partIdx->rd_rel->relispartition ?
   21666                 :           4 :                 get_partition_parent(partIdxId, false) : InvalidOid;
   21667         [ +  + ]:          43 :         if (currParent != RelationGetRelid(parentIdx))
   21668                 :             :         {
   21669                 :          38 :                 IndexInfo  *childInfo;
   21670                 :          38 :                 IndexInfo  *parentInfo;
   21671                 :          38 :                 AttrMap    *attmap;
   21672                 :          38 :                 bool            found;
   21673                 :          38 :                 int                     i;
   21674                 :          38 :                 PartitionDesc partDesc;
   21675                 :          38 :                 Oid                     constraintOid,
   21676                 :          38 :                                         cldConstrId = InvalidOid;
   21677                 :             : 
   21678                 :             :                 /*
   21679                 :             :                  * If this partition already has an index attached, refuse the
   21680                 :             :                  * operation.
   21681                 :             :                  */
   21682                 :          38 :                 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
   21683                 :             : 
   21684         [ +  - ]:          38 :                 if (OidIsValid(currParent))
   21685   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   21686                 :             :                                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21687                 :             :                                          errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   21688                 :             :                                                         RelationGetRelationName(partIdx),
   21689                 :             :                                                         RelationGetRelationName(parentIdx)),
   21690                 :             :                                          errdetail("Index \"%s\" is already attached to another index.",
   21691                 :             :                                                            RelationGetRelationName(partIdx))));
   21692                 :             : 
   21693                 :             :                 /* Make sure it indexes a partition of the other index's table */
   21694                 :          38 :                 partDesc = RelationGetPartitionDesc(parentTbl, true);
   21695                 :          38 :                 found = false;
   21696         [ +  + ]:          52 :                 for (i = 0; i < partDesc->nparts; i++)
   21697                 :             :                 {
   21698         [ +  + ]:          51 :                         if (partDesc->oids[i] == state.partitionOid)
   21699                 :             :                         {
   21700                 :          37 :                                 found = true;
   21701                 :          37 :                                 break;
   21702                 :             :                         }
   21703                 :          14 :                 }
   21704         [ +  + ]:          38 :                 if (!found)
   21705   [ +  -  +  - ]:           1 :                         ereport(ERROR,
   21706                 :             :                                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21707                 :             :                                          errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   21708                 :             :                                                         RelationGetRelationName(partIdx),
   21709                 :             :                                                         RelationGetRelationName(parentIdx)),
   21710                 :             :                                          errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
   21711                 :             :                                                            RelationGetRelationName(partIdx),
   21712                 :             :                                                            RelationGetRelationName(parentTbl))));
   21713                 :             : 
   21714                 :             :                 /* Ensure the indexes are compatible */
   21715                 :          37 :                 childInfo = BuildIndexInfo(partIdx);
   21716                 :          37 :                 parentInfo = BuildIndexInfo(parentIdx);
   21717                 :          74 :                 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
   21718                 :          37 :                                                                            RelationGetDescr(parentTbl),
   21719                 :             :                                                                            false);
   21720   [ +  +  +  + ]:          74 :                 if (!CompareIndexInfo(childInfo, parentInfo,
   21721                 :          37 :                                                           partIdx->rd_indcollation,
   21722                 :          37 :                                                           parentIdx->rd_indcollation,
   21723                 :          37 :                                                           partIdx->rd_opfamily,
   21724                 :          37 :                                                           parentIdx->rd_opfamily,
   21725                 :          37 :                                                           attmap))
   21726   [ +  -  +  - ]:           7 :                         ereport(ERROR,
   21727                 :             :                                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   21728                 :             :                                          errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   21729                 :             :                                                         RelationGetRelationName(partIdx),
   21730                 :             :                                                         RelationGetRelationName(parentIdx)),
   21731                 :             :                                          errdetail("The index definitions do not match.")));
   21732                 :             : 
   21733                 :             :                 /*
   21734                 :             :                  * If there is a constraint in the parent, make sure there is one in
   21735                 :             :                  * the child too.
   21736                 :             :                  */
   21737                 :          60 :                 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
   21738                 :          30 :                                                                                                                 RelationGetRelid(parentIdx));
   21739                 :             : 
   21740         [ +  + ]:          30 :                 if (OidIsValid(constraintOid))
   21741                 :             :                 {
   21742                 :           8 :                         cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
   21743                 :           4 :                                                                                                                   partIdxId);
   21744         [ +  + ]:           4 :                         if (!OidIsValid(cldConstrId))
   21745   [ +  -  +  - ]:           1 :                                 ereport(ERROR,
   21746                 :             :                                                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   21747                 :             :                                                  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   21748                 :             :                                                                 RelationGetRelationName(partIdx),
   21749                 :             :                                                                 RelationGetRelationName(parentIdx)),
   21750                 :             :                                                  errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
   21751                 :             :                                                                    RelationGetRelationName(parentIdx),
   21752                 :             :                                                                    RelationGetRelationName(parentTbl),
   21753                 :             :                                                                    RelationGetRelationName(partIdx))));
   21754                 :           3 :                 }
   21755                 :             : 
   21756                 :             :                 /*
   21757                 :             :                  * If it's a primary key, make sure the columns in the partition are
   21758                 :             :                  * NOT NULL.
   21759                 :             :                  */
   21760         [ +  + ]:          29 :                 if (parentIdx->rd_index->indisprimary)
   21761                 :           3 :                         verifyPartitionIndexNotNull(childInfo, partTbl);
   21762                 :             : 
   21763                 :             :                 /* All good -- do it */
   21764                 :          29 :                 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
   21765         [ +  + ]:          29 :                 if (OidIsValid(constraintOid))
   21766                 :           6 :                         ConstraintSetParentConstraint(cldConstrId, constraintOid,
   21767                 :           3 :                                                                                   RelationGetRelid(partTbl));
   21768                 :             : 
   21769                 :          29 :                 free_attrmap(attmap);
   21770                 :             : 
   21771                 :          29 :                 validatePartitionedIndex(parentIdx, parentTbl);
   21772                 :          29 :         }
   21773                 :             : 
   21774                 :          34 :         relation_close(parentTbl, AccessShareLock);
   21775                 :             :         /* keep these locks till commit */
   21776                 :          34 :         relation_close(partTbl, NoLock);
   21777                 :          34 :         relation_close(partIdx, NoLock);
   21778                 :             : 
   21779                 :             :         return address;
   21780                 :          34 : }
   21781                 :             : 
   21782                 :             : /*
   21783                 :             :  * Verify whether the given partition already contains an index attached
   21784                 :             :  * to the given partitioned index.  If so, raise an error.
   21785                 :             :  */
   21786                 :             : static void
   21787                 :          39 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
   21788                 :             : {
   21789                 :          39 :         Oid                     existingIdx;
   21790                 :             : 
   21791                 :          78 :         existingIdx = index_get_partition(partitionTbl,
   21792                 :          39 :                                                                           RelationGetRelid(parentIdx));
   21793         [ +  + ]:          39 :         if (OidIsValid(existingIdx))
   21794   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   21795                 :             :                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21796                 :             :                                  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   21797                 :             :                                                 RelationGetRelationName(partIdx),
   21798                 :             :                                                 RelationGetRelationName(parentIdx)),
   21799                 :             :                                  errdetail("Another index \"%s\" is already attached for partition \"%s\".",
   21800                 :             :                                                    get_rel_name(existingIdx),
   21801                 :             :                                                    RelationGetRelationName(partitionTbl))));
   21802                 :          38 : }
   21803                 :             : 
   21804                 :             : /*
   21805                 :             :  * Verify whether the set of attached partition indexes to a parent index on
   21806                 :             :  * a partitioned table is complete.  If it is, mark the parent index valid.
   21807                 :             :  *
   21808                 :             :  * This should be called each time a partition index is attached.
   21809                 :             :  */
   21810                 :             : static void
   21811                 :          36 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
   21812                 :             : {
   21813                 :          36 :         Relation        inheritsRel;
   21814                 :          36 :         SysScanDesc scan;
   21815                 :          36 :         ScanKeyData key;
   21816                 :          36 :         int                     tuples = 0;
   21817                 :          36 :         HeapTuple       inhTup;
   21818                 :          36 :         bool            updated = false;
   21819                 :             : 
   21820         [ +  - ]:          36 :         Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
   21821                 :             : 
   21822                 :             :         /*
   21823                 :             :          * Scan pg_inherits for this parent index.  Count each valid index we find
   21824                 :             :          * (verifying the pg_index entry for each), and if we reach the total
   21825                 :             :          * amount we expect, we can mark this parent index as valid.
   21826                 :             :          */
   21827                 :          36 :         inheritsRel = table_open(InheritsRelationId, AccessShareLock);
   21828                 :          36 :         ScanKeyInit(&key, Anum_pg_inherits_inhparent,
   21829                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
   21830                 :          36 :                                 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   21831                 :          36 :         scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
   21832                 :             :                                                           NULL, 1, &key);
   21833         [ +  + ]:          87 :         while ((inhTup = systable_getnext(scan)) != NULL)
   21834                 :             :         {
   21835                 :          51 :                 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
   21836                 :          51 :                 HeapTuple       indTup;
   21837                 :          51 :                 Form_pg_index indexForm;
   21838                 :             : 
   21839                 :          51 :                 indTup = SearchSysCache1(INDEXRELID,
   21840                 :          51 :                                                                  ObjectIdGetDatum(inhForm->inhrelid));
   21841         [ +  - ]:          51 :                 if (!HeapTupleIsValid(indTup))
   21842   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
   21843                 :          51 :                 indexForm = (Form_pg_index) GETSTRUCT(indTup);
   21844         [ +  + ]:          51 :                 if (indexForm->indisvalid)
   21845                 :          42 :                         tuples += 1;
   21846                 :          51 :                 ReleaseSysCache(indTup);
   21847                 :          51 :         }
   21848                 :             : 
   21849                 :             :         /* Done with pg_inherits */
   21850                 :          36 :         systable_endscan(scan);
   21851                 :          36 :         table_close(inheritsRel, AccessShareLock);
   21852                 :             : 
   21853                 :             :         /*
   21854                 :             :          * If we found as many inherited indexes as the partitioned table has
   21855                 :             :          * partitions, we're good; update pg_index to set indisvalid.
   21856                 :             :          */
   21857         [ +  + ]:          36 :         if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
   21858                 :             :         {
   21859                 :          20 :                 Relation        idxRel;
   21860                 :          20 :                 HeapTuple       indTup;
   21861                 :          20 :                 Form_pg_index indexForm;
   21862                 :             : 
   21863                 :          20 :                 idxRel = table_open(IndexRelationId, RowExclusiveLock);
   21864                 :          20 :                 indTup = SearchSysCacheCopy1(INDEXRELID,
   21865                 :             :                                                                          ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   21866         [ +  - ]:          20 :                 if (!HeapTupleIsValid(indTup))
   21867   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for index %u",
   21868                 :             :                                  RelationGetRelid(partedIdx));
   21869                 :          20 :                 indexForm = (Form_pg_index) GETSTRUCT(indTup);
   21870                 :             : 
   21871                 :          20 :                 indexForm->indisvalid = true;
   21872                 :          20 :                 updated = true;
   21873                 :             : 
   21874                 :          20 :                 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
   21875                 :             : 
   21876                 :          20 :                 table_close(idxRel, RowExclusiveLock);
   21877                 :          20 :                 heap_freetuple(indTup);
   21878                 :          20 :         }
   21879                 :             : 
   21880                 :             :         /*
   21881                 :             :          * If this index is in turn a partition of a larger index, validating it
   21882                 :             :          * might cause the parent to become valid also.  Try that.
   21883                 :             :          */
   21884   [ +  +  +  + ]:          36 :         if (updated && partedIdx->rd_rel->relispartition)
   21885                 :             :         {
   21886                 :           7 :                 Oid                     parentIdxId,
   21887                 :             :                                         parentTblId;
   21888                 :           7 :                 Relation        parentIdx,
   21889                 :             :                                         parentTbl;
   21890                 :             : 
   21891                 :             :                 /* make sure we see the validation we just did */
   21892                 :           7 :                 CommandCounterIncrement();
   21893                 :             : 
   21894                 :           7 :                 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
   21895                 :           7 :                 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
   21896                 :           7 :                 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
   21897                 :           7 :                 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
   21898         [ +  - ]:           7 :                 Assert(!parentIdx->rd_index->indisvalid);
   21899                 :             : 
   21900                 :           7 :                 validatePartitionedIndex(parentIdx, parentTbl);
   21901                 :             : 
   21902                 :           7 :                 relation_close(parentIdx, AccessExclusiveLock);
   21903                 :           7 :                 relation_close(parentTbl, AccessExclusiveLock);
   21904                 :           7 :         }
   21905                 :          36 : }
   21906                 :             : 
   21907                 :             : /*
   21908                 :             :  * When attaching an index as a partition of a partitioned index which is a
   21909                 :             :  * primary key, verify that all the columns in the partition are marked NOT
   21910                 :             :  * NULL.
   21911                 :             :  */
   21912                 :             : static void
   21913                 :           3 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
   21914                 :             : {
   21915         [ +  + ]:           6 :         for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
   21916                 :             :         {
   21917                 :           6 :                 Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
   21918                 :           3 :                                                                                           iinfo->ii_IndexAttrNumbers[i] - 1);
   21919                 :             : 
   21920         [ +  - ]:           3 :                 if (!att->attnotnull)
   21921   [ #  #  #  # ]:           0 :                         ereport(ERROR,
   21922                 :             :                                         errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   21923                 :             :                                         errmsg("invalid primary key definition"),
   21924                 :             :                                         errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
   21925                 :             :                                                           NameStr(att->attname),
   21926                 :             :                                                           RelationGetRelationName(partition)));
   21927                 :           3 :         }
   21928                 :           3 : }
   21929                 :             : 
   21930                 :             : /*
   21931                 :             :  * Return an OID list of constraints that reference the given relation
   21932                 :             :  * that are marked as having a parent constraints.
   21933                 :             :  */
   21934                 :             : static List *
   21935                 :         210 : GetParentedForeignKeyRefs(Relation partition)
   21936                 :             : {
   21937                 :         210 :         Relation        pg_constraint;
   21938                 :         210 :         HeapTuple       tuple;
   21939                 :         210 :         SysScanDesc scan;
   21940                 :         210 :         ScanKeyData key[2];
   21941                 :         210 :         List       *constraints = NIL;
   21942                 :             : 
   21943                 :             :         /*
   21944                 :             :          * If no indexes, or no columns are referenceable by FKs, we can avoid the
   21945                 :             :          * scan.
   21946                 :             :          */
   21947   [ +  +  +  + ]:         210 :         if (RelationGetIndexList(partition) == NIL ||
   21948                 :          92 :                 bms_is_empty(RelationGetIndexAttrBitmap(partition,
   21949                 :             :                                                                                                 INDEX_ATTR_BITMAP_KEY)))
   21950                 :         160 :                 return NIL;
   21951                 :             : 
   21952                 :             :         /* Search for constraints referencing this table */
   21953                 :          50 :         pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   21954                 :         100 :         ScanKeyInit(&key[0],
   21955                 :             :                                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   21956                 :          50 :                                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
   21957                 :         100 :         ScanKeyInit(&key[1],
   21958                 :             :                                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   21959                 :          50 :                                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   21960                 :             : 
   21961                 :             :         /* XXX This is a seqscan, as we don't have a usable index */
   21962                 :          50 :         scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
   21963         [ +  + ]:          70 :         while ((tuple = systable_getnext(scan)) != NULL)
   21964                 :             :         {
   21965                 :          20 :                 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   21966                 :             : 
   21967                 :             :                 /*
   21968                 :             :                  * We only need to process constraints that are part of larger ones.
   21969                 :             :                  */
   21970         [ +  - ]:          20 :                 if (!OidIsValid(constrForm->conparentid))
   21971                 :           0 :                         continue;
   21972                 :             : 
   21973                 :          20 :                 constraints = lappend_oid(constraints, constrForm->oid);
   21974      [ -  -  + ]:          20 :         }
   21975                 :             : 
   21976                 :          50 :         systable_endscan(scan);
   21977                 :          50 :         table_close(pg_constraint, AccessShareLock);
   21978                 :             : 
   21979                 :          50 :         return constraints;
   21980                 :         210 : }
   21981                 :             : 
   21982                 :             : /*
   21983                 :             :  * During DETACH PARTITION, verify that any foreign keys pointing to the
   21984                 :             :  * partitioned table would not become invalid.  An error is raised if any
   21985                 :             :  * referenced values exist.
   21986                 :             :  */
   21987                 :             : static void
   21988                 :          66 : ATDetachCheckNoForeignKeyRefs(Relation partition)
   21989                 :             : {
   21990                 :          66 :         List       *constraints;
   21991                 :          66 :         ListCell   *cell;
   21992                 :             : 
   21993                 :          66 :         constraints = GetParentedForeignKeyRefs(partition);
   21994                 :             : 
   21995   [ +  +  +  +  :          71 :         foreach(cell, constraints)
                   +  + ]
   21996                 :             :         {
   21997                 :           5 :                 Oid                     constrOid = lfirst_oid(cell);
   21998                 :           5 :                 HeapTuple       tuple;
   21999                 :           5 :                 Form_pg_constraint constrForm;
   22000                 :           5 :                 Relation        rel;
   22001                 :           5 :                 Trigger         trig = {0};
   22002                 :             : 
   22003                 :           5 :                 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   22004         [ +  - ]:           5 :                 if (!HeapTupleIsValid(tuple))
   22005   [ #  #  #  # ]:           0 :                         elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   22006                 :           5 :                 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   22007                 :             : 
   22008         [ -  + ]:           5 :                 Assert(OidIsValid(constrForm->conparentid));
   22009         [ -  + ]:           5 :                 Assert(constrForm->confrelid == RelationGetRelid(partition));
   22010                 :             : 
   22011                 :             :                 /* prevent data changes into the referencing table until commit */
   22012                 :           5 :                 rel = table_open(constrForm->conrelid, ShareLock);
   22013                 :             : 
   22014                 :           5 :                 trig.tgoid = InvalidOid;
   22015                 :           5 :                 trig.tgname = NameStr(constrForm->conname);
   22016                 :           5 :                 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   22017                 :           5 :                 trig.tgisinternal = true;
   22018                 :           5 :                 trig.tgconstrrelid = RelationGetRelid(partition);
   22019                 :           5 :                 trig.tgconstrindid = constrForm->conindid;
   22020                 :           5 :                 trig.tgconstraint = constrForm->oid;
   22021                 :           5 :                 trig.tgdeferrable = false;
   22022                 :           5 :                 trig.tginitdeferred = false;
   22023                 :             :                 /* we needn't fill in remaining fields */
   22024                 :             : 
   22025                 :           5 :                 RI_PartitionRemove_Check(&trig, rel, partition);
   22026                 :             : 
   22027                 :           5 :                 ReleaseSysCache(tuple);
   22028                 :             : 
   22029                 :           5 :                 table_close(rel, NoLock);
   22030                 :           5 :         }
   22031                 :          66 : }
   22032                 :             : 
   22033                 :             : /*
   22034                 :             :  * resolve column compression specification to compression method.
   22035                 :             :  */
   22036                 :             : static char
   22037                 :       15038 : GetAttributeCompression(Oid atttypid, const char *compression)
   22038                 :             : {
   22039                 :       15038 :         char            cmethod;
   22040                 :             : 
   22041   [ +  +  +  + ]:       15038 :         if (compression == NULL || strcmp(compression, "default") == 0)
   22042                 :       15008 :                 return InvalidCompressionMethod;
   22043                 :             : 
   22044                 :             :         /*
   22045                 :             :          * To specify a nondefault method, the column data type must be toastable.
   22046                 :             :          * Note this says nothing about whether the column's attstorage setting
   22047                 :             :          * permits compression; we intentionally allow attstorage and
   22048                 :             :          * attcompression to be independent.  But with a non-toastable type,
   22049                 :             :          * attstorage could not be set to a value that would permit compression.
   22050                 :             :          *
   22051                 :             :          * We don't actually need to enforce this, since nothing bad would happen
   22052                 :             :          * if attcompression were non-default; it would never be consulted.  But
   22053                 :             :          * it seems more user-friendly to complain about a certainly-useless
   22054                 :             :          * attempt to set the property.
   22055                 :             :          */
   22056         [ +  + ]:          30 :         if (!TypeIsToastable(atttypid))
   22057   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   22058                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   22059                 :             :                                  errmsg("column data type %s does not support compression",
   22060                 :             :                                                 format_type_be(atttypid))));
   22061                 :             : 
   22062                 :          29 :         cmethod = CompressionNameToMethod(compression);
   22063         [ +  + ]:          29 :         if (!CompressionMethodIsValid(cmethod))
   22064   [ +  -  +  - ]:           2 :                 ereport(ERROR,
   22065                 :             :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   22066                 :             :                                  errmsg("invalid compression method \"%s\"", compression)));
   22067                 :             : 
   22068                 :          27 :         return cmethod;
   22069                 :       15035 : }
   22070                 :             : 
   22071                 :             : /*
   22072                 :             :  * resolve column storage specification
   22073                 :             :  */
   22074                 :             : static char
   22075                 :          39 : GetAttributeStorage(Oid atttypid, const char *storagemode)
   22076                 :             : {
   22077                 :          39 :         char            cstorage = 0;
   22078                 :             : 
   22079         [ +  + ]:          39 :         if (pg_strcasecmp(storagemode, "plain") == 0)
   22080                 :           8 :                 cstorage = TYPSTORAGE_PLAIN;
   22081         [ +  + ]:          31 :         else if (pg_strcasecmp(storagemode, "external") == 0)
   22082                 :          16 :                 cstorage = TYPSTORAGE_EXTERNAL;
   22083         [ +  + ]:          15 :         else if (pg_strcasecmp(storagemode, "extended") == 0)
   22084                 :           6 :                 cstorage = TYPSTORAGE_EXTENDED;
   22085         [ +  + ]:           9 :         else if (pg_strcasecmp(storagemode, "main") == 0)
   22086                 :           8 :                 cstorage = TYPSTORAGE_MAIN;
   22087         [ +  - ]:           1 :         else if (pg_strcasecmp(storagemode, "default") == 0)
   22088                 :           1 :                 cstorage = get_typstorage(atttypid);
   22089                 :             :         else
   22090   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   22091                 :             :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   22092                 :             :                                  errmsg("invalid storage type \"%s\"",
   22093                 :             :                                                 storagemode)));
   22094                 :             : 
   22095                 :             :         /*
   22096                 :             :          * safety check: do not allow toasted storage modes unless column datatype
   22097                 :             :          * is TOAST-aware.
   22098                 :             :          */
   22099   [ +  +  +  + ]:          39 :         if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
   22100   [ +  -  +  - ]:           1 :                 ereport(ERROR,
   22101                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   22102                 :             :                                  errmsg("column data type %s can only have storage PLAIN",
   22103                 :             :                                                 format_type_be(atttypid))));
   22104                 :             : 
   22105                 :          76 :         return cstorage;
   22106                 :          38 : }
   22107                 :             : 
   22108                 :             : /*
   22109                 :             :  * buildExpressionExecutionStates: build the needed expression execution states
   22110                 :             :  * for new partition (newPartRel) checks and initialize expressions for
   22111                 :             :  * generated columns. All expressions should be created in "tab"
   22112                 :             :  * (AlteredTableInfo structure).
   22113                 :             :  */
   22114                 :             : static void
   22115                 :          96 : buildExpressionExecutionStates(AlteredTableInfo *tab, Relation newPartRel, EState *estate)
   22116                 :             : {
   22117                 :             :         /*
   22118                 :             :          * Build the needed expression execution states. Here, we expect only NOT
   22119                 :             :          * NULL and CHECK constraint.
   22120                 :             :          */
   22121   [ +  +  +  +  :         196 :         foreach_ptr(NewConstraint, con, tab->constraints)
             +  +  +  + ]
   22122                 :             :         {
   22123      [ -  +  - ]:           4 :                 switch (con->contype)
   22124                 :             :                 {
   22125                 :             :                         case CONSTR_CHECK:
   22126                 :             : 
   22127                 :             :                                 /*
   22128                 :             :                                  * We already expanded virtual expression in
   22129                 :             :                                  * createTableConstraints.
   22130                 :             :                                  */
   22131                 :           4 :                                 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
   22132                 :           4 :                                 break;
   22133                 :             :                         case CONSTR_NOTNULL:
   22134                 :             :                                 /* Nothing to do here. */
   22135                 :             :                                 break;
   22136                 :             :                         default:
   22137   [ #  #  #  # ]:           0 :                                 elog(ERROR, "unrecognized constraint type: %d",
   22138                 :             :                                          (int) con->contype);
   22139                 :           0 :                 }
   22140                 :         100 :         }
   22141                 :             : 
   22142                 :             :         /* Expression already planned in createTableConstraints */
   22143   [ +  +  +  +  :         203 :         foreach_ptr(NewColumnValue, ex, tab->newvals)
             +  +  +  + ]
   22144                 :         107 :                 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
   22145                 :          96 : }
   22146                 :             : 
   22147                 :             : /*
   22148                 :             :  * evaluateGeneratedExpressionsAndCheckConstraints: evaluate any generated
   22149                 :             :  * expressions for "tab" (AlteredTableInfo structure) whose inputs come from
   22150                 :             :  * the new tuple (insertslot) of the new partition (newPartRel).
   22151                 :             :  */
   22152                 :             : static void
   22153                 :         155 : evaluateGeneratedExpressionsAndCheckConstraints(AlteredTableInfo *tab,
   22154                 :             :                                                                                                 Relation newPartRel,
   22155                 :             :                                                                                                 TupleTableSlot *insertslot,
   22156                 :             :                                                                                                 ExprContext *econtext)
   22157                 :             : {
   22158                 :         155 :         econtext->ecxt_scantuple = insertslot;
   22159                 :             : 
   22160   [ +  +  +  +  :         326 :         foreach_ptr(NewColumnValue, ex, tab->newvals)
             +  +  +  + ]
   22161                 :             :         {
   22162         [ +  - ]:          16 :                 if (!ex->is_generated)
   22163                 :           0 :                         continue;
   22164                 :             : 
   22165                 :          16 :                 insertslot->tts_values[ex->attnum - 1]
   22166                 :          48 :                         = ExecEvalExpr(ex->exprstate,
   22167                 :          16 :                                                    econtext,
   22168                 :          16 :                                                    &insertslot->tts_isnull[ex->attnum - 1]);
   22169                 :         171 :         }
   22170                 :             : 
   22171   [ +  +  +  +  :         316 :         foreach_ptr(NewConstraint, con, tab->constraints)
             +  +  +  + ]
   22172                 :             :         {
   22173      [ -  -  + ]:           6 :                 switch (con->contype)
   22174                 :             :                 {
   22175                 :             :                         case CONSTR_CHECK:
   22176         [ +  - ]:           6 :                                 if (!ExecCheck(con->qualstate, econtext))
   22177   [ #  #  #  # ]:           0 :                                         ereport(ERROR,
   22178                 :             :                                                         errcode(ERRCODE_CHECK_VIOLATION),
   22179                 :             :                                                         errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
   22180                 :             :                                                                    con->name, RelationGetRelationName(newPartRel)),
   22181                 :             :                                                         errtableconstraint(newPartRel, con->name));
   22182                 :           6 :                                 break;
   22183                 :             :                         case CONSTR_NOTNULL:
   22184                 :             :                         case CONSTR_FOREIGN:
   22185                 :             :                                 /* Nothing to do here */
   22186                 :           0 :                                 break;
   22187                 :             :                         default:
   22188   [ #  #  #  # ]:           0 :                                 elog(ERROR, "unrecognized constraint type: %d",
   22189                 :             :                                          (int) con->contype);
   22190                 :           0 :                 }
   22191                 :         161 :         }
   22192                 :         155 : }
   22193                 :             : 
   22194                 :             : /*
   22195                 :             :  * getAttributesList: build a list of columns (ColumnDef) based on parent_rel
   22196                 :             :  */
   22197                 :             : static List *
   22198                 :         101 : getAttributesList(Relation parent_rel)
   22199                 :             : {
   22200                 :         101 :         AttrNumber      parent_attno;
   22201                 :         101 :         TupleDesc       modelDesc;
   22202                 :         101 :         List       *colList = NIL;
   22203                 :             : 
   22204                 :         101 :         modelDesc = RelationGetDescr(parent_rel);
   22205                 :             : 
   22206         [ +  + ]:         369 :         for (parent_attno = 1; parent_attno <= modelDesc->natts;
   22207                 :         268 :                  parent_attno++)
   22208                 :             :         {
   22209                 :         536 :                 Form_pg_attribute attribute = TupleDescAttr(modelDesc,
   22210                 :         268 :                                                                                                         parent_attno - 1);
   22211                 :         268 :                 ColumnDef  *def;
   22212                 :             : 
   22213                 :             :                 /* Ignore dropped columns in the parent. */
   22214         [ -  + ]:         268 :                 if (attribute->attisdropped)
   22215                 :           0 :                         continue;
   22216                 :             : 
   22217                 :         536 :                 def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
   22218                 :         268 :                                                         attribute->atttypmod, attribute->attcollation);
   22219                 :             : 
   22220                 :         268 :                 def->is_not_null = attribute->attnotnull;
   22221                 :             : 
   22222                 :             :                 /* Copy identity. */
   22223                 :         268 :                 def->identity = attribute->attidentity;
   22224                 :             : 
   22225                 :             :                 /* Copy attgenerated. */
   22226                 :         268 :                 def->generated = attribute->attgenerated;
   22227                 :             : 
   22228                 :         268 :                 def->storage = attribute->attstorage;
   22229                 :             : 
   22230                 :             :                 /* Likewise, copy compression. */
   22231         [ +  + ]:         268 :                 if (CompressionMethodIsValid(attribute->attcompression))
   22232                 :           3 :                         def->compression =
   22233                 :           3 :                                 pstrdup(GetCompressionMethodName(attribute->attcompression));
   22234                 :             :                 else
   22235                 :         265 :                         def->compression = NULL;
   22236                 :             : 
   22237                 :             :                 /* Add to column list. */
   22238                 :         268 :                 colList = lappend(colList, def);
   22239      [ -  -  + ]:         268 :         }
   22240                 :             : 
   22241                 :         202 :         return colList;
   22242                 :         101 : }
   22243                 :             : 
   22244                 :             : /*
   22245                 :             :  * createTableConstraints:
   22246                 :             :  * create check constraints, default values, and generated values for newRel
   22247                 :             :  * based on parent_rel.  tab is pending-work queue for newRel, we may need it in
   22248                 :             :  * MergePartitionsMoveRows.
   22249                 :             :  */
   22250                 :             : static void
   22251                 :          96 : createTableConstraints(List **wqueue, AlteredTableInfo *tab,
   22252                 :             :                                            Relation parent_rel, Relation newRel)
   22253                 :             : {
   22254                 :          96 :         TupleDesc       tupleDesc;
   22255                 :          96 :         TupleConstr *constr;
   22256                 :          96 :         AttrMap    *attmap;
   22257                 :          96 :         AttrNumber      parent_attno;
   22258                 :          96 :         int                     ccnum;
   22259                 :          96 :         List       *constraints = NIL;
   22260                 :          96 :         List       *cookedConstraints = NIL;
   22261                 :             : 
   22262                 :          96 :         tupleDesc = RelationGetDescr(parent_rel);
   22263                 :          96 :         constr = tupleDesc->constr;
   22264                 :             : 
   22265         [ +  + ]:          96 :         if (!constr)
   22266                 :          57 :                 return;
   22267                 :             : 
   22268                 :             :         /*
   22269                 :             :          * Construct a map from the parent relation's attnos to the child rel's.
   22270                 :             :          * This re-checks type match, etc, although it shouldn't be possible to
   22271                 :             :          * have a failure since both tables are locked.
   22272                 :             :          */
   22273                 :          78 :         attmap = build_attrmap_by_name(RelationGetDescr(newRel),
   22274                 :          39 :                                                                    tupleDesc,
   22275                 :             :                                                                    false);
   22276                 :             : 
   22277                 :             :         /* Cycle for default values. */
   22278         [ +  + ]:         148 :         for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
   22279                 :             :         {
   22280                 :         218 :                 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
   22281                 :         109 :                                                                                                         parent_attno - 1);
   22282                 :             : 
   22283                 :             :                 /* Ignore dropped columns in the parent. */
   22284         [ -  + ]:         109 :                 if (attribute->attisdropped)
   22285                 :           0 :                         continue;
   22286                 :             : 
   22287                 :             :                 /* Copy the default, if present, and it should be copied. */
   22288         [ +  + ]:         109 :                 if (attribute->atthasdef)
   22289                 :             :                 {
   22290                 :          25 :                         Node       *this_default = NULL;
   22291                 :          25 :                         bool            found_whole_row;
   22292                 :          25 :                         AttrNumber      num;
   22293                 :          25 :                         Node       *def;
   22294                 :          25 :                         NewColumnValue *newval;
   22295                 :             : 
   22296         [ +  + ]:          25 :                         if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
   22297                 :           1 :                                 this_default = build_generation_expression(parent_rel, attribute->attnum);
   22298                 :             :                         else
   22299                 :             :                         {
   22300                 :          24 :                                 this_default = TupleDescGetDefault(tupleDesc, attribute->attnum);
   22301         [ +  - ]:          24 :                                 if (this_default == NULL)
   22302   [ #  #  #  # ]:           0 :                                         elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
   22303                 :             :                                                  attribute->attnum, RelationGetRelationName(parent_rel));
   22304                 :             :                         }
   22305                 :             : 
   22306                 :          25 :                         num = attmap->attnums[parent_attno - 1];
   22307                 :          25 :                         def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
   22308                 :             : 
   22309   [ -  +  #  # ]:          25 :                         if (found_whole_row && attribute->attgenerated != '\0')
   22310   [ #  #  #  # ]:           0 :                                 elog(ERROR, "cannot convert whole-row table reference");
   22311                 :             : 
   22312                 :             :                         /* Add a pre-cooked default expression. */
   22313                 :          25 :                         StoreAttrDefault(newRel, num, def, true);
   22314                 :             : 
   22315                 :             :                         /*
   22316                 :             :                          * Stored generated column expressions in parent_rel might
   22317                 :             :                          * reference the tableoid.  newRel, parent_rel tableoid clear is
   22318                 :             :                          * not the same. If so, these stored generated columns require
   22319                 :             :                          * recomputation for newRel within MergePartitionsMoveRows.
   22320                 :             :                          */
   22321         [ +  + ]:          25 :                         if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
   22322                 :             :                         {
   22323                 :          11 :                                 newval = palloc0_object(NewColumnValue);
   22324                 :          11 :                                 newval->attnum = num;
   22325                 :          11 :                                 newval->expr = expression_planner((Expr *) def);
   22326                 :          11 :                                 newval->is_generated = (attribute->attgenerated != '\0');
   22327                 :          11 :                                 tab->newvals = lappend(tab->newvals, newval);
   22328                 :          11 :                         }
   22329                 :          25 :                 }
   22330         [ -  + ]:         109 :         }
   22331                 :             : 
   22332                 :             :         /* Cycle for CHECK constraints. */
   22333         [ +  + ]:          56 :         for (ccnum = 0; ccnum < constr->num_check; ccnum++)
   22334                 :             :         {
   22335                 :          17 :                 char       *ccname = constr->check[ccnum].ccname;
   22336                 :          17 :                 char       *ccbin = constr->check[ccnum].ccbin;
   22337                 :          17 :                 bool            ccenforced = constr->check[ccnum].ccenforced;
   22338                 :          17 :                 bool            ccnoinherit = constr->check[ccnum].ccnoinherit;
   22339                 :          17 :                 bool            ccvalid = constr->check[ccnum].ccvalid;
   22340                 :          17 :                 Node       *ccbin_node;
   22341                 :          17 :                 bool            found_whole_row;
   22342                 :          17 :                 Constraint *constr;
   22343                 :             : 
   22344                 :             :                 /*
   22345                 :             :                  * The partitioned table can not have a NO INHERIT check constraint
   22346                 :             :                  * (see StoreRelCheck function for details).
   22347                 :             :                  */
   22348         [ +  - ]:          17 :                 Assert(!ccnoinherit);
   22349                 :             : 
   22350                 :          34 :                 ccbin_node = map_variable_attnos(stringToNode(ccbin),
   22351                 :             :                                                                                  1, 0,
   22352                 :          17 :                                                                                  attmap,
   22353                 :             :                                                                                  InvalidOid, &found_whole_row);
   22354                 :             : 
   22355                 :             :                 /*
   22356                 :             :                  * For the moment we have to reject whole-row variables (as for CREATE
   22357                 :             :                  * TABLE LIKE and inheritances).
   22358                 :             :                  */
   22359         [ +  - ]:          17 :                 if (found_whole_row)
   22360   [ #  #  #  # ]:           0 :                         elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
   22361                 :             :                                  ccname,
   22362                 :             :                                  RelationGetRelationName(parent_rel));
   22363                 :             : 
   22364                 :          17 :                 constr = makeNode(Constraint);
   22365                 :          17 :                 constr->contype = CONSTR_CHECK;
   22366                 :          17 :                 constr->conname = pstrdup(ccname);
   22367                 :          17 :                 constr->deferrable = false;
   22368                 :          17 :                 constr->initdeferred = false;
   22369                 :          17 :                 constr->is_enforced = ccenforced;
   22370                 :          17 :                 constr->skip_validation = !ccvalid;
   22371                 :          17 :                 constr->initially_valid = ccvalid;
   22372                 :          17 :                 constr->is_no_inherit = ccnoinherit;
   22373                 :          17 :                 constr->raw_expr = NULL;
   22374                 :          17 :                 constr->cooked_expr = nodeToString(ccbin_node);
   22375                 :          17 :                 constr->location = -1;
   22376                 :          17 :                 constraints = lappend(constraints, constr);
   22377                 :          17 :         }
   22378                 :             : 
   22379                 :             :         /* Install all CHECK constraints. */
   22380                 :          39 :         cookedConstraints = AddRelationNewConstraints(newRel, NIL, constraints,
   22381                 :             :                                                                                                   false, true, true, NULL);
   22382                 :             : 
   22383                 :             :         /* Make the additional catalog changes visible. */
   22384                 :          39 :         CommandCounterIncrement();
   22385                 :             : 
   22386                 :             :         /*
   22387                 :             :          * parent_rel check constraint expression may reference tableoid, so later
   22388                 :             :          * in MergePartitionsMoveRows, we need to evaluate the check constraint
   22389                 :             :          * again for the newRel. We can check whether the check constraint
   22390                 :             :          * contains a tableoid reference via pull_varattnos.
   22391                 :             :          */
   22392   [ +  +  +  +  :          95 :         foreach_ptr(CookedConstraint, ccon, cookedConstraints)
             +  +  +  + ]
   22393                 :             :         {
   22394         [ +  + ]:          17 :                 if (!ccon->skip_validation)
   22395                 :             :                 {
   22396                 :          11 :                         Node       *qual;
   22397                 :          11 :                         Bitmapset  *attnums = NULL;
   22398                 :             : 
   22399         [ -  + ]:          11 :                         Assert(ccon->contype == CONSTR_CHECK);
   22400                 :          11 :                         qual = expand_generated_columns_in_expr(ccon->expr, newRel, 1);
   22401                 :          11 :                         pull_varattnos(qual, 1, &attnums);
   22402                 :             : 
   22403                 :             :                         /*
   22404                 :             :                          * Add a check only if it contains a tableoid
   22405                 :             :                          * (TableOidAttributeNumber).
   22406                 :             :                          */
   22407         [ +  + ]:          11 :                         if (bms_is_member(TableOidAttributeNumber - FirstLowInvalidHeapAttributeNumber,
   22408                 :          11 :                                                           attnums))
   22409                 :             :                         {
   22410                 :           4 :                                 NewConstraint *newcon;
   22411                 :             : 
   22412                 :           4 :                                 newcon = palloc0_object(NewConstraint);
   22413                 :           4 :                                 newcon->name = ccon->name;
   22414                 :           4 :                                 newcon->contype = CONSTR_CHECK;
   22415                 :           4 :                                 newcon->qual = qual;
   22416                 :             : 
   22417                 :           4 :                                 tab->constraints = lappend(tab->constraints, newcon);
   22418                 :           4 :                         }
   22419                 :          11 :                 }
   22420                 :          56 :         }
   22421                 :             : 
   22422                 :             :         /* Don't need the cookedConstraints anymore. */
   22423                 :          39 :         list_free_deep(cookedConstraints);
   22424                 :             : 
   22425                 :             :         /* Reproduce not-null constraints. */
   22426         [ +  + ]:          39 :         if (constr->has_not_null)
   22427                 :             :         {
   22428                 :          27 :                 List       *nnconstraints;
   22429                 :             : 
   22430                 :             :                 /*
   22431                 :             :                  * The "include_noinh" argument is false because a partitioned table
   22432                 :             :                  * can't have NO INHERIT constraint.
   22433                 :             :                  */
   22434                 :          27 :                 nnconstraints = RelationGetNotNullConstraints(RelationGetRelid(parent_rel),
   22435                 :             :                                                                                                           false, false);
   22436                 :             : 
   22437         [ +  - ]:          27 :                 Assert(list_length(nnconstraints) > 0);
   22438                 :             : 
   22439                 :             :                 /*
   22440                 :             :                  * We already set pg_attribute.attnotnull in createPartitionTable. No
   22441                 :             :                  * need call set_attnotnull again.
   22442                 :             :                  */
   22443                 :          27 :                 AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, true, NULL);
   22444                 :          27 :         }
   22445                 :          96 : }
   22446                 :             : 
   22447                 :             : /*
   22448                 :             :  * createPartitionTable:
   22449                 :             :  *
   22450                 :             :  * Create a new partition (newPartName) for the partitioned table (parent_rel).
   22451                 :             :  * ownerId is determined by the partition on which the operation is performed,
   22452                 :             :  * so it is passed separately.  The new partition will inherit the access method
   22453                 :             :  * and persistence type from the parent table.
   22454                 :             :  *
   22455                 :             :  * Returns the created relation (locked in AccessExclusiveLock mode).
   22456                 :             :  */
   22457                 :             : static Relation
   22458                 :         101 : createPartitionTable(List **wqueue, RangeVar *newPartName,
   22459                 :             :                                          Relation parent_rel, Oid ownerId)
   22460                 :             : {
   22461                 :         101 :         Relation        newRel;
   22462                 :         101 :         Oid                     newRelId;
   22463                 :         101 :         Oid                     existingRelid;
   22464                 :         101 :         TupleDesc       descriptor;
   22465                 :         101 :         List       *colList = NIL;
   22466                 :         101 :         Oid                     relamId;
   22467                 :         101 :         Oid                     namespaceId;
   22468                 :         101 :         AlteredTableInfo *new_partrel_tab;
   22469                 :         101 :         Form_pg_class parent_relform = parent_rel->rd_rel;
   22470                 :             : 
   22471                 :             :         /* If the existing rel is temp, it must belong to this session. */
   22472   [ +  +  +  - ]:         101 :         if (RELATION_IS_OTHER_TEMP(parent_rel))
   22473   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   22474                 :             :                                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   22475                 :             :                                 errmsg("cannot create as partition of temporary relation of another session"));
   22476                 :             : 
   22477                 :             :         /* Look up inheritance ancestors and generate the relation schema. */
   22478                 :         101 :         colList = getAttributesList(parent_rel);
   22479                 :             : 
   22480                 :             :         /* Create a tuple descriptor from the relation schema. */
   22481                 :         101 :         descriptor = BuildDescForRelation(colList);
   22482                 :             : 
   22483                 :             :         /* Look up the access method for the new relation. */
   22484         [ +  + ]:         101 :         relamId = (parent_relform->relam != InvalidOid) ? parent_relform->relam : HEAP_TABLE_AM_OID;
   22485                 :             : 
   22486                 :             :         /* Look up the namespace in which we are supposed to create the relation. */
   22487                 :         101 :         namespaceId =
   22488                 :         101 :                 RangeVarGetAndCheckCreationNamespace(newPartName, NoLock, &existingRelid);
   22489         [ +  - ]:         101 :         if (OidIsValid(existingRelid))
   22490   [ #  #  #  # ]:           0 :                 ereport(ERROR,
   22491                 :             :                                 errcode(ERRCODE_DUPLICATE_TABLE),
   22492                 :             :                                 errmsg("relation \"%s\" already exists", newPartName->relname));
   22493                 :             : 
   22494                 :             :         /*
   22495                 :             :          * We intended to create the partition with the same persistence as the
   22496                 :             :          * parent table, but we still need to recheck because that might be
   22497                 :             :          * affected by the search_path.  If the parent is permanent, so must be
   22498                 :             :          * all of its partitions.
   22499                 :             :          */
   22500   [ +  +  +  + ]:         101 :         if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
   22501                 :          92 :                 newPartName->relpersistence == RELPERSISTENCE_TEMP)
   22502   [ +  -  +  - ]:           2 :                 ereport(ERROR,
   22503                 :             :                                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   22504                 :             :                                 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
   22505                 :             :                                            RelationGetRelationName(parent_rel)));
   22506                 :             : 
   22507                 :             :         /* Permanent rels cannot be partitions belonging to a temporary parent. */
   22508   [ +  +  +  + ]:          99 :         if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
   22509                 :          93 :                 parent_relform->relpersistence == RELPERSISTENCE_TEMP)
   22510   [ +  -  +  - ]:           3 :                 ereport(ERROR,
   22511                 :             :                                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   22512                 :             :                                 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
   22513                 :             :                                            RelationGetRelationName(parent_rel)));
   22514                 :             : 
   22515                 :             :         /* Create the relation. */
   22516                 :         192 :         newRelId = heap_create_with_catalog(newPartName->relname,
   22517                 :          96 :                                                                                 namespaceId,
   22518                 :          96 :                                                                                 parent_relform->reltablespace,
   22519                 :             :                                                                                 InvalidOid,
   22520                 :             :                                                                                 InvalidOid,
   22521                 :             :                                                                                 InvalidOid,
   22522                 :          96 :                                                                                 ownerId,
   22523                 :          96 :                                                                                 relamId,
   22524                 :          96 :                                                                                 descriptor,
   22525                 :             :                                                                                 NIL,
   22526                 :             :                                                                                 RELKIND_RELATION,
   22527                 :          96 :                                                                                 newPartName->relpersistence,
   22528                 :             :                                                                                 false,
   22529                 :             :                                                                                 false,
   22530                 :             :                                                                                 ONCOMMIT_NOOP,
   22531                 :             :                                                                                 (Datum) 0,
   22532                 :             :                                                                                 true,
   22533                 :          96 :                                                                                 allowSystemTableMods,
   22534                 :             :                                                                                 true,
   22535                 :             :                                                                                 InvalidOid,
   22536                 :             :                                                                                 NULL);
   22537                 :             : 
   22538                 :             :         /*
   22539                 :             :          * We must bump the command counter to make the newly-created relation
   22540                 :             :          * tuple visible for opening.
   22541                 :             :          */
   22542                 :          96 :         CommandCounterIncrement();
   22543                 :             : 
   22544                 :             :         /*
   22545                 :             :          * Open the new partition with no lock, because we already have an
   22546                 :             :          * AccessExclusiveLock placed there after creation.
   22547                 :             :          */
   22548                 :          96 :         newRel = table_open(newRelId, NoLock);
   22549                 :             : 
   22550                 :             :         /* Find or create a work queue entry for the newly created table. */
   22551                 :          96 :         new_partrel_tab = ATGetQueueEntry(wqueue, newRel);
   22552                 :             : 
   22553                 :             :         /* Create constraints, default values, and generated values. */
   22554                 :          96 :         createTableConstraints(wqueue, new_partrel_tab, parent_rel, newRel);
   22555                 :             : 
   22556                 :             :         /*
   22557                 :             :          * Need to call CommandCounterIncrement, so a fresh relcache entry has
   22558                 :             :          * newly installed constraint info.
   22559                 :             :          */
   22560                 :          96 :         CommandCounterIncrement();
   22561                 :             : 
   22562                 :         192 :         return newRel;
   22563                 :          96 : }
   22564                 :             : 
   22565                 :             : /*
   22566                 :             :  * MergePartitionsMoveRows: scan partitions to be merged (mergingPartitions)
   22567                 :             :  * of the partitioned table and move rows into the new partition
   22568                 :             :  * (newPartRel). We also verify check constraints against these rows.
   22569                 :             :  */
   22570                 :             : static void
   22571                 :          19 : MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
   22572                 :             : {
   22573                 :          19 :         CommandId       mycid;
   22574                 :          19 :         EState     *estate;
   22575                 :          19 :         AlteredTableInfo *tab;
   22576                 :          19 :         ListCell   *ltab;
   22577                 :             : 
   22578                 :             :         /* The FSM is empty, so don't bother using it. */
   22579                 :          19 :         int                     ti_options = TABLE_INSERT_SKIP_FSM;
   22580                 :          19 :         BulkInsertState bistate;        /* state of bulk inserts for partition */
   22581                 :          19 :         TupleTableSlot *dstslot;
   22582                 :             : 
   22583                 :             :         /* Find the work queue entry for the new partition table: newPartRel. */
   22584                 :          19 :         tab = ATGetQueueEntry(wqueue, newPartRel);
   22585                 :             : 
   22586                 :             :         /* Generate the constraint and default execution states. */
   22587                 :          19 :         estate = CreateExecutorState();
   22588                 :             : 
   22589                 :          19 :         buildExpressionExecutionStates(tab, newPartRel, estate);
   22590                 :             : 
   22591                 :          19 :         mycid = GetCurrentCommandId(true);
   22592                 :             : 
   22593                 :             :         /* Prepare a BulkInsertState for table_tuple_insert. */
   22594                 :          19 :         bistate = GetBulkInsertState();
   22595                 :             : 
   22596                 :             :         /* Create the necessary tuple slot. */
   22597                 :          19 :         dstslot = table_slot_create(newPartRel, NULL);
   22598                 :             : 
   22599   [ +  +  +  -  :          83 :         foreach_oid(merging_oid, mergingPartitions)
             +  +  +  + ]
   22600                 :             :         {
   22601                 :          45 :                 ExprContext *econtext;
   22602                 :          45 :                 TupleTableSlot *srcslot;
   22603                 :          45 :                 TupleConversionMap *tuple_map;
   22604                 :          45 :                 TableScanDesc scan;
   22605                 :          45 :                 MemoryContext oldCxt;
   22606                 :          45 :                 Snapshot        snapshot;
   22607                 :          45 :                 Relation        mergingPartition;
   22608                 :             : 
   22609         [ +  + ]:          45 :                 econtext = GetPerTupleExprContext(estate);
   22610                 :             : 
   22611                 :             :                 /*
   22612                 :             :                  * Partition is already locked in the transformPartitionCmdForMerge
   22613                 :             :                  * function.
   22614                 :             :                  */
   22615                 :          45 :                 mergingPartition = table_open(merging_oid, NoLock);
   22616                 :             : 
   22617                 :             :                 /* Create a source tuple slot for the partition being merged. */
   22618                 :          45 :                 srcslot = table_slot_create(mergingPartition, NULL);
   22619                 :             : 
   22620                 :             :                 /*
   22621                 :             :                  * Map computing for moving attributes of the merged partition to the
   22622                 :             :                  * new partition.
   22623                 :             :                  */
   22624                 :          90 :                 tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
   22625                 :          45 :                                                                                    RelationGetDescr(newPartRel));
   22626                 :             : 
   22627                 :             :                 /* Scan through the rows. */
   22628                 :          45 :                 snapshot = RegisterSnapshot(GetLatestSnapshot());
   22629                 :          45 :                 scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
   22630                 :             : 
   22631                 :             :                 /*
   22632                 :             :                  * Switch to per-tuple memory context and reset it for each tuple
   22633                 :             :                  * produced, so we don't leak memory.
   22634                 :             :                  */
   22635         [ +  - ]:          45 :                 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
   22636                 :             : 
   22637         [ +  + ]:          97 :                 while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
   22638                 :             :                 {
   22639                 :          52 :                         TupleTableSlot *insertslot;
   22640                 :             : 
   22641         [ +  - ]:          52 :                         CHECK_FOR_INTERRUPTS();
   22642                 :             : 
   22643         [ +  + ]:          52 :                         if (tuple_map)
   22644                 :             :                         {
   22645                 :             :                                 /* Need to use a map to copy attributes. */
   22646                 :           7 :                                 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
   22647                 :           7 :                         }
   22648                 :             :                         else
   22649                 :             :                         {
   22650                 :          45 :                                 slot_getallattrs(srcslot);
   22651                 :             : 
   22652                 :             :                                 /* Copy attributes directly. */
   22653                 :          45 :                                 insertslot = dstslot;
   22654                 :             : 
   22655                 :          45 :                                 ExecClearTuple(insertslot);
   22656                 :             : 
   22657                 :          45 :                                 memcpy(insertslot->tts_values, srcslot->tts_values,
   22658                 :             :                                            sizeof(Datum) * srcslot->tts_nvalid);
   22659                 :          45 :                                 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
   22660                 :             :                                            sizeof(bool) * srcslot->tts_nvalid);
   22661                 :             : 
   22662                 :          45 :                                 ExecStoreVirtualTuple(insertslot);
   22663                 :             :                         }
   22664                 :             : 
   22665                 :             :                         /*
   22666                 :             :                          * Constraints and GENERATED expressions might reference the
   22667                 :             :                          * tableoid column, so fill tts_tableOid with the desired value.
   22668                 :             :                          * (We must do this each time, because it gets overwritten with
   22669                 :             :                          * newrel's OID during storing.)
   22670                 :             :                          */
   22671                 :          52 :                         insertslot->tts_tableOid = RelationGetRelid(newPartRel);
   22672                 :             : 
   22673                 :             :                         /*
   22674                 :             :                          * Now, evaluate any generated expressions whose inputs come from
   22675                 :             :                          * the new tuple.  We assume these columns won't reference each
   22676                 :             :                          * other, so that there's no ordering dependency.
   22677                 :             :                          */
   22678                 :         104 :                         evaluateGeneratedExpressionsAndCheckConstraints(tab, newPartRel,
   22679                 :          52 :                                                                                                                         insertslot, econtext);
   22680                 :             : 
   22681                 :             :                         /* Write the tuple out to the new relation. */
   22682                 :         104 :                         table_tuple_insert(newPartRel, insertslot, mycid,
   22683                 :          52 :                                                            ti_options, bistate);
   22684                 :             : 
   22685                 :          52 :                         ResetExprContext(econtext);
   22686                 :          52 :                 }
   22687                 :             : 
   22688                 :          45 :                 MemoryContextSwitchTo(oldCxt);
   22689                 :          45 :                 table_endscan(scan);
   22690                 :          45 :                 UnregisterSnapshot(snapshot);
   22691                 :             : 
   22692         [ +  + ]:          45 :                 if (tuple_map)
   22693                 :           5 :                         free_conversion_map(tuple_map);
   22694                 :             : 
   22695                 :          45 :                 ExecDropSingleTupleTableSlot(srcslot);
   22696                 :          45 :                 table_close(mergingPartition, NoLock);
   22697                 :          64 :         }
   22698                 :             : 
   22699                 :          19 :         FreeExecutorState(estate);
   22700                 :          19 :         ExecDropSingleTupleTableSlot(dstslot);
   22701                 :          19 :         FreeBulkInsertState(bistate);
   22702                 :             : 
   22703                 :          19 :         table_finish_bulk_insert(newPartRel, ti_options);
   22704                 :             : 
   22705                 :             :         /*
   22706                 :             :          * We don't need to process this newPartRel since we already processed it
   22707                 :             :          * here, so delete the ALTER TABLE queue for it.
   22708                 :             :          */
   22709   [ +  -  -  +  :          57 :         foreach(ltab, *wqueue)
                   +  - ]
   22710                 :             :         {
   22711                 :          38 :                 tab = (AlteredTableInfo *) lfirst(ltab);
   22712         [ +  + ]:          38 :                 if (tab->relid == RelationGetRelid(newPartRel))
   22713                 :             :                 {
   22714                 :          19 :                         *wqueue = list_delete_cell(*wqueue, ltab);
   22715                 :          19 :                         break;
   22716                 :             :                 }
   22717                 :          19 :         }
   22718                 :          19 : }
   22719                 :             : 
   22720                 :             : /*
   22721                 :             :  * detachPartitionTable: detach partition "child_rel" from partitioned table
   22722                 :             :  * "parent_rel" with default partition identifier "defaultPartOid"
   22723                 :             :  */
   22724                 :             : static void
   22725                 :          83 : detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
   22726                 :             : {
   22727                 :             :         /* Remove the pg_inherits row first. */
   22728                 :          83 :         RemoveInheritance(child_rel, parent_rel, false);
   22729                 :             : 
   22730                 :             :         /*
   22731                 :             :          * Detaching the partition might involve TOAST table access, so ensure we
   22732                 :             :          * have a valid snapshot.
   22733                 :             :          */
   22734                 :          83 :         PushActiveSnapshot(GetTransactionSnapshot());
   22735                 :             : 
   22736                 :             :         /* Do the final part of detaching. */
   22737                 :          83 :         DetachPartitionFinalize(parent_rel, child_rel, false, defaultPartOid);
   22738                 :             : 
   22739                 :          83 :         PopActiveSnapshot();
   22740                 :          83 : }
   22741                 :             : 
   22742                 :             : /*
   22743                 :             :  * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
   22744                 :             :  */
   22745                 :             : static void
   22746                 :          21 : ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
   22747                 :             :                                           PartitionCmd *cmd, AlterTableUtilityContext *context)
   22748                 :             : {
   22749                 :          21 :         Relation        newPartRel;
   22750                 :          21 :         List       *mergingPartitions = NIL;
   22751                 :          21 :         Oid                     defaultPartOid;
   22752                 :          21 :         Oid                     existingRelid;
   22753                 :          21 :         Oid                     ownerId = InvalidOid;
   22754                 :          21 :         Oid                     save_userid;
   22755                 :          21 :         int                     save_sec_context;
   22756                 :          21 :         int                     save_nestlevel;
   22757                 :             : 
   22758                 :             :         /*
   22759                 :             :          * Check ownership of merged partitions - partitions with different owners
   22760                 :             :          * cannot be merged. Also, collect the OIDs of these partitions during the
   22761                 :             :          * check.
   22762                 :             :          */
   22763   [ +  +  +  -  :         105 :         foreach_node(RangeVar, name, cmd->partlist)
             +  +  +  + ]
   22764                 :             :         {
   22765                 :          60 :                 Relation        mergingPartition;
   22766                 :             : 
   22767                 :             :                 /*
   22768                 :             :                  * We are going to detach and remove this partition.  We already took
   22769                 :             :                  * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
   22770                 :             :                  * NoLock is fine.
   22771                 :             :                  */
   22772                 :          60 :                 mergingPartition = table_openrv_extended(name, NoLock, false);
   22773         [ +  - ]:          60 :                 Assert(CheckRelationLockedByMe(mergingPartition, AccessExclusiveLock, false));
   22774                 :             : 
   22775         [ +  + ]:          60 :                 if (OidIsValid(ownerId))
   22776                 :             :                 {
   22777                 :             :                         /* Do the partitions being merged have different owners? */
   22778         [ +  + ]:          34 :                         if (ownerId != mergingPartition->rd_rel->relowner)
   22779   [ +  -  +  - ]:           1 :                                 ereport(ERROR,
   22780                 :             :                                                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   22781                 :             :                                                 errmsg("partitions being merged have different owners"));
   22782                 :          33 :                 }
   22783                 :             :                 else
   22784                 :          26 :                         ownerId = mergingPartition->rd_rel->relowner;
   22785                 :             : 
   22786                 :             :                 /* Store the next merging partition into the list. */
   22787                 :         118 :                 mergingPartitions = lappend_oid(mergingPartitions,
   22788                 :          59 :                                                                                 RelationGetRelid(mergingPartition));
   22789                 :             : 
   22790                 :          59 :                 table_close(mergingPartition, NoLock);
   22791                 :          84 :         }
   22792                 :             : 
   22793                 :             :         /* Look up the existing relation by the new partition name. */
   22794                 :          20 :         RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, &existingRelid);
   22795                 :             : 
   22796                 :             :         /*
   22797                 :             :          * Check if this name is already taken.  This helps us to detect the
   22798                 :             :          * situation when one of the merging partitions has the same name as the
   22799                 :             :          * new partition.  Otherwise, this would fail later on anyway, but
   22800                 :             :          * catching this here allows us to emit a nicer error message.
   22801                 :             :          */
   22802         [ +  + ]:          20 :         if (OidIsValid(existingRelid))
   22803                 :             :         {
   22804         [ +  + ]:           4 :                 if (list_member_oid(mergingPartitions, existingRelid))
   22805                 :             :                 {
   22806                 :             :                         /*
   22807                 :             :                          * The new partition has the same name as one of the merging
   22808                 :             :                          * partitions.
   22809                 :             :                          */
   22810                 :           3 :                         char            tmpRelName[NAMEDATALEN];
   22811                 :             : 
   22812                 :             :                         /* Generate a temporary name. */
   22813                 :           3 :                         sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
   22814                 :             : 
   22815                 :             :                         /*
   22816                 :             :                          * Rename the existing partition with a temporary name, leaving it
   22817                 :             :                          * free for the new partition.  We don't need to care about this
   22818                 :             :                          * in the future because we're going to eventually drop the
   22819                 :             :                          * existing partition anyway.
   22820                 :             :                          */
   22821                 :           3 :                         RenameRelationInternal(existingRelid, tmpRelName, true, false);
   22822                 :             : 
   22823                 :             :                         /*
   22824                 :             :                          * We must bump the command counter to make the new partition
   22825                 :             :                          * tuple visible for rename.
   22826                 :             :                          */
   22827                 :           3 :                         CommandCounterIncrement();
   22828                 :           3 :                 }
   22829                 :             :                 else
   22830                 :             :                 {
   22831   [ +  -  +  - ]:           1 :                         ereport(ERROR,
   22832                 :             :                                         errcode(ERRCODE_DUPLICATE_TABLE),
   22833                 :             :                                         errmsg("relation \"%s\" already exists", cmd->name->relname));
   22834                 :             :                 }
   22835                 :           3 :         }
   22836                 :             : 
   22837                 :          19 :         defaultPartOid =
   22838                 :          19 :                 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   22839                 :             : 
   22840                 :             :         /* Detach all merging partitions. */
   22841   [ +  +  +  -  :          98 :         foreach_oid(mergingPartitionOid, mergingPartitions)
             +  +  +  + ]
   22842                 :             :         {
   22843                 :          55 :                 Relation        child_rel;
   22844                 :             : 
   22845                 :          55 :                 child_rel = table_open(mergingPartitionOid, NoLock);
   22846                 :             : 
   22847                 :          55 :                 detachPartitionTable(rel, child_rel, defaultPartOid);
   22848                 :             : 
   22849                 :          55 :                 table_close(child_rel, NoLock);
   22850                 :          79 :         }
   22851                 :             : 
   22852                 :             :         /*
   22853                 :             :          * Perform a preliminary check to determine whether it's safe to drop all
   22854                 :             :          * merging partitions before we actually do so later. After merging rows
   22855                 :             :          * into the new partitions via MergePartitionsMoveRows, all old partitions
   22856                 :             :          * need to be dropped. However, since the drop behavior is DROP_RESTRICT
   22857                 :             :          * and the merge process (MergePartitionsMoveRows) can be time-consuming,
   22858                 :             :          * performing an early check on the drop eligibility of old partitions is
   22859                 :             :          * preferable.
   22860                 :             :          */
   22861   [ +  +  +  -  :          96 :         foreach_oid(mergingPartitionOid, mergingPartitions)
             +  +  +  + ]
   22862                 :             :         {
   22863                 :          53 :                 ObjectAddress object;
   22864                 :             : 
   22865                 :             :                 /* Get oid of the later to be dropped relation. */
   22866                 :          53 :                 object.objectId = mergingPartitionOid;
   22867                 :          53 :                 object.classId = RelationRelationId;
   22868                 :          53 :                 object.objectSubId = 0;
   22869                 :             : 
   22870                 :          53 :                 performDeletionCheck(&object, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   22871                 :          77 :         }
   22872                 :             : 
   22873                 :             :         /*
   22874                 :             :          * Create a table for the new partition, using the partitioned table as a
   22875                 :             :          * model.
   22876                 :             :          */
   22877         [ +  - ]:          19 :         Assert(OidIsValid(ownerId));
   22878                 :          19 :         newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
   22879                 :             : 
   22880                 :             :         /*
   22881                 :             :          * Switch to the table owner's userid, so that any index functions are run
   22882                 :             :          * as that user.  Also, lockdown security-restricted operations and
   22883                 :             :          * arrange to make GUC variable changes local to this command.
   22884                 :             :          *
   22885                 :             :          * Need to do it after determining the namespace in the
   22886                 :             :          * createPartitionTable() call.
   22887                 :             :          */
   22888                 :          19 :         GetUserIdAndSecContext(&save_userid, &save_sec_context);
   22889                 :          38 :         SetUserIdAndSecContext(ownerId,
   22890                 :          19 :                                                    save_sec_context | SECURITY_RESTRICTED_OPERATION);
   22891                 :          19 :         save_nestlevel = NewGUCNestLevel();
   22892                 :          19 :         RestrictSearchPath();
   22893                 :             : 
   22894                 :             :         /* Copy data from merged partitions to the new partition. */
   22895                 :          19 :         MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel);
   22896                 :             : 
   22897                 :             :         /* Drop the current partitions before attaching the new one. */
   22898   [ +  +  +  -  :          83 :         foreach_oid(mergingPartitionOid, mergingPartitions)
             +  +  +  + ]
   22899                 :             :         {
   22900                 :          45 :                 ObjectAddress object;
   22901                 :             : 
   22902                 :          45 :                 object.objectId = mergingPartitionOid;
   22903                 :          45 :                 object.classId = RelationRelationId;
   22904                 :          45 :                 object.objectSubId = 0;
   22905                 :             : 
   22906                 :          45 :                 performDeletion(&object, DROP_RESTRICT, 0);
   22907                 :          64 :         }
   22908                 :             : 
   22909                 :          19 :         list_free(mergingPartitions);
   22910                 :             : 
   22911                 :             :         /*
   22912                 :             :          * Attach a new partition to the partitioned table. wqueue = NULL:
   22913                 :             :          * verification for each cloned constraint is not needed.
   22914                 :             :          */
   22915                 :          19 :         attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
   22916                 :             : 
   22917                 :             :         /* Keep the lock until commit. */
   22918                 :          19 :         table_close(newPartRel, NoLock);
   22919                 :             : 
   22920                 :             :         /* Roll back any GUC changes executed by index functions. */
   22921                 :          19 :         AtEOXact_GUC(false, save_nestlevel);
   22922                 :             : 
   22923                 :             :         /* Restore the userid and security context. */
   22924                 :          19 :         SetUserIdAndSecContext(save_userid, save_sec_context);
   22925                 :          19 : }
   22926                 :             : 
   22927                 :             : /*
   22928                 :             :  * Struct with the context of the new partition for inserting rows from the
   22929                 :             :  * split partition.
   22930                 :             :  */
   22931                 :             : typedef struct SplitPartitionContext
   22932                 :             : {
   22933                 :             :         ExprState  *partqualstate;      /* expression for checking a slot for a
   22934                 :             :                                                                  * partition (NULL for DEFAULT partition) */
   22935                 :             :         BulkInsertState bistate;        /* state of bulk inserts for partition */
   22936                 :             :         TupleTableSlot *dstslot;        /* slot for inserting row into partition */
   22937                 :             :         AlteredTableInfo *tab;          /* structure with generated column expressions
   22938                 :             :                                                                  * and check constraint expressions. */
   22939                 :             :         Relation        partRel;                /* relation for partition */
   22940                 :             : } SplitPartitionContext;
   22941                 :             : 
   22942                 :             : /*
   22943                 :             :  * createSplitPartitionContext: create context for partition and fill it
   22944                 :             :  */
   22945                 :             : static SplitPartitionContext *
   22946                 :          77 : createSplitPartitionContext(Relation partRel)
   22947                 :             : {
   22948                 :          77 :         SplitPartitionContext *pc;
   22949                 :             : 
   22950                 :          77 :         pc = palloc0_object(SplitPartitionContext);
   22951                 :          77 :         pc->partRel = partRel;
   22952                 :             : 
   22953                 :             :         /*
   22954                 :             :          * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
   22955                 :             :          * don't bother using it.
   22956                 :             :          */
   22957                 :          77 :         pc->bistate = GetBulkInsertState();
   22958                 :             : 
   22959                 :             :         /* Create a destination tuple slot for the new partition. */
   22960                 :          77 :         pc->dstslot = table_slot_create(pc->partRel, NULL);
   22961                 :             : 
   22962                 :         154 :         return pc;
   22963                 :          77 : }
   22964                 :             : 
   22965                 :             : /*
   22966                 :             :  * deleteSplitPartitionContext: delete context for partition
   22967                 :             :  */
   22968                 :             : static void
   22969                 :          77 : deleteSplitPartitionContext(SplitPartitionContext *pc, List **wqueue, int ti_options)
   22970                 :             : {
   22971                 :          77 :         ListCell   *ltab;
   22972                 :             : 
   22973                 :          77 :         ExecDropSingleTupleTableSlot(pc->dstslot);
   22974                 :          77 :         FreeBulkInsertState(pc->bistate);
   22975                 :             : 
   22976                 :          77 :         table_finish_bulk_insert(pc->partRel, ti_options);
   22977                 :             : 
   22978                 :             :         /*
   22979                 :             :          * We don't need to process this pc->partRel so delete the ALTER TABLE
   22980                 :             :          * queue of it.
   22981                 :             :          */
   22982   [ +  -  -  +  :         231 :         foreach(ltab, *wqueue)
                   +  - ]
   22983                 :             :         {
   22984                 :         154 :                 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
   22985                 :             : 
   22986         [ +  + ]:         154 :                 if (tab->relid == RelationGetRelid(pc->partRel))
   22987                 :             :                 {
   22988                 :          77 :                         *wqueue = list_delete_cell(*wqueue, ltab);
   22989                 :          77 :                         break;
   22990                 :             :                 }
   22991         [ +  + ]:         154 :         }
   22992                 :             : 
   22993                 :          77 :         pfree(pc);
   22994                 :          77 : }
   22995                 :             : 
   22996                 :             : /*
   22997                 :             :  * SplitPartitionMoveRows: scan split partition (splitRel) of partitioned table
   22998                 :             :  * (rel) and move rows into new partitions.
   22999                 :             :  *
   23000                 :             :  * New partitions description:
   23001                 :             :  * partlist: list of pointers to SinglePartitionSpec structures.  It contains
   23002                 :             :  * the partition specification details for all new partitions.
   23003                 :             :  * newPartRels: list of Relations, new partitions created in
   23004                 :             :  * ATExecSplitPartition.
   23005                 :             :  */
   23006                 :             : static void
   23007                 :          27 : SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel,
   23008                 :             :                                            List *partlist, List *newPartRels)
   23009                 :             : {
   23010                 :             :         /* The FSM is empty, so don't bother using it. */
   23011                 :          27 :         int                     ti_options = TABLE_INSERT_SKIP_FSM;
   23012                 :          27 :         CommandId       mycid;
   23013                 :          27 :         EState     *estate;
   23014                 :          27 :         ListCell   *listptr,
   23015                 :             :                            *listptr2;
   23016                 :          27 :         TupleTableSlot *srcslot;
   23017                 :          27 :         ExprContext *econtext;
   23018                 :          27 :         TableScanDesc scan;
   23019                 :          27 :         Snapshot        snapshot;
   23020                 :          27 :         MemoryContext oldCxt;
   23021                 :          27 :         List       *partContexts = NIL;
   23022                 :          27 :         TupleConversionMap *tuple_map;
   23023                 :          27 :         SplitPartitionContext *defaultPartCtx = NULL,
   23024                 :             :                            *pc;
   23025                 :             : 
   23026                 :          27 :         mycid = GetCurrentCommandId(true);
   23027                 :             : 
   23028                 :          27 :         estate = CreateExecutorState();
   23029                 :             : 
   23030   [ +  -  +  +  :         104 :         forboth(listptr, partlist, listptr2, newPartRels)
          +  -  +  +  +  
                +  +  + ]
   23031                 :             :         {
   23032                 :          77 :                 SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   23033                 :             : 
   23034                 :          77 :                 pc = createSplitPartitionContext((Relation) lfirst(listptr2));
   23035                 :             : 
   23036                 :             :                 /* Find the work queue entry for the new partition table: newPartRel. */
   23037                 :          77 :                 pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
   23038                 :             : 
   23039                 :          77 :                 buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
   23040                 :             : 
   23041         [ +  + ]:          77 :                 if (sps->bound->is_default)
   23042                 :             :                 {
   23043                 :             :                         /*
   23044                 :             :                          * We should not create a structure to check the partition
   23045                 :             :                          * constraint for the new DEFAULT partition.
   23046                 :             :                          */
   23047                 :           7 :                         defaultPartCtx = pc;
   23048                 :           7 :                 }
   23049                 :             :                 else
   23050                 :             :                 {
   23051                 :          70 :                         List       *partConstraint;
   23052                 :             : 
   23053                 :             :                         /* Build expression execution states for partition check quals. */
   23054                 :          70 :                         partConstraint = get_qual_from_partbound(rel, sps->bound);
   23055                 :          70 :                         partConstraint =
   23056                 :          70 :                                 (List *) eval_const_expressions(NULL,
   23057                 :          70 :                                                                                                 (Node *) partConstraint);
   23058                 :             :                         /* Make a boolean expression for ExecCheck(). */
   23059                 :          70 :                         partConstraint = list_make1(make_ands_explicit(partConstraint));
   23060                 :             : 
   23061                 :             :                         /*
   23062                 :             :                          * Map the vars in the constraint expression from rel's attnos to
   23063                 :             :                          * splitRel's.
   23064                 :             :                          */
   23065                 :         140 :                         partConstraint = map_partition_varattnos(partConstraint,
   23066                 :          70 :                                                                                                          1, splitRel, rel);
   23067                 :             : 
   23068                 :          70 :                         pc->partqualstate =
   23069                 :          70 :                                 ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
   23070         [ -  + ]:          70 :                         Assert(pc->partqualstate != NULL);
   23071                 :          70 :                 }
   23072                 :             : 
   23073                 :             :                 /* Store partition context into a list. */
   23074                 :          77 :                 partContexts = lappend(partContexts, pc);
   23075                 :          77 :         }
   23076                 :             : 
   23077         [ -  + ]:          27 :         econtext = GetPerTupleExprContext(estate);
   23078                 :             : 
   23079                 :             :         /* Create the necessary tuple slot. */
   23080                 :          27 :         srcslot = table_slot_create(splitRel, NULL);
   23081                 :             : 
   23082                 :             :         /*
   23083                 :             :          * Map computing for moving attributes of the split partition to the new
   23084                 :             :          * partition (for the first new partition, but other new partitions can
   23085                 :             :          * use the same map).
   23086                 :             :          */
   23087                 :          27 :         pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
   23088                 :          54 :         tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
   23089                 :          27 :                                                                            RelationGetDescr(pc->partRel));
   23090                 :             : 
   23091                 :             :         /* Scan through the rows. */
   23092                 :          27 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
   23093                 :          27 :         scan = table_beginscan(splitRel, snapshot, 0, NULL);
   23094                 :             : 
   23095                 :             :         /*
   23096                 :             :          * Switch to per-tuple memory context and reset it for each tuple
   23097                 :             :          * produced, so we don't leak memory.
   23098                 :             :          */
   23099         [ +  - ]:          27 :         oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
   23100                 :             : 
   23101         [ +  + ]:         130 :         while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
   23102                 :             :         {
   23103                 :         103 :                 bool            found = false;
   23104                 :         103 :                 TupleTableSlot *insertslot;
   23105                 :             : 
   23106         [ +  - ]:         103 :                 CHECK_FOR_INTERRUPTS();
   23107                 :             : 
   23108                 :         103 :                 econtext->ecxt_scantuple = srcslot;
   23109                 :             : 
   23110                 :             :                 /* Search partition for the current slot, srcslot. */
   23111   [ +  -  +  +  :         363 :                 foreach(listptr, partContexts)
                   +  + ]
   23112                 :             :                 {
   23113                 :         260 :                         pc = (SplitPartitionContext *) lfirst(listptr);
   23114                 :             : 
   23115                 :             :                         /* skip DEFAULT partition */
   23116   [ +  +  +  + ]:         260 :                         if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
   23117                 :             :                         {
   23118                 :          84 :                                 found = true;
   23119                 :          84 :                                 break;
   23120                 :             :                         }
   23121                 :         176 :                 }
   23122         [ +  + ]:         103 :                 if (!found)
   23123                 :             :                 {
   23124                 :             :                         /* Use the DEFAULT partition if it exists. */
   23125         [ +  - ]:          19 :                         if (defaultPartCtx)
   23126                 :          19 :                                 pc = defaultPartCtx;
   23127                 :             :                         else
   23128   [ #  #  #  # ]:           0 :                                 ereport(ERROR,
   23129                 :             :                                                 errcode(ERRCODE_CHECK_VIOLATION),
   23130                 :             :                                                 errmsg("can not find partition for split partition row"),
   23131                 :             :                                                 errtable(splitRel));
   23132                 :          19 :                 }
   23133                 :             : 
   23134         [ +  + ]:         103 :                 if (tuple_map)
   23135                 :             :                 {
   23136                 :             :                         /* Need to use a map to copy attributes. */
   23137                 :           4 :                         insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
   23138                 :           4 :                 }
   23139                 :             :                 else
   23140                 :             :                 {
   23141                 :             :                         /* Extract data from the old tuple. */
   23142                 :          99 :                         slot_getallattrs(srcslot);
   23143                 :             : 
   23144                 :             :                         /* Copy attributes directly. */
   23145                 :          99 :                         insertslot = pc->dstslot;
   23146                 :             : 
   23147                 :          99 :                         ExecClearTuple(insertslot);
   23148                 :             : 
   23149                 :          99 :                         memcpy(insertslot->tts_values, srcslot->tts_values,
   23150                 :             :                                    sizeof(Datum) * srcslot->tts_nvalid);
   23151                 :          99 :                         memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
   23152                 :             :                                    sizeof(bool) * srcslot->tts_nvalid);
   23153                 :             : 
   23154                 :          99 :                         ExecStoreVirtualTuple(insertslot);
   23155                 :             :                 }
   23156                 :             : 
   23157                 :             :                 /*
   23158                 :             :                  * Constraints and GENERATED expressions might reference the tableoid
   23159                 :             :                  * column, so fill tts_tableOid with the desired value. (We must do
   23160                 :             :                  * this each time, because it gets overwritten with newrel's OID
   23161                 :             :                  * during storing.)
   23162                 :             :                  */
   23163                 :         103 :                 insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
   23164                 :             : 
   23165                 :             :                 /*
   23166                 :             :                  * Now, evaluate any generated expressions whose inputs come from the
   23167                 :             :                  * new tuple.  We assume these columns won't reference each other, so
   23168                 :             :                  * that there's no ordering dependency.
   23169                 :             :                  */
   23170                 :         206 :                 evaluateGeneratedExpressionsAndCheckConstraints(pc->tab, pc->partRel,
   23171                 :         103 :                                                                                                                 insertslot, econtext);
   23172                 :             : 
   23173                 :             :                 /* Write the tuple out to the new relation. */
   23174                 :         206 :                 table_tuple_insert(pc->partRel, insertslot, mycid,
   23175                 :         103 :                                                    ti_options, pc->bistate);
   23176                 :             : 
   23177                 :         103 :                 ResetExprContext(econtext);
   23178                 :         103 :         }
   23179                 :             : 
   23180                 :          27 :         MemoryContextSwitchTo(oldCxt);
   23181                 :             : 
   23182                 :          27 :         table_endscan(scan);
   23183                 :          27 :         UnregisterSnapshot(snapshot);
   23184                 :             : 
   23185         [ +  + ]:          27 :         if (tuple_map)
   23186                 :           1 :                 free_conversion_map(tuple_map);
   23187                 :             : 
   23188                 :          27 :         ExecDropSingleTupleTableSlot(srcslot);
   23189                 :             : 
   23190                 :          27 :         FreeExecutorState(estate);
   23191                 :             : 
   23192   [ +  +  +  -  :         131 :         foreach_ptr(SplitPartitionContext, spc, partContexts)
             +  +  +  + ]
   23193                 :         104 :                 deleteSplitPartitionContext(spc, wqueue, ti_options);
   23194                 :          27 : }
   23195                 :             : 
   23196                 :             : /*
   23197                 :             :  * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
   23198                 :             :  */
   23199                 :             : static void
   23200                 :          28 : ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
   23201                 :             :                                          PartitionCmd *cmd, AlterTableUtilityContext *context)
   23202                 :             : {
   23203                 :          28 :         Relation        splitRel;
   23204                 :          28 :         Oid                     splitRelOid;
   23205                 :          28 :         ListCell   *listptr,
   23206                 :             :                            *listptr2;
   23207                 :          28 :         bool            isSameName = false;
   23208                 :          28 :         char            tmpRelName[NAMEDATALEN];
   23209                 :          28 :         List       *newPartRels = NIL;
   23210                 :          28 :         ObjectAddress object;
   23211                 :          28 :         Oid                     defaultPartOid;
   23212                 :          28 :         Oid                     save_userid;
   23213                 :          28 :         int                     save_sec_context;
   23214                 :          28 :         int                     save_nestlevel;
   23215                 :             : 
   23216                 :          28 :         defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   23217                 :             : 
   23218                 :             :         /*
   23219                 :             :          * Partition is already locked in the transformPartitionCmdForSplit
   23220                 :             :          * function.
   23221                 :             :          */
   23222                 :          28 :         splitRel = table_openrv(cmd->name, NoLock);
   23223                 :             : 
   23224                 :          28 :         splitRelOid = RelationGetRelid(splitRel);
   23225                 :             : 
   23226                 :             :         /* Check descriptions of new partitions. */
   23227   [ +  +  +  -  :         135 :         foreach_node(SinglePartitionSpec, sps, cmd->partlist)
             +  +  +  + ]
   23228                 :             :         {
   23229                 :          80 :                 Oid                     existingRelid;
   23230                 :             : 
   23231                 :             :                 /* Look up the existing relation by the new partition name. */
   23232                 :          80 :                 RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, &existingRelid);
   23233                 :             : 
   23234                 :             :                 /*
   23235                 :             :                  * This would fail later on anyway if the relation already exists. But
   23236                 :             :                  * by catching it here, we can emit a nicer error message.
   23237                 :             :                  */
   23238   [ +  +  -  + ]:          80 :                 if (existingRelid == splitRelOid && !isSameName)
   23239                 :             :                         /* One new partition can have the same name as a split partition. */
   23240                 :           7 :                         isSameName = true;
   23241         [ +  + ]:          73 :                 else if (OidIsValid(existingRelid))
   23242   [ +  -  +  - ]:           1 :                         ereport(ERROR,
   23243                 :             :                                         errcode(ERRCODE_DUPLICATE_TABLE),
   23244                 :             :                                         errmsg("relation \"%s\" already exists", sps->name->relname));
   23245                 :         107 :         }
   23246                 :             : 
   23247                 :             :         /* Detach the split partition. */
   23248                 :          27 :         detachPartitionTable(rel, splitRel, defaultPartOid);
   23249                 :             : 
   23250                 :             :         /*
   23251                 :             :          * Perform a preliminary check to determine whether it's safe to drop the
   23252                 :             :          * split partition before we actually do so later. After merging rows into
   23253                 :             :          * the new partitions via SplitPartitionMoveRows, all old partitions need
   23254                 :             :          * to be dropped. However, since the drop behavior is DROP_RESTRICT and
   23255                 :             :          * the merge process (SplitPartitionMoveRows) can be time-consuming,
   23256                 :             :          * performing an early check on the drop eligibility of old partitions is
   23257                 :             :          * preferable.
   23258                 :             :          */
   23259                 :          27 :         object.objectId = splitRelOid;
   23260                 :          27 :         object.classId = RelationRelationId;
   23261                 :          27 :         object.objectSubId = 0;
   23262                 :          27 :         performDeletionCheck(&object, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   23263                 :             : 
   23264                 :             :         /*
   23265                 :             :          * If a new partition has the same name as the split partition, then we
   23266                 :             :          * should rename the split partition to reuse its name.
   23267                 :             :          */
   23268         [ +  + ]:          27 :         if (isSameName)
   23269                 :             :         {
   23270                 :             :                 /*
   23271                 :             :                  * We must bump the command counter to make the split partition tuple
   23272                 :             :                  * visible for renaming.
   23273                 :             :                  */
   23274                 :           7 :                 CommandCounterIncrement();
   23275                 :             :                 /* Rename partition. */
   23276                 :           7 :                 sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
   23277                 :           7 :                 RenameRelationInternal(splitRelOid, tmpRelName, true, false);
   23278                 :             : 
   23279                 :             :                 /*
   23280                 :             :                  * We must bump the command counter to make the split partition tuple
   23281                 :             :                  * visible after renaming.
   23282                 :             :                  */
   23283                 :           7 :                 CommandCounterIncrement();
   23284                 :           7 :         }
   23285                 :             : 
   23286                 :             :         /* Create new partitions (like a split partition), without indexes. */
   23287   [ +  +  +  -  :         132 :         foreach_node(SinglePartitionSpec, sps, cmd->partlist)
             +  +  +  + ]
   23288                 :             :         {
   23289                 :          77 :                 Relation        newPartRel;
   23290                 :             : 
   23291                 :         154 :                 newPartRel = createPartitionTable(wqueue, sps->name, rel,
   23292                 :          77 :                                                                                   splitRel->rd_rel->relowner);
   23293                 :          77 :                 newPartRels = lappend(newPartRels, newPartRel);
   23294                 :         105 :         }
   23295                 :             : 
   23296                 :             :         /*
   23297                 :             :          * Switch to the table owner's userid, so that any index functions are run
   23298                 :             :          * as that user.  Also, lockdown security-restricted operations and
   23299                 :             :          * arrange to make GUC variable changes local to this command.
   23300                 :             :          *
   23301                 :             :          * Need to do it after determining the namespace in the
   23302                 :             :          * createPartitionTable() call.
   23303                 :             :          */
   23304                 :          27 :         GetUserIdAndSecContext(&save_userid, &save_sec_context);
   23305                 :          54 :         SetUserIdAndSecContext(splitRel->rd_rel->relowner,
   23306                 :          27 :                                                    save_sec_context | SECURITY_RESTRICTED_OPERATION);
   23307                 :          27 :         save_nestlevel = NewGUCNestLevel();
   23308                 :          27 :         RestrictSearchPath();
   23309                 :             : 
   23310                 :             :         /* Copy data from the split partition to the new partitions. */
   23311                 :          27 :         SplitPartitionMoveRows(wqueue, rel, splitRel, cmd->partlist, newPartRels);
   23312                 :             :         /* Keep the lock until commit. */
   23313                 :          27 :         table_close(splitRel, NoLock);
   23314                 :             : 
   23315                 :             :         /* Attach new partitions to the partitioned table. */
   23316   [ +  -  +  +  :         104 :         forboth(listptr, cmd->partlist, listptr2, newPartRels)
          +  -  +  +  +  
                +  +  + ]
   23317                 :             :         {
   23318                 :          77 :                 SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   23319                 :          77 :                 Relation        newPartRel = (Relation) lfirst(listptr2);
   23320                 :             : 
   23321                 :             :                 /*
   23322                 :             :                  * wqueue = NULL: verification for each cloned constraint is not
   23323                 :             :                  * needed.
   23324                 :             :                  */
   23325                 :          77 :                 attachPartitionTable(NULL, rel, newPartRel, sps->bound);
   23326                 :             :                 /* Keep the lock until commit. */
   23327                 :          77 :                 table_close(newPartRel, NoLock);
   23328                 :          77 :         }
   23329                 :             : 
   23330                 :             :         /* Drop the split partition. */
   23331                 :          27 :         object.classId = RelationRelationId;
   23332                 :          27 :         object.objectId = splitRelOid;
   23333                 :          27 :         object.objectSubId = 0;
   23334                 :             :         /* Probably DROP_CASCADE is not needed. */
   23335                 :          27 :         performDeletion(&object, DROP_RESTRICT, 0);
   23336                 :             : 
   23337                 :             :         /* Roll back any GUC changes executed by index functions. */
   23338                 :          27 :         AtEOXact_GUC(false, save_nestlevel);
   23339                 :             : 
   23340                 :             :         /* Restore the userid and security context. */
   23341                 :          27 :         SetUserIdAndSecContext(save_userid, save_sec_context);
   23342                 :          27 : }
        

Generated by: LCOV version 2.3.2-1