LCOV - code coverage report
Current view: top level - src/backend/commands - tablecmds.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 92.7 % 7481 6937
Test Date: 2026-08-15 09:15:43 Functions: 100.0 % 229 229
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 73.5 % 6081 4469

             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/tupconvert.h"
      29                 :             : #include "access/xact.h"
      30                 :             : #include "access/xlog.h"
      31                 :             : #include "access/xloginsert.h"
      32                 :             : #include "catalog/catalog.h"
      33                 :             : #include "catalog/heap.h"
      34                 :             : #include "catalog/index.h"
      35                 :             : #include "catalog/namespace.h"
      36                 :             : #include "catalog/objectaccess.h"
      37                 :             : #include "catalog/partition.h"
      38                 :             : #include "catalog/pg_am.h"
      39                 :             : #include "catalog/pg_attrdef.h"
      40                 :             : #include "catalog/pg_collation.h"
      41                 :             : #include "catalog/pg_constraint.h"
      42                 :             : #include "catalog/pg_depend.h"
      43                 :             : #include "catalog/pg_extension_d.h"
      44                 :             : #include "catalog/pg_foreign_table.h"
      45                 :             : #include "catalog/pg_inherits.h"
      46                 :             : #include "catalog/pg_largeobject.h"
      47                 :             : #include "catalog/pg_largeobject_metadata.h"
      48                 :             : #include "catalog/pg_namespace.h"
      49                 :             : #include "catalog/pg_opclass.h"
      50                 :             : #include "catalog/pg_policy.h"
      51                 :             : #include "catalog/pg_proc.h"
      52                 :             : #include "catalog/pg_publication_rel.h"
      53                 :             : #include "catalog/pg_rewrite.h"
      54                 :             : #include "catalog/pg_statistic_ext.h"
      55                 :             : #include "catalog/pg_tablespace.h"
      56                 :             : #include "catalog/pg_trigger.h"
      57                 :             : #include "catalog/pg_type.h"
      58                 :             : #include "catalog/storage.h"
      59                 :             : #include "catalog/storage_xlog.h"
      60                 :             : #include "catalog/toasting.h"
      61                 :             : #include "commands/comment.h"
      62                 :             : #include "commands/defrem.h"
      63                 :             : #include "commands/event_trigger.h"
      64                 :             : #include "commands/extension.h"
      65                 :             : #include "commands/repack.h"
      66                 :             : #include "commands/sequence.h"
      67                 :             : #include "commands/tablecmds.h"
      68                 :             : #include "commands/tablespace.h"
      69                 :             : #include "commands/trigger.h"
      70                 :             : #include "commands/typecmds.h"
      71                 :             : #include "commands/user.h"
      72                 :             : #include "commands/vacuum.h"
      73                 :             : #include "common/int.h"
      74                 :             : #include "executor/executor.h"
      75                 :             : #include "foreign/fdwapi.h"
      76                 :             : #include "foreign/foreign.h"
      77                 :             : #include "miscadmin.h"
      78                 :             : #include "nodes/makefuncs.h"
      79                 :             : #include "nodes/nodeFuncs.h"
      80                 :             : #include "nodes/parsenodes.h"
      81                 :             : #include "optimizer/optimizer.h"
      82                 :             : #include "parser/parse_coerce.h"
      83                 :             : #include "parser/parse_collate.h"
      84                 :             : #include "parser/parse_expr.h"
      85                 :             : #include "parser/parse_relation.h"
      86                 :             : #include "parser/parse_type.h"
      87                 :             : #include "parser/parse_utilcmd.h"
      88                 :             : #include "parser/parser.h"
      89                 :             : #include "partitioning/partbounds.h"
      90                 :             : #include "partitioning/partdesc.h"
      91                 :             : #include "pgstat.h"
      92                 :             : #include "rewrite/rewriteDefine.h"
      93                 :             : #include "rewrite/rewriteHandler.h"
      94                 :             : #include "rewrite/rewriteManip.h"
      95                 :             : #include "storage/bufmgr.h"
      96                 :             : #include "storage/lmgr.h"
      97                 :             : #include "storage/lock.h"
      98                 :             : #include "storage/predicate.h"
      99                 :             : #include "storage/smgr.h"
     100                 :             : #include "tcop/utility.h"
     101                 :             : #include "utils/acl.h"
     102                 :             : #include "utils/builtins.h"
     103                 :             : #include "utils/fmgroids.h"
     104                 :             : #include "utils/inval.h"
     105                 :             : #include "utils/lsyscache.h"
     106                 :             : #include "utils/memutils.h"
     107                 :             : #include "utils/partcache.h"
     108                 :             : #include "utils/relcache.h"
     109                 :             : #include "utils/ruleutils.h"
     110                 :             : #include "utils/snapmgr.h"
     111                 :             : #include "utils/syscache.h"
     112                 :             : #include "utils/timestamp.h"
     113                 :             : #include "utils/typcache.h"
     114                 :             : #include "utils/usercontext.h"
     115                 :             : 
     116                 :             : /*
     117                 :             :  * ON COMMIT action list
     118                 :             :  */
     119                 :             : typedef struct OnCommitItem
     120                 :             : {
     121                 :             :     Oid         relid;          /* relid of relation */
     122                 :             :     OnCommitAction oncommit;    /* what to do at end of xact */
     123                 :             : 
     124                 :             :     /*
     125                 :             :      * If this entry was created during the current transaction,
     126                 :             :      * creating_subid is the ID of the creating subxact; if created in a prior
     127                 :             :      * transaction, creating_subid is zero.  If deleted during the current
     128                 :             :      * transaction, deleting_subid is the ID of the deleting subxact; if no
     129                 :             :      * deletion request is pending, deleting_subid is zero.
     130                 :             :      */
     131                 :             :     SubTransactionId creating_subid;
     132                 :             :     SubTransactionId deleting_subid;
     133                 :             : } OnCommitItem;
     134                 :             : 
     135                 :             : static List *on_commits = NIL;
     136                 :             : 
     137                 :             : 
     138                 :             : /*
     139                 :             :  * State information for ALTER TABLE
     140                 :             :  *
     141                 :             :  * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
     142                 :             :  * structs, one for each table modified by the operation (the named table
     143                 :             :  * plus any child tables that are affected).  We save lists of subcommands
     144                 :             :  * to apply to this table (possibly modified by parse transformation steps);
     145                 :             :  * these lists will be executed in Phase 2.  If a Phase 3 step is needed,
     146                 :             :  * necessary information is stored in the constraints and newvals lists.
     147                 :             :  *
     148                 :             :  * Phase 2 is divided into multiple passes; subcommands are executed in
     149                 :             :  * a pass determined by subcommand type.
     150                 :             :  */
     151                 :             : 
     152                 :             : typedef enum AlterTablePass
     153                 :             : {
     154                 :             :     AT_PASS_UNSET = -1,         /* UNSET will cause ERROR */
     155                 :             :     AT_PASS_DROP,               /* DROP (all flavors) */
     156                 :             :     AT_PASS_ALTER_TYPE,         /* ALTER COLUMN TYPE */
     157                 :             :     AT_PASS_ADD_COL,            /* ADD COLUMN */
     158                 :             :     AT_PASS_SET_EXPRESSION,     /* ALTER SET EXPRESSION */
     159                 :             :     AT_PASS_OLD_INDEX,          /* re-add existing indexes */
     160                 :             :     AT_PASS_OLD_CONSTR,         /* re-add existing constraints */
     161                 :             :     /* We could support a RENAME COLUMN pass here, but not currently used */
     162                 :             :     AT_PASS_ADD_CONSTR,         /* ADD constraints (initial examination) */
     163                 :             :     AT_PASS_COL_ATTRS,          /* set column attributes, eg NOT NULL */
     164                 :             :     AT_PASS_ADD_INDEXCONSTR,    /* ADD index-based constraints */
     165                 :             :     AT_PASS_ADD_INDEX,          /* ADD indexes */
     166                 :             :     AT_PASS_ADD_OTHERCONSTR,    /* ADD other constraints, defaults */
     167                 :             :     AT_PASS_MISC,               /* other stuff */
     168                 :             : } AlterTablePass;
     169                 :             : 
     170                 :             : #define AT_NUM_PASSES           (AT_PASS_MISC + 1)
     171                 :             : 
     172                 :             : typedef struct AlteredTableInfo
     173                 :             : {
     174                 :             :     /* Information saved before any work commences: */
     175                 :             :     Oid         relid;          /* Relation to work on */
     176                 :             :     char        relkind;        /* Its relkind */
     177                 :             :     TupleDesc   oldDesc;        /* Pre-modification tuple descriptor */
     178                 :             : 
     179                 :             :     /*
     180                 :             :      * Transiently set during Phase 2, normally set to NULL.
     181                 :             :      *
     182                 :             :      * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
     183                 :             :      * returns control.  This can be exploited by ATExecCmd subroutines to
     184                 :             :      * close/reopen across transaction boundaries.
     185                 :             :      */
     186                 :             :     Relation    rel;
     187                 :             : 
     188                 :             :     /* Information saved by Phase 1 for Phase 2: */
     189                 :             :     List       *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
     190                 :             :     /* Information saved by Phases 1/2 for Phase 3: */
     191                 :             :     List       *constraints;    /* List of NewConstraint */
     192                 :             :     List       *newvals;        /* List of NewColumnValue */
     193                 :             :     List       *afterStmts;     /* List of utility command parsetrees */
     194                 :             :     bool        verify_new_notnull; /* T if we should recheck NOT NULL */
     195                 :             :     int         rewrite;        /* Reason for forced rewrite, if any */
     196                 :             :     bool        chgAccessMethod;    /* T if SET ACCESS METHOD is used */
     197                 :             :     Oid         newAccessMethod;    /* new access method; 0 means no change,
     198                 :             :                                      * if above is true */
     199                 :             :     Oid         newTableSpace;  /* new tablespace; 0 means no change */
     200                 :             :     bool        chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
     201                 :             :     char        newrelpersistence;  /* if above is true */
     202                 :             :     Expr       *partition_constraint;   /* for attach partition validation */
     203                 :             :     /* true, if validating default due to some other attach/detach */
     204                 :             :     bool        validate_default;
     205                 :             :     /* Objects to rebuild after completing ALTER TYPE operations */
     206                 :             :     List       *changedConstraintOids;  /* OIDs of constraints to rebuild */
     207                 :             :     List       *changedConstraintDefs;  /* string definitions of same */
     208                 :             :     List       *changedIndexOids;   /* OIDs of indexes to rebuild */
     209                 :             :     List       *changedIndexDefs;   /* string definitions of same */
     210                 :             :     char       *replicaIdentityIndex;   /* index to reset as REPLICA IDENTITY */
     211                 :             :     char       *clusterOnIndex; /* index to use for CLUSTER */
     212                 :             :     List       *changedStatisticsOids;  /* OIDs of statistics to rebuild */
     213                 :             :     List       *changedStatisticsDefs;  /* string definitions of same */
     214                 :             :     List       *changedStatisticsOwners;    /* owners of same */
     215                 :             : } AlteredTableInfo;
     216                 :             : 
     217                 :             : /* Struct describing one new constraint to check in Phase 3 scan */
     218                 :             : /* Note: new not-null constraints are handled elsewhere */
     219                 :             : typedef struct NewConstraint
     220                 :             : {
     221                 :             :     char       *name;           /* Constraint name, or NULL if none */
     222                 :             :     ConstrType  contype;        /* CHECK or FOREIGN */
     223                 :             :     Oid         refrelid;       /* PK rel, if FOREIGN */
     224                 :             :     Oid         refindid;       /* OID of PK's index, if FOREIGN */
     225                 :             :     bool        conwithperiod;  /* Whether the new FOREIGN KEY uses PERIOD */
     226                 :             :     Oid         conid;          /* OID of pg_constraint entry, if FOREIGN */
     227                 :             :     Node       *qual;           /* Check expr or CONSTR_FOREIGN Constraint */
     228                 :             :     ExprState  *qualstate;      /* Execution state for CHECK expr */
     229                 :             : } NewConstraint;
     230                 :             : 
     231                 :             : /*
     232                 :             :  * Struct describing one new column value that needs to be computed during
     233                 :             :  * Phase 3 copy (this could be either a new column with a non-null default, or
     234                 :             :  * a column that we're changing the type of).  Columns without such an entry
     235                 :             :  * are just copied from the old table during ATRewriteTable.  Note that the
     236                 :             :  * expr is an expression over *old* table values, except when is_generated
     237                 :             :  * is true; then it is an expression over columns of the *new* tuple.
     238                 :             :  */
     239                 :             : typedef struct NewColumnValue
     240                 :             : {
     241                 :             :     AttrNumber  attnum;         /* which column */
     242                 :             :     Expr       *expr;           /* expression to compute */
     243                 :             :     ExprState  *exprstate;      /* execution state */
     244                 :             :     bool        is_generated;   /* is it a GENERATED expression? */
     245                 :             : } NewColumnValue;
     246                 :             : 
     247                 :             : /*
     248                 :             :  * Error-reporting support for RemoveRelations
     249                 :             :  */
     250                 :             : struct dropmsgstrings
     251                 :             : {
     252                 :             :     char        kind;
     253                 :             :     int         nonexistent_code;
     254                 :             :     const char *nonexistent_msg;
     255                 :             :     const char *skipping_msg;
     256                 :             :     const char *nota_msg;
     257                 :             :     const char *drophint_msg;
     258                 :             : };
     259                 :             : 
     260                 :             : static const struct dropmsgstrings dropmsgstringarray[] = {
     261                 :             :     {RELKIND_RELATION,
     262                 :             :         ERRCODE_UNDEFINED_TABLE,
     263                 :             :         gettext_noop("table \"%s\" does not exist"),
     264                 :             :         gettext_noop("table \"%s\" does not exist, skipping"),
     265                 :             :         gettext_noop("\"%s\" is not a table"),
     266                 :             :     gettext_noop("Use DROP TABLE to remove a table.")},
     267                 :             :     {RELKIND_SEQUENCE,
     268                 :             :         ERRCODE_UNDEFINED_TABLE,
     269                 :             :         gettext_noop("sequence \"%s\" does not exist"),
     270                 :             :         gettext_noop("sequence \"%s\" does not exist, skipping"),
     271                 :             :         gettext_noop("\"%s\" is not a sequence"),
     272                 :             :     gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
     273                 :             :     {RELKIND_VIEW,
     274                 :             :         ERRCODE_UNDEFINED_TABLE,
     275                 :             :         gettext_noop("view \"%s\" does not exist"),
     276                 :             :         gettext_noop("view \"%s\" does not exist, skipping"),
     277                 :             :         gettext_noop("\"%s\" is not a view"),
     278                 :             :     gettext_noop("Use DROP VIEW to remove a view.")},
     279                 :             :     {RELKIND_MATVIEW,
     280                 :             :         ERRCODE_UNDEFINED_TABLE,
     281                 :             :         gettext_noop("materialized view \"%s\" does not exist"),
     282                 :             :         gettext_noop("materialized view \"%s\" does not exist, skipping"),
     283                 :             :         gettext_noop("\"%s\" is not a materialized view"),
     284                 :             :     gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
     285                 :             :     {RELKIND_INDEX,
     286                 :             :         ERRCODE_UNDEFINED_OBJECT,
     287                 :             :         gettext_noop("index \"%s\" does not exist"),
     288                 :             :         gettext_noop("index \"%s\" does not exist, skipping"),
     289                 :             :         gettext_noop("\"%s\" is not an index"),
     290                 :             :     gettext_noop("Use DROP INDEX to remove an index.")},
     291                 :             :     {RELKIND_COMPOSITE_TYPE,
     292                 :             :         ERRCODE_UNDEFINED_OBJECT,
     293                 :             :         gettext_noop("type \"%s\" does not exist"),
     294                 :             :         gettext_noop("type \"%s\" does not exist, skipping"),
     295                 :             :         gettext_noop("\"%s\" is not a type"),
     296                 :             :     gettext_noop("Use DROP TYPE to remove a type.")},
     297                 :             :     {RELKIND_FOREIGN_TABLE,
     298                 :             :         ERRCODE_UNDEFINED_OBJECT,
     299                 :             :         gettext_noop("foreign table \"%s\" does not exist"),
     300                 :             :         gettext_noop("foreign table \"%s\" does not exist, skipping"),
     301                 :             :         gettext_noop("\"%s\" is not a foreign table"),
     302                 :             :     gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
     303                 :             :     {RELKIND_PARTITIONED_TABLE,
     304                 :             :         ERRCODE_UNDEFINED_TABLE,
     305                 :             :         gettext_noop("table \"%s\" does not exist"),
     306                 :             :         gettext_noop("table \"%s\" does not exist, skipping"),
     307                 :             :         gettext_noop("\"%s\" is not a table"),
     308                 :             :     gettext_noop("Use DROP TABLE to remove a table.")},
     309                 :             :     {RELKIND_PARTITIONED_INDEX,
     310                 :             :         ERRCODE_UNDEFINED_OBJECT,
     311                 :             :         gettext_noop("index \"%s\" does not exist"),
     312                 :             :         gettext_noop("index \"%s\" does not exist, skipping"),
     313                 :             :         gettext_noop("\"%s\" is not an index"),
     314                 :             :     gettext_noop("Use DROP INDEX to remove an index.")},
     315                 :             :     {RELKIND_PROPGRAPH,
     316                 :             :         ERRCODE_UNDEFINED_OBJECT,
     317                 :             :         gettext_noop("property graph \"%s\" does not exist"),
     318                 :             :         gettext_noop("property graph \"%s\" does not exist, skipping"),
     319                 :             :         gettext_noop("\"%s\" is not a property graph"),
     320                 :             :     gettext_noop("Use DROP PROPERTY GRAPH to remove a property graph.")},
     321                 :             :     {'\0', 0, NULL, NULL, NULL, NULL}
     322                 :             : };
     323                 :             : 
     324                 :             : /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
     325                 :             : struct DropRelationCallbackState
     326                 :             : {
     327                 :             :     /* These fields are set by RemoveRelations: */
     328                 :             :     char        expected_relkind;
     329                 :             :     LOCKMODE    heap_lockmode;
     330                 :             :     /* These fields are state to track which subsidiary locks are held: */
     331                 :             :     Oid         heapOid;
     332                 :             :     Oid         partParentOid;
     333                 :             :     /* These fields are passed back by RangeVarCallbackForDropRelation: */
     334                 :             :     char        actual_relkind;
     335                 :             :     char        actual_relpersistence;
     336                 :             : };
     337                 :             : 
     338                 :             : /* Alter table target-type flags for ATSimplePermissions */
     339                 :             : #define     ATT_TABLE               0x0001
     340                 :             : #define     ATT_VIEW                0x0002
     341                 :             : #define     ATT_MATVIEW             0x0004
     342                 :             : #define     ATT_INDEX               0x0008
     343                 :             : #define     ATT_COMPOSITE_TYPE      0x0010
     344                 :             : #define     ATT_FOREIGN_TABLE       0x0020
     345                 :             : #define     ATT_PARTITIONED_INDEX   0x0040
     346                 :             : #define     ATT_SEQUENCE            0x0080
     347                 :             : #define     ATT_PARTITIONED_TABLE   0x0100
     348                 :             : 
     349                 :             : /*
     350                 :             :  * ForeignTruncateInfo
     351                 :             :  *
     352                 :             :  * Information related to truncation of foreign tables.  This is used for
     353                 :             :  * the elements in a hash table. It uses the server OID as lookup key,
     354                 :             :  * and includes a per-server list of all foreign tables involved in the
     355                 :             :  * truncation.
     356                 :             :  */
     357                 :             : typedef struct ForeignTruncateInfo
     358                 :             : {
     359                 :             :     Oid         serverid;
     360                 :             :     List       *rels;
     361                 :             : } ForeignTruncateInfo;
     362                 :             : 
     363                 :             : /* Partial or complete FK creation in addFkConstraint() */
     364                 :             : typedef enum addFkConstraintSides
     365                 :             : {
     366                 :             :     addFkReferencedSide,
     367                 :             :     addFkReferencingSide,
     368                 :             :     addFkBothSides,
     369                 :             : } addFkConstraintSides;
     370                 :             : 
     371                 :             : /*
     372                 :             :  * Hold extension dependencies of one partition index, during
     373                 :             :  * MERGE/SPLIT PARTITION processing.
     374                 :             :  *
     375                 :             :  * collectPartitionIndexExtDeps() builds a list of these entries sorted by
     376                 :             :  * parentIndexOid with exactly one entry per parent partitioned index; the
     377                 :             :  * list is then consumed by applyPartitionIndexExtDeps() to re-record the
     378                 :             :  * same dependencies on the newly created partition's indexes.
     379                 :             :  *
     380                 :             :  * extensionOids is kept sorted ascending so that equality checks between
     381                 :             :  * entries from different partitions can be done in a single pass.
     382                 :             :  * indexOid is carried only so that conflict errors can cite specific
     383                 :             :  * partition index names.
     384                 :             :  */
     385                 :             : typedef struct PartitionIndexExtDepEntry
     386                 :             : {
     387                 :             :     Oid         parentIndexOid; /* OID of the parent partitioned index */
     388                 :             :     Oid         indexOid;       /* OID of a representative partition index */
     389                 :             :     List       *extensionOids;  /* OIDs of dependent extensions, sorted asc */
     390                 :             : } PartitionIndexExtDepEntry;
     391                 :             : 
     392                 :             : /*
     393                 :             :  * Partition tables are expected to be dropped when the parent partitioned
     394                 :             :  * table gets dropped. Hence for partitioning we use AUTO dependency.
     395                 :             :  * Otherwise, for regular inheritance use NORMAL dependency.
     396                 :             :  */
     397                 :             : #define child_dependency_type(child_is_partition)   \
     398                 :             :     ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
     399                 :             : 
     400                 :             : static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
     401                 :             : static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
     402                 :             : static void truncate_check_activity(Relation rel);
     403                 :             : static void RangeVarCallbackForTruncate(const RangeVar *relation,
     404                 :             :                                         Oid relId, Oid oldRelId, void *arg);
     405                 :             : static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
     406                 :             :                              bool is_partition, List **supconstr,
     407                 :             :                              List **supnotnulls);
     408                 :             : static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
     409                 :             : static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
     410                 :             : static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
     411                 :             : static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
     412                 :             : static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
     413                 :             : static void StoreCatalogInheritance(Oid relationId, List *supers,
     414                 :             :                                     bool child_is_partition);
     415                 :             : static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
     416                 :             :                                      int32 seqNumber, Relation inhRelation,
     417                 :             :                                      bool child_is_partition);
     418                 :             : static int  findAttrByName(const char *attributeName, const List *columns);
     419                 :             : static void AlterIndexNamespaces(Relation classRel, Relation rel,
     420                 :             :                                  Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
     421                 :             : static void AlterSeqNamespaces(Relation classRel, Relation rel,
     422                 :             :                                Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
     423                 :             :                                LOCKMODE lockmode);
     424                 :             : static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel,
     425                 :             :                                            ATAlterConstraint *cmdcon,
     426                 :             :                                            bool recurse, LOCKMODE lockmode);
     427                 :             : static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
     428                 :             :                                           Relation tgrel, Relation rel, HeapTuple contuple,
     429                 :             :                                           bool recurse, LOCKMODE lockmode);
     430                 :             : static bool ATExecAlterFKConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
     431                 :             :                                               Relation conrel, Relation tgrel,
     432                 :             :                                               Oid fkrelid, Oid pkrelid,
     433                 :             :                                               HeapTuple contuple, LOCKMODE lockmode,
     434                 :             :                                               Oid ReferencedParentDelTrigger,
     435                 :             :                                               Oid ReferencedParentUpdTrigger,
     436                 :             :                                               Oid ReferencingParentInsTrigger,
     437                 :             :                                               Oid ReferencingParentUpdTrigger);
     438                 :             : static bool ATExecAlterCheckConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
     439                 :             :                                                  Relation conrel, HeapTuple contuple,
     440                 :             :                                                  bool recurse, bool recursing,
     441                 :             :                                                  List *changing_conids,
     442                 :             :                                                  LOCKMODE lockmode);
     443                 :             : static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
     444                 :             :                                            Relation conrel, Relation tgrel, Relation rel,
     445                 :             :                                            HeapTuple contuple, bool recurse,
     446                 :             :                                            List **otherrelids, LOCKMODE lockmode);
     447                 :             : static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
     448                 :             :                                             Relation conrel, Relation rel,
     449                 :             :                                             HeapTuple contuple, LOCKMODE lockmode);
     450                 :             : static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
     451                 :             :                                             bool deferrable, bool initdeferred,
     452                 :             :                                             List **otherrelids);
     453                 :             : static void AlterFKConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
     454                 :             :                                                Relation conrel, Relation tgrel,
     455                 :             :                                                Oid fkrelid, Oid pkrelid,
     456                 :             :                                                HeapTuple contuple, LOCKMODE lockmode,
     457                 :             :                                                Oid ReferencedParentDelTrigger,
     458                 :             :                                                Oid ReferencedParentUpdTrigger,
     459                 :             :                                                Oid ReferencingParentInsTrigger,
     460                 :             :                                                Oid ReferencingParentUpdTrigger);
     461                 :             : static void AlterCheckConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
     462                 :             :                                                   Relation conrel, Oid conrelid,
     463                 :             :                                                   bool recurse, bool recursing,
     464                 :             :                                                   List *changing_conids,
     465                 :             :                                                   LOCKMODE lockmode);
     466                 :             : static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
     467                 :             :                                             Relation conrel, Relation tgrel, Relation rel,
     468                 :             :                                             HeapTuple contuple, bool recurse,
     469                 :             :                                             List **otherrelids, LOCKMODE lockmode);
     470                 :             : static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
     471                 :             :                                              HeapTuple contuple);
     472                 :             : static bool ATCheckCheckConstrHasEnforcedParent(Relation conrel, Relation rel,
     473                 :             :                                                 HeapTuple contuple,
     474                 :             :                                                 List *changing_conids,
     475                 :             :                                                 Oid *enforced_parentoid);
     476                 :             : static ObjectAddress ATExecValidateConstraint(List **wqueue,
     477                 :             :                                               Relation rel, char *constrName,
     478                 :             :                                               bool recurse, bool recursing, LOCKMODE lockmode);
     479                 :             : static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
     480                 :             :                                         Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode);
     481                 :             : static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
     482                 :             :                                            char *constrName, HeapTuple contuple,
     483                 :             :                                            bool recurse, bool recursing, LOCKMODE lockmode);
     484                 :             : static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
     485                 :             :                                         HeapTuple contuple, bool recurse, bool recursing,
     486                 :             :                                         LOCKMODE lockmode);
     487                 :             : static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc);
     488                 :             : static int  transformColumnNameList(Oid relId, List *colList,
     489                 :             :                                     int16 *attnums, Oid *atttypids, Oid *attcollids);
     490                 :             : static int  transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
     491                 :             :                                        List **attnamelist,
     492                 :             :                                        int16 *attnums, Oid *atttypids, Oid *attcollids,
     493                 :             :                                        Oid *opclasses, bool *pk_has_without_overlaps);
     494                 :             : static Oid  transformFkeyCheckAttrs(Relation pkrel,
     495                 :             :                                     int numattrs, int16 *attnums,
     496                 :             :                                     bool with_period, Oid *opclasses,
     497                 :             :                                     bool *pk_has_without_overlaps);
     498                 :             : static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
     499                 :             : static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
     500                 :             :                                      Oid *funcid);
     501                 :             : static void validateForeignKeyConstraint(char *conname,
     502                 :             :                                          Relation rel, Relation pkrel,
     503                 :             :                                          Oid pkindOid, Oid constraintOid, bool hasperiod);
     504                 :             : static void CheckAlterTableIsSafe(Relation rel);
     505                 :             : static void ATController(AlterTableStmt *parsetree,
     506                 :             :                          Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
     507                 :             :                          AlterTableUtilityContext *context);
     508                 :             : static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
     509                 :             :                       bool recurse, bool recursing, LOCKMODE lockmode,
     510                 :             :                       AlterTableUtilityContext *context);
     511                 :             : static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
     512                 :             :                               AlterTableUtilityContext *context);
     513                 :             : static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
     514                 :             :                       AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
     515                 :             :                       AlterTableUtilityContext *context);
     516                 :             : static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
     517                 :             :                                           Relation rel, AlterTableCmd *cmd,
     518                 :             :                                           bool recurse, LOCKMODE lockmode,
     519                 :             :                                           AlterTablePass cur_pass,
     520                 :             :                                           AlterTableUtilityContext *context);
     521                 :             : static void ATRewriteTables(AlterTableStmt *parsetree,
     522                 :             :                             List **wqueue, LOCKMODE lockmode,
     523                 :             :                             AlterTableUtilityContext *context);
     524                 :             : static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
     525                 :             : static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
     526                 :             : static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
     527                 :             : static void ATSimpleRecursion(List **wqueue, Relation rel,
     528                 :             :                               AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
     529                 :             :                               AlterTableUtilityContext *context);
     530                 :             : static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
     531                 :             : static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
     532                 :             :                                   LOCKMODE lockmode,
     533                 :             :                                   AlterTableUtilityContext *context);
     534                 :             : static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
     535                 :             :                                            DropBehavior behavior);
     536                 :             : static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     537                 :             :                             bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
     538                 :             :                             AlterTableUtilityContext *context);
     539                 :             : static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
     540                 :             :                                      Relation rel, AlterTableCmd **cmd,
     541                 :             :                                      bool recurse, bool recursing,
     542                 :             :                                      LOCKMODE lockmode, AlterTablePass cur_pass,
     543                 :             :                                      AlterTableUtilityContext *context);
     544                 :             : static bool check_for_column_name_collision(Relation rel, const char *colname,
     545                 :             :                                             bool if_not_exists);
     546                 :             : static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
     547                 :             : static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
     548                 :             : static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
     549                 :             :                                        LOCKMODE lockmode);
     550                 :             : static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
     551                 :             :                            bool queue_validation);
     552                 :             : static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
     553                 :             :                                       char *conName, char *colName,
     554                 :             :                                       bool recurse, bool recursing,
     555                 :             :                                       LOCKMODE lockmode);
     556                 :             : static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
     557                 :             : static bool ConstraintImpliedByRelConstraint(Relation scanrel,
     558                 :             :                                              List *testConstraint, List *provenConstraint);
     559                 :             : static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
     560                 :             :                                          Node *newDefault, LOCKMODE lockmode);
     561                 :             : static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
     562                 :             :                                                Node *newDefault);
     563                 :             : static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
     564                 :             :                                        Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
     565                 :             : static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
     566                 :             :                                        Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
     567                 :             : static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
     568                 :             :                                         bool recurse, bool recursing);
     569                 :             : static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
     570                 :             :                                          Node *newExpr, LOCKMODE lockmode);
     571                 :             : static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
     572                 :             : static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
     573                 :             : static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
     574                 :             :                                          Node *newValue, LOCKMODE lockmode);
     575                 :             : static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
     576                 :             :                                       Node *options, bool isReset, LOCKMODE lockmode);
     577                 :             : static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
     578                 :             :                                       Node *newValue, LOCKMODE lockmode);
     579                 :             : static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
     580                 :             :                              AlterTableCmd *cmd, LOCKMODE lockmode,
     581                 :             :                              AlterTableUtilityContext *context);
     582                 :             : static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
     583                 :             :                                       DropBehavior behavior,
     584                 :             :                                       bool recurse, bool recursing,
     585                 :             :                                       bool missing_ok, LOCKMODE lockmode,
     586                 :             :                                       ObjectAddresses *addrs);
     587                 :             : static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
     588                 :             :                                 bool recurse, LOCKMODE lockmode,
     589                 :             :                                 AlterTableUtilityContext *context);
     590                 :             : static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname);
     591                 :             : static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
     592                 :             :                                     IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     593                 :             : static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
     594                 :             :                                          CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
     595                 :             : static ObjectAddress ATExecAddConstraint(List **wqueue,
     596                 :             :                                          AlteredTableInfo *tab, Relation rel,
     597                 :             :                                          Constraint *newConstraint, bool recurse, bool is_readd,
     598                 :             :                                          LOCKMODE lockmode);
     599                 :             : static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
     600                 :             : static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
     601                 :             :                                               IndexStmt *stmt, LOCKMODE lockmode);
     602                 :             : static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
     603                 :             :                                             AlteredTableInfo *tab, Relation rel,
     604                 :             :                                             Constraint *constr,
     605                 :             :                                             bool recurse, bool recursing, bool is_readd,
     606                 :             :                                             LOCKMODE lockmode);
     607                 :             : static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
     608                 :             :                                                Relation rel, Constraint *fkconstraint,
     609                 :             :                                                bool recurse, bool recursing,
     610                 :             :                                                LOCKMODE lockmode);
     611                 :             : static int  validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
     612                 :             :                                          int numfksetcols, int16 *fksetcolsattnums,
     613                 :             :                                          List *fksetcols);
     614                 :             : static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
     615                 :             :                                      char *constraintname,
     616                 :             :                                      Constraint *fkconstraint, Relation rel,
     617                 :             :                                      Relation pkrel, Oid indexOid,
     618                 :             :                                      Oid parentConstr,
     619                 :             :                                      int numfks, int16 *pkattnum, int16 *fkattnum,
     620                 :             :                                      Oid *pfeqoperators, Oid *ppeqoperators,
     621                 :             :                                      Oid *ffeqoperators, int numfkdelsetcols,
     622                 :             :                                      int16 *fkdelsetcols, bool is_internal,
     623                 :             :                                      bool with_period);
     624                 :             : static void addFkRecurseReferenced(Constraint *fkconstraint,
     625                 :             :                                    Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     626                 :             :                                    int numfks, int16 *pkattnum, int16 *fkattnum,
     627                 :             :                                    Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     628                 :             :                                    int numfkdelsetcols, int16 *fkdelsetcols,
     629                 :             :                                    bool old_check_ok,
     630                 :             :                                    Oid parentDelTrigger, Oid parentUpdTrigger,
     631                 :             :                                    bool with_period);
     632                 :             : static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
     633                 :             :                                     Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
     634                 :             :                                     int numfks, int16 *pkattnum, int16 *fkattnum,
     635                 :             :                                     Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
     636                 :             :                                     int numfkdelsetcols, int16 *fkdelsetcols,
     637                 :             :                                     bool old_check_ok, LOCKMODE lockmode,
     638                 :             :                                     Oid parentInsTrigger, Oid parentUpdTrigger,
     639                 :             :                                     bool with_period);
     640                 :             : static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
     641                 :             :                                        Relation partitionRel);
     642                 :             : static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
     643                 :             : static void CloneFkReferencing(List **wqueue, Relation parentRel,
     644                 :             :                                Relation partRel);
     645                 :             : static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
     646                 :             :                                           Constraint *fkconstraint, Oid constraintOid,
     647                 :             :                                           Oid indexOid,
     648                 :             :                                           Oid parentInsTrigger, Oid parentUpdTrigger,
     649                 :             :                                           Oid *insertTrigOid, Oid *updateTrigOid);
     650                 :             : static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
     651                 :             :                                            Constraint *fkconstraint, Oid constraintOid,
     652                 :             :                                            Oid indexOid,
     653                 :             :                                            Oid parentDelTrigger, Oid parentUpdTrigger,
     654                 :             :                                            Oid *deleteTrigOid, Oid *updateTrigOid);
     655                 :             : static bool tryAttachPartitionForeignKey(List **wqueue,
     656                 :             :                                          ForeignKeyCacheInfo *fk,
     657                 :             :                                          Relation partition,
     658                 :             :                                          Oid parentConstrOid, int numfks,
     659                 :             :                                          AttrNumber *mapped_conkey, AttrNumber *confkey,
     660                 :             :                                          Oid *conpfeqop,
     661                 :             :                                          Oid parentInsTrigger,
     662                 :             :                                          Oid parentUpdTrigger,
     663                 :             :                                          Relation trigrel);
     664                 :             : static void AttachPartitionForeignKey(List **wqueue, Relation partition,
     665                 :             :                                       Oid partConstrOid, Oid parentConstrOid,
     666                 :             :                                       Oid parentInsTrigger, Oid parentUpdTrigger,
     667                 :             :                                       Relation trigrel);
     668                 :             : static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
     669                 :             :                                       Oid conoid, Oid conrelid);
     670                 :             : static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
     671                 :             :                                              Oid confrelid, Oid conrelid);
     672                 :             : static void GetForeignKeyActionTriggers(Relation trigrel,
     673                 :             :                                         Oid conoid, Oid confrelid, Oid conrelid,
     674                 :             :                                         Oid *deleteTriggerOid,
     675                 :             :                                         Oid *updateTriggerOid);
     676                 :             : static void GetForeignKeyCheckTriggers(Relation trigrel,
     677                 :             :                                        Oid conoid, Oid confrelid, Oid conrelid,
     678                 :             :                                        Oid *insertTriggerOid,
     679                 :             :                                        Oid *updateTriggerOid);
     680                 :             : static void ATExecDropConstraint(Relation rel, const char *constrName,
     681                 :             :                                  DropBehavior behavior, bool recurse,
     682                 :             :                                  bool missing_ok, LOCKMODE lockmode);
     683                 :             : static ObjectAddress dropconstraint_internal(Relation rel,
     684                 :             :                                              HeapTuple constraintTup, DropBehavior behavior,
     685                 :             :                                              bool recurse, bool recursing,
     686                 :             :                                              bool missing_ok, LOCKMODE lockmode);
     687                 :             : static void ATPrepAlterColumnType(List **wqueue,
     688                 :             :                                   AlteredTableInfo *tab, Relation rel,
     689                 :             :                                   bool recurse, bool recursing,
     690                 :             :                                   AlterTableCmd *cmd, LOCKMODE lockmode,
     691                 :             :                                   AlterTableUtilityContext *context);
     692                 :             : static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
     693                 :             : static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
     694                 :             :                                            AlterTableCmd *cmd, LOCKMODE lockmode);
     695                 :             : static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
     696                 :             :                                               Relation rel, AttrNumber attnum, const char *colName);
     697                 :             : static void RememberWholeRowDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel);
     698                 :             : static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
     699                 :             : static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
     700                 :             : static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
     701                 :             : static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
     702                 :             :                                    LOCKMODE lockmode);
     703                 :             : static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, Oid ownerId,
     704                 :             :                                  char *cmd, List **wqueue, LOCKMODE lockmode,
     705                 :             :                                  bool rewrite);
     706                 :             : static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
     707                 :             :                                      Oid objid, Relation rel, List *domname,
     708                 :             :                                      const char *conname);
     709                 :             : static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
     710                 :             : static void TryReuseForeignKey(Oid oldId, Constraint *con);
     711                 :             : static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
     712                 :             :                                                      List *options, LOCKMODE lockmode);
     713                 :             : static void change_owner_fix_column_acls(Oid relationOid,
     714                 :             :                                          Oid oldOwnerId, Oid newOwnerId);
     715                 :             : static void change_owner_recurse_to_sequences(Oid relationOid,
     716                 :             :                                               Oid newOwnerId, LOCKMODE lockmode);
     717                 :             : static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
     718                 :             :                                      LOCKMODE lockmode);
     719                 :             : static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
     720                 :             : static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
     721                 :             : static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
     722                 :             : static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
     723                 :             :                                     bool toLogged);
     724                 :             : static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
     725                 :             :                                 const char *tablespacename, LOCKMODE lockmode);
     726                 :             : static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
     727                 :             : static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
     728                 :             : static void ATExecSetRelOptions(Relation rel, List *defList,
     729                 :             :                                 AlterTableType operation,
     730                 :             :                                 LOCKMODE lockmode);
     731                 :             : static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
     732                 :             :                                        char fires_when, bool skip_system, bool recurse,
     733                 :             :                                        LOCKMODE lockmode);
     734                 :             : static void ATExecEnableDisableRule(Relation rel, const char *rulename,
     735                 :             :                                     char fires_when, LOCKMODE lockmode);
     736                 :             : static void ATPrepChangeInherit(Relation child_rel);
     737                 :             : static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
     738                 :             : static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
     739                 :             : static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
     740                 :             :                                    DependencyType deptype);
     741                 :             : static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
     742                 :             : static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
     743                 :             : static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
     744                 :             : static void ATExecGenericOptions(Relation rel, List *options);
     745                 :             : static void ATExecSetRowSecurity(Relation rel, bool rls);
     746                 :             : static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
     747                 :             : static ObjectAddress ATExecSetCompression(Relation rel,
     748                 :             :                                           const char *column, Node *newValue, LOCKMODE lockmode);
     749                 :             : 
     750                 :             : static void index_copy_data(Relation rel, RelFileLocator newrlocator);
     751                 :             : static const char *storage_name(char c);
     752                 :             : 
     753                 :             : static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
     754                 :             :                                             Oid oldRelOid, void *arg);
     755                 :             : static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
     756                 :             :                                              Oid oldrelid, void *arg);
     757                 :             : static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
     758                 :             : static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
     759                 :             :                                   List **partexprs, Oid *partopclass, Oid *partcollation,
     760                 :             :                                   PartitionStrategy strategy);
     761                 :             : static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
     762                 :             : static void RemoveInheritance(Relation child_rel, Relation parent_rel,
     763                 :             :                               bool expect_detached);
     764                 :             : static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
     765                 :             :                                            PartitionCmd *cmd,
     766                 :             :                                            AlterTableUtilityContext *context);
     767                 :             : static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
     768                 :             : static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
     769                 :             :                                                List *partConstraint,
     770                 :             :                                                bool validate_default);
     771                 :             : static void CloneRowTriggersToPartition(Relation parent, Relation partition);
     772                 :             : static void DropClonedTriggersFromPartition(Oid partitionId);
     773                 :             : static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
     774                 :             :                                            Relation rel, RangeVar *name,
     775                 :             :                                            bool concurrent);
     776                 :             : static void DetachPartitionFinalize(Relation rel, Relation partRel,
     777                 :             :                                     bool concurrent, Oid defaultPartOid);
     778                 :             : static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
     779                 :             : static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
     780                 :             :                                               RangeVar *name);
     781                 :             : static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
     782                 :             : static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
     783                 :             :                                   Relation partitionTbl);
     784                 :             : static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
     785                 :             : static List *GetParentedForeignKeyRefs(Relation partition);
     786                 :             : static void ATDetachCheckNoForeignKeyRefs(Relation partition);
     787                 :             : static char GetAttributeCompression(Oid atttypid, const char *compression);
     788                 :             : static char GetAttributeStorage(Oid atttypid, const char *storagemode);
     789                 :             : 
     790                 :             : static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
     791                 :             :                                   PartitionCmd *cmd, AlterTableUtilityContext *context);
     792                 :             : static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
     793                 :             :                                  Relation rel, PartitionCmd *cmd,
     794                 :             :                                  AlterTableUtilityContext *context);
     795                 :             : static List *collectPartitionIndexExtDeps(List *partitionOids);
     796                 :             : static void applyPartitionIndexExtDeps(Oid newPartOid, List *extDepState);
     797                 :             : static void freePartitionIndexExtDeps(List *extDepState);
     798                 :             : 
     799                 :             : /* ----------------------------------------------------------------
     800                 :             :  *      DefineRelation
     801                 :             :  *              Creates a new relation.
     802                 :             :  *
     803                 :             :  * stmt carries parsetree information from an ordinary CREATE TABLE statement.
     804                 :             :  * The other arguments are used to extend the behavior for other cases:
     805                 :             :  * relkind: relkind to assign to the new relation
     806                 :             :  * ownerId: if not InvalidOid, use this as the new relation's owner.
     807                 :             :  * typaddress: if not null, it's set to the pg_type entry's address.
     808                 :             :  * queryString: for error reporting
     809                 :             :  *
     810                 :             :  * Note that permissions checks are done against current user regardless of
     811                 :             :  * ownerId.  A nonzero ownerId is used when someone is creating a relation
     812                 :             :  * "on behalf of" someone else, so we still want to see that the current user
     813                 :             :  * has permissions to do it.
     814                 :             :  *
     815                 :             :  * If successful, returns the address of the new relation.
     816                 :             :  * ----------------------------------------------------------------
     817                 :             :  */
     818                 :             : ObjectAddress
     819                 :       42566 : DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
     820                 :             :                ObjectAddress *typaddress, const char *queryString)
     821                 :             : {
     822                 :             :     char        relname[NAMEDATALEN];
     823                 :             :     Oid         namespaceId;
     824                 :             :     Oid         relationId;
     825                 :             :     Oid         tablespaceId;
     826                 :             :     Relation    rel;
     827                 :             :     TupleDesc   descriptor;
     828                 :             :     List       *inheritOids;
     829                 :             :     List       *old_constraints;
     830                 :             :     List       *old_notnulls;
     831                 :             :     List       *rawDefaults;
     832                 :             :     List       *cookedDefaults;
     833                 :             :     List       *nncols;
     834                 :       42566 :     List       *connames = NIL;
     835                 :             :     Datum       reloptions;
     836                 :             :     ListCell   *listptr;
     837                 :             :     AttrNumber  attnum;
     838                 :             :     bool        partitioned;
     839                 :       42566 :     const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
     840                 :             :     Oid         ofTypeId;
     841                 :             :     ObjectAddress address;
     842                 :             :     LOCKMODE    parentLockmode;
     843                 :       42566 :     Oid         accessMethodId = InvalidOid;
     844                 :             : 
     845                 :             :     /*
     846                 :             :      * Truncate relname to appropriate length (probably a waste of time, as
     847                 :             :      * parser should have done this already).
     848                 :             :      */
     849                 :       42566 :     strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
     850                 :             : 
     851                 :             :     /*
     852                 :             :      * Check consistency of arguments
     853                 :             :      */
     854         [ +  + ]:       42566 :     if (stmt->oncommit != ONCOMMIT_NOOP
     855         [ +  + ]:         128 :         && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
     856         [ +  - ]:           8 :         ereport(ERROR,
     857                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
     858                 :             :                  errmsg("ON COMMIT can only be used on temporary tables")));
     859                 :             : 
     860         [ +  + ]:       42558 :     if (stmt->partspec != NULL)
     861                 :             :     {
     862         [ -  + ]:        3688 :         if (relkind != RELKIND_RELATION)
     863         [ #  # ]:           0 :             elog(ERROR, "unexpected relkind: %d", (int) relkind);
     864                 :             : 
     865                 :        3688 :         relkind = RELKIND_PARTITIONED_TABLE;
     866                 :        3688 :         partitioned = true;
     867                 :             :     }
     868                 :             :     else
     869                 :       38870 :         partitioned = false;
     870                 :             : 
     871         [ +  + ]:       42558 :     if (relkind == RELKIND_PARTITIONED_TABLE &&
     872         [ +  + ]:        3688 :         stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
     873         [ +  - ]:           4 :         ereport(ERROR,
     874                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     875                 :             :                  errmsg("partitioned tables cannot be unlogged")));
     876                 :             : 
     877                 :             :     /*
     878                 :             :      * Look up the namespace in which we are supposed to create the relation,
     879                 :             :      * check we have permission to create there, lock it against concurrent
     880                 :             :      * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
     881                 :             :      * namespace is selected.
     882                 :             :      */
     883                 :             :     namespaceId =
     884                 :       42554 :         RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
     885                 :             : 
     886                 :             :     /*
     887                 :             :      * Security check: disallow creating temp tables from security-restricted
     888                 :             :      * code.  This is needed because calling code might not expect untrusted
     889                 :             :      * tables to appear in pg_temp at the front of its search path.
     890                 :             :      */
     891         [ +  + ]:       42554 :     if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
     892         [ -  + ]:        2358 :         && InSecurityRestrictedOperation())
     893         [ #  # ]:           0 :         ereport(ERROR,
     894                 :             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     895                 :             :                  errmsg("cannot create temporary table within security-restricted operation")));
     896                 :             : 
     897                 :             :     /*
     898                 :             :      * Determine the lockmode to use when scanning parents.  A self-exclusive
     899                 :             :      * lock is needed here.
     900                 :             :      *
     901                 :             :      * For regular inheritance, if two backends attempt to add children to the
     902                 :             :      * same parent simultaneously, and that parent has no pre-existing
     903                 :             :      * children, then both will attempt to update the parent's relhassubclass
     904                 :             :      * field, leading to a "tuple concurrently updated" error.  Also, this
     905                 :             :      * interlocks against a concurrent ANALYZE on the parent table, which
     906                 :             :      * might otherwise be attempting to clear the parent's relhassubclass
     907                 :             :      * field, if its previous children were recently dropped.
     908                 :             :      *
     909                 :             :      * If the child table is a partition, then we instead grab an exclusive
     910                 :             :      * lock on the parent because its partition descriptor will be changed by
     911                 :             :      * addition of the new partition.
     912                 :             :      */
     913         [ +  + ]:       42554 :     parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
     914                 :             :                       ShareUpdateExclusiveLock);
     915                 :             : 
     916                 :             :     /* Determine the list of OIDs of the parents. */
     917                 :       42554 :     inheritOids = NIL;
     918   [ +  +  +  +  :       50827 :     foreach(listptr, stmt->inhRelations)
                   +  + ]
     919                 :             :     {
     920                 :        8273 :         RangeVar   *rv = (RangeVar *) lfirst(listptr);
     921                 :             :         Oid         parentOid;
     922                 :             : 
     923                 :        8273 :         parentOid = RangeVarGetRelid(rv, parentLockmode, false);
     924                 :             : 
     925                 :             :         /*
     926                 :             :          * Reject duplications in the list of parents.
     927                 :             :          */
     928         [ -  + ]:        8273 :         if (list_member_oid(inheritOids, parentOid))
     929         [ #  # ]:           0 :             ereport(ERROR,
     930                 :             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
     931                 :             :                      errmsg("relation \"%s\" would be inherited from more than once",
     932                 :             :                             get_rel_name(parentOid))));
     933                 :             : 
     934                 :        8273 :         inheritOids = lappend_oid(inheritOids, parentOid);
     935                 :             :     }
     936                 :             : 
     937                 :             :     /*
     938                 :             :      * Select tablespace to use: an explicitly indicated one, or (in the case
     939                 :             :      * of a partitioned table) the parent's, if it has one.
     940                 :             :      */
     941         [ +  + ]:       42554 :     if (stmt->tablespacename)
     942                 :             :     {
     943                 :          92 :         tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
     944                 :             : 
     945   [ +  +  +  + ]:          88 :         if (partitioned && tablespaceId == MyDatabaseTableSpace)
     946         [ +  - ]:           4 :             ereport(ERROR,
     947                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     948                 :             :                      errmsg("cannot specify default tablespace for partitioned relations")));
     949                 :             :     }
     950         [ +  + ]:       42462 :     else if (stmt->partbound)
     951                 :             :     {
     952                 :             :         Assert(list_length(inheritOids) == 1);
     953                 :        6568 :         tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
     954                 :             :     }
     955                 :             :     else
     956                 :       35894 :         tablespaceId = InvalidOid;
     957                 :             : 
     958                 :             :     /* still nothing? use the default */
     959         [ +  + ]:       42546 :     if (!OidIsValid(tablespaceId))
     960                 :       42424 :         tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
     961                 :             :                                             partitioned);
     962                 :             : 
     963                 :             :     /* Check permissions except when using database's default */
     964   [ +  +  +  + ]:       42542 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
     965                 :             :     {
     966                 :             :         AclResult   aclresult;
     967                 :             : 
     968                 :         138 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
     969                 :             :                                     ACL_CREATE);
     970         [ +  + ]:         138 :         if (aclresult != ACLCHECK_OK)
     971                 :           3 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
     972                 :           3 :                            get_tablespace_name(tablespaceId));
     973                 :             :     }
     974                 :             : 
     975                 :             :     /* In all cases disallow placing user relations in pg_global */
     976         [ +  + ]:       42539 :     if (tablespaceId == GLOBALTABLESPACE_OID)
     977         [ +  - ]:          12 :         ereport(ERROR,
     978                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     979                 :             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
     980                 :             : 
     981                 :             :     /* Identify user ID that will own the table */
     982         [ +  + ]:       42527 :     if (!OidIsValid(ownerId))
     983                 :       42372 :         ownerId = GetUserId();
     984                 :             : 
     985                 :             :     /*
     986                 :             :      * Parse and validate reloptions, if any.
     987                 :             :      */
     988                 :       42527 :     reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
     989                 :             :                                      true, false);
     990                 :             : 
     991      [ +  +  + ]:       42515 :     switch (relkind)
     992                 :             :     {
     993                 :       10849 :         case RELKIND_VIEW:
     994                 :       10849 :             (void) view_reloptions(reloptions, true);
     995                 :       10837 :             break;
     996                 :        3672 :         case RELKIND_PARTITIONED_TABLE:
     997                 :        3672 :             (void) partitioned_table_reloptions(reloptions, true);
     998                 :        3668 :             break;
     999                 :       27994 :         default:
    1000                 :       27994 :             (void) heap_reloptions(relkind, reloptions, true);
    1001                 :             :     }
    1002                 :             : 
    1003         [ +  + ]:       42435 :     if (stmt->ofTypename)
    1004                 :             :     {
    1005                 :             :         AclResult   aclresult;
    1006                 :             : 
    1007                 :          57 :         ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
    1008                 :             : 
    1009                 :          57 :         aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
    1010         [ +  + ]:          57 :         if (aclresult != ACLCHECK_OK)
    1011                 :           4 :             aclcheck_error_type(aclresult, ofTypeId);
    1012                 :             :     }
    1013                 :             :     else
    1014                 :       42378 :         ofTypeId = InvalidOid;
    1015                 :             : 
    1016                 :             :     /*
    1017                 :             :      * Look up inheritance ancestors and generate relation schema, including
    1018                 :             :      * inherited attributes.  (Note that stmt->tableElts is destructively
    1019                 :             :      * modified by MergeAttributes.)
    1020                 :             :      */
    1021                 :       42267 :     stmt->tableElts =
    1022                 :       42431 :         MergeAttributes(stmt->tableElts, inheritOids,
    1023                 :       42431 :                         stmt->relation->relpersistence,
    1024                 :       42431 :                         stmt->partbound != NULL,
    1025                 :             :                         &old_constraints, &old_notnulls);
    1026                 :             : 
    1027                 :             :     /*
    1028                 :             :      * NB: The defaults and constraints we just inherited are already cooked,
    1029                 :             :      * so they don't get the USAGE checks that AddRelationNewConstraints()
    1030                 :             :      * applies to raw ones.  That's intentional: the parent's own catalog
    1031                 :             :      * entries already depend on those types, so copying them pins nothing
    1032                 :             :      * new, and creating a child requires owning the parent, anyway.
    1033                 :             :      */
    1034                 :             : 
    1035                 :             :     /*
    1036                 :             :      * Create a tuple descriptor from the relation schema.  Note that this
    1037                 :             :      * deals with column names, types, and in-descriptor NOT NULL flags, but
    1038                 :             :      * not default values, NOT NULL or CHECK constraints; we handle those
    1039                 :             :      * below.
    1040                 :             :      */
    1041                 :       42267 :     descriptor = BuildDescForRelation(stmt->tableElts);
    1042                 :             : 
    1043                 :             :     /*
    1044                 :             :      * Find columns with default values and prepare for insertion of the
    1045                 :             :      * defaults.  Pre-cooked (that is, inherited) defaults go into a list of
    1046                 :             :      * CookedConstraint structs that we'll pass to heap_create_with_catalog,
    1047                 :             :      * while raw defaults go into a list of RawColumnDefault structs that will
    1048                 :             :      * be processed by AddRelationNewConstraints.  (We can't deal with raw
    1049                 :             :      * expressions until we can do transformExpr.)
    1050                 :             :      */
    1051                 :       42235 :     rawDefaults = NIL;
    1052                 :       42235 :     cookedDefaults = NIL;
    1053                 :       42235 :     attnum = 0;
    1054                 :             : 
    1055   [ +  +  +  +  :      210239 :     foreach(listptr, stmt->tableElts)
                   +  + ]
    1056                 :             :     {
    1057                 :      168004 :         ColumnDef  *colDef = lfirst(listptr);
    1058                 :             : 
    1059                 :      168004 :         attnum++;
    1060         [ +  + ]:      168004 :         if (colDef->raw_default != NULL)
    1061                 :             :         {
    1062                 :             :             RawColumnDefault *rawEnt;
    1063                 :             : 
    1064                 :             :             Assert(colDef->cooked_default == NULL);
    1065                 :             : 
    1066                 :        2253 :             rawEnt = palloc_object(RawColumnDefault);
    1067                 :        2253 :             rawEnt->attnum = attnum;
    1068                 :        2253 :             rawEnt->raw_default = colDef->raw_default;
    1069                 :        2253 :             rawEnt->generated = colDef->generated;
    1070                 :        2253 :             rawDefaults = lappend(rawDefaults, rawEnt);
    1071                 :             :         }
    1072         [ +  + ]:      165751 :         else if (colDef->cooked_default != NULL)
    1073                 :             :         {
    1074                 :             :             CookedConstraint *cooked;
    1075                 :             : 
    1076                 :         374 :             cooked = palloc_object(CookedConstraint);
    1077                 :         374 :             cooked->contype = CONSTR_DEFAULT;
    1078                 :         374 :             cooked->conoid = InvalidOid; /* until created */
    1079                 :         374 :             cooked->name = NULL;
    1080                 :         374 :             cooked->attnum = attnum;
    1081                 :         374 :             cooked->expr = colDef->cooked_default;
    1082                 :         374 :             cooked->is_enforced = true;
    1083                 :         374 :             cooked->skip_validation = false;
    1084                 :         374 :             cooked->is_local = true; /* not used for defaults */
    1085                 :         374 :             cooked->inhcount = 0;    /* ditto */
    1086                 :         374 :             cooked->is_no_inherit = false;
    1087                 :         374 :             cookedDefaults = lappend(cookedDefaults, cooked);
    1088                 :             :         }
    1089                 :             :     }
    1090                 :             : 
    1091                 :       42235 :     TupleDescFinalize(descriptor);
    1092                 :             : 
    1093                 :             :     /*
    1094                 :             :      * For relations with table AM and partitioned tables, select access
    1095                 :             :      * method to use: an explicitly indicated one, or (in the case of a
    1096                 :             :      * partitioned table) the parent's, if it has one.
    1097                 :             :      */
    1098         [ +  + ]:       42235 :     if (stmt->accessMethod != NULL)
    1099                 :             :     {
    1100                 :             :         Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
    1101                 :          87 :         accessMethodId = get_table_am_oid(stmt->accessMethod, false);
    1102                 :             :     }
    1103   [ +  +  +  -  :       42148 :     else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
             +  +  +  + ]
    1104                 :             :     {
    1105         [ +  + ]:       27241 :         if (stmt->partbound)
    1106                 :             :         {
    1107                 :             :             Assert(list_length(inheritOids) == 1);
    1108                 :        6452 :             accessMethodId = get_rel_relam(linitial_oid(inheritOids));
    1109                 :             :         }
    1110                 :             : 
    1111   [ +  +  +  -  :       27241 :         if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
             +  +  +  + ]
    1112                 :       23560 :             accessMethodId = get_table_am_oid(default_table_access_method, false);
    1113                 :             :     }
    1114                 :             : 
    1115                 :             :     /*
    1116                 :             :      * Create the relation.  Inherited defaults and CHECK constraints are
    1117                 :             :      * passed in for immediate handling --- since they don't need parsing,
    1118                 :             :      * they can be stored immediately.
    1119                 :             :      */
    1120                 :       42223 :     relationId = heap_create_with_catalog(relname,
    1121                 :             :                                           namespaceId,
    1122                 :             :                                           tablespaceId,
    1123                 :             :                                           InvalidOid,
    1124                 :             :                                           InvalidOid,
    1125                 :             :                                           ofTypeId,
    1126                 :             :                                           ownerId,
    1127                 :             :                                           accessMethodId,
    1128                 :             :                                           descriptor,
    1129                 :             :                                           list_concat(cookedDefaults,
    1130                 :             :                                                       old_constraints),
    1131                 :             :                                           relkind,
    1132                 :       42223 :                                           stmt->relation->relpersistence,
    1133                 :             :                                           false,
    1134                 :             :                                           false,
    1135                 :             :                                           stmt->oncommit,
    1136                 :             :                                           reloptions,
    1137                 :             :                                           true,
    1138                 :             :                                           allowSystemTableMods,
    1139                 :             :                                           false,
    1140                 :             :                                           InvalidOid,
    1141                 :             :                                           typaddress);
    1142                 :             : 
    1143                 :             :     /*
    1144                 :             :      * We must bump the command counter to make the newly-created relation
    1145                 :             :      * tuple visible for opening.
    1146                 :             :      */
    1147                 :       42187 :     CommandCounterIncrement();
    1148                 :             : 
    1149                 :             :     /*
    1150                 :             :      * Open the new relation and acquire exclusive lock on it.  This isn't
    1151                 :             :      * really necessary for locking out other backends (since they can't see
    1152                 :             :      * the new rel anyway until we commit), but it keeps the lock manager from
    1153                 :             :      * complaining about deadlock risks.
    1154                 :             :      */
    1155                 :       42187 :     rel = relation_open(relationId, AccessExclusiveLock);
    1156                 :             : 
    1157                 :             :     /*
    1158                 :             :      * Now add any newly specified column default and generation expressions
    1159                 :             :      * to the new relation.  These are passed to us in the form of raw
    1160                 :             :      * parsetrees; we need to transform them to executable expression trees
    1161                 :             :      * before they can be added. The most convenient way to do that is to
    1162                 :             :      * apply the parser's transformExpr routine, but transformExpr doesn't
    1163                 :             :      * work unless we have a pre-existing relation. So, the transformation has
    1164                 :             :      * to be postponed to this final step of CREATE TABLE.
    1165                 :             :      *
    1166                 :             :      * This needs to be before processing the partitioning clauses because
    1167                 :             :      * those could refer to generated columns.
    1168                 :             :      */
    1169         [ +  + ]:       42187 :     if (rawDefaults)
    1170                 :        1904 :         AddRelationNewConstraints(rel, rawDefaults, NIL,
    1171                 :             :                                   true, true, false, queryString);
    1172                 :             : 
    1173                 :             :     /*
    1174                 :             :      * Make column generation expressions visible for use by partitioning.
    1175                 :             :      */
    1176                 :       42055 :     CommandCounterIncrement();
    1177                 :             : 
    1178                 :             :     /* Process and store partition bound, if any. */
    1179         [ +  + ]:       42055 :     if (stmt->partbound)
    1180                 :             :     {
    1181                 :             :         PartitionBoundSpec *bound;
    1182                 :             :         ParseState *pstate;
    1183                 :        6516 :         Oid         parentId = linitial_oid(inheritOids),
    1184                 :             :                     defaultPartOid;
    1185                 :             :         Relation    parent,
    1186                 :        6516 :                     defaultRel = NULL;
    1187                 :             :         ParseNamespaceItem *nsitem;
    1188                 :             : 
    1189                 :             :         /* Already have strong enough lock on the parent */
    1190                 :        6516 :         parent = table_open(parentId, NoLock);
    1191                 :             : 
    1192                 :             :         /*
    1193                 :             :          * We are going to try to validate the partition bound specification
    1194                 :             :          * against the partition key of parentRel, so it better have one.
    1195                 :             :          */
    1196         [ +  + ]:        6516 :         if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    1197         [ +  - ]:          12 :             ereport(ERROR,
    1198                 :             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
    1199                 :             :                      errmsg("\"%s\" is not partitioned",
    1200                 :             :                             RelationGetRelationName(parent))));
    1201                 :             : 
    1202                 :             :         /*
    1203                 :             :          * The partition constraint of the default partition depends on the
    1204                 :             :          * partition bounds of every other partition. It is possible that
    1205                 :             :          * another backend might be about to execute a query on the default
    1206                 :             :          * partition table, and that the query relies on previously cached
    1207                 :             :          * default partition constraints. We must therefore take a table lock
    1208                 :             :          * strong enough to prevent all queries on the default partition from
    1209                 :             :          * proceeding until we commit and send out a shared-cache-inval notice
    1210                 :             :          * that will make them update their index lists.
    1211                 :             :          *
    1212                 :             :          * Order of locking: The relation being added won't be visible to
    1213                 :             :          * other backends until it is committed, hence here in
    1214                 :             :          * DefineRelation() the order of locking the default partition and the
    1215                 :             :          * relation being added does not matter. But at all other places we
    1216                 :             :          * need to lock the default relation before we lock the relation being
    1217                 :             :          * added or removed i.e. we should take the lock in same order at all
    1218                 :             :          * the places such that lock parent, lock default partition and then
    1219                 :             :          * lock the partition so as to avoid a deadlock.
    1220                 :             :          */
    1221                 :             :         defaultPartOid =
    1222                 :        6504 :             get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
    1223                 :             :                                                                    true));
    1224         [ +  + ]:        6504 :         if (OidIsValid(defaultPartOid))
    1225                 :         255 :             defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
    1226                 :             : 
    1227                 :             :         /* Transform the bound values */
    1228                 :        6504 :         pstate = make_parsestate(NULL);
    1229                 :        6504 :         pstate->p_sourcetext = queryString;
    1230                 :             : 
    1231                 :             :         /*
    1232                 :             :          * Add an nsitem containing this relation, so that transformExpr
    1233                 :             :          * called on partition bound expressions is able to report errors
    1234                 :             :          * using a proper context.
    1235                 :             :          */
    1236                 :        6504 :         nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
    1237                 :             :                                                NULL, false, false);
    1238                 :        6504 :         addNSItemToQuery(pstate, nsitem, false, true, true);
    1239                 :             : 
    1240                 :        6504 :         bound = transformPartitionBound(pstate, parent, stmt->partbound);
    1241                 :             : 
    1242                 :             :         /*
    1243                 :             :          * Check first that the new partition's bound is valid and does not
    1244                 :             :          * overlap with any of existing partitions of the parent.
    1245                 :             :          */
    1246                 :        6368 :         check_new_partition_bound(relname, parent, bound, pstate);
    1247                 :             : 
    1248                 :             :         /*
    1249                 :             :          * If the default partition exists, its partition constraints will
    1250                 :             :          * change after the addition of this new partition such that it won't
    1251                 :             :          * allow any row that qualifies for this new partition. So, check that
    1252                 :             :          * the existing data in the default partition satisfies the constraint
    1253                 :             :          * as it will exist after adding this partition.
    1254                 :             :          */
    1255         [ +  + ]:        6292 :         if (OidIsValid(defaultPartOid))
    1256                 :             :         {
    1257                 :         235 :             check_default_partition_contents(parent, defaultRel, bound);
    1258                 :             :             /* Keep the lock until commit. */
    1259                 :         223 :             table_close(defaultRel, NoLock);
    1260                 :             :         }
    1261                 :             : 
    1262                 :             :         /* Update the pg_class entry. */
    1263                 :        6280 :         StorePartitionBound(rel, parent, bound);
    1264                 :             : 
    1265                 :        6280 :         table_close(parent, NoLock);
    1266                 :             :     }
    1267                 :             : 
    1268                 :             :     /* Store inheritance information for new rel. */
    1269                 :       41819 :     StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
    1270                 :             : 
    1271                 :             :     /*
    1272                 :             :      * Process the partitioning specification (if any) and store the partition
    1273                 :             :      * key information into the catalog.
    1274                 :             :      */
    1275         [ +  + ]:       41819 :     if (partitioned)
    1276                 :             :     {
    1277                 :             :         ParseState *pstate;
    1278                 :             :         int         partnatts;
    1279                 :             :         AttrNumber  partattrs[PARTITION_MAX_KEYS];
    1280                 :             :         Oid         partopclass[PARTITION_MAX_KEYS];
    1281                 :             :         Oid         partcollation[PARTITION_MAX_KEYS];
    1282                 :        3668 :         List       *partexprs = NIL;
    1283                 :             : 
    1284                 :        3668 :         pstate = make_parsestate(NULL);
    1285                 :        3668 :         pstate->p_sourcetext = queryString;
    1286                 :             : 
    1287                 :        3668 :         partnatts = list_length(stmt->partspec->partParams);
    1288                 :             : 
    1289                 :             :         /* Protect fixed-size arrays here and in executor */
    1290         [ -  + ]:        3668 :         if (partnatts > PARTITION_MAX_KEYS)
    1291         [ #  # ]:           0 :             ereport(ERROR,
    1292                 :             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    1293                 :             :                      errmsg("cannot partition using more than %d columns",
    1294                 :             :                             PARTITION_MAX_KEYS)));
    1295                 :             : 
    1296                 :             :         /*
    1297                 :             :          * We need to transform the raw parsetrees corresponding to partition
    1298                 :             :          * expressions into executable expression trees.  Like column defaults
    1299                 :             :          * and CHECK constraints, we could not have done the transformation
    1300                 :             :          * earlier.
    1301                 :             :          */
    1302                 :        3668 :         stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
    1303                 :             : 
    1304                 :        3648 :         ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
    1305                 :             :                               partattrs, &partexprs, partopclass,
    1306                 :        3648 :                               partcollation, stmt->partspec->strategy);
    1307                 :             : 
    1308                 :        3560 :         StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
    1309                 :             :                           partexprs,
    1310                 :             :                           partopclass, partcollation);
    1311                 :             : 
    1312                 :             :         /* make it all visible */
    1313                 :        3556 :         CommandCounterIncrement();
    1314                 :             :     }
    1315                 :             : 
    1316                 :             :     /*
    1317                 :             :      * If we're creating a partition, create now all the indexes, triggers,
    1318                 :             :      * FKs defined in the parent.
    1319                 :             :      *
    1320                 :             :      * We can't do it earlier, because DefineIndex wants to know the partition
    1321                 :             :      * key which we just stored.
    1322                 :             :      */
    1323         [ +  + ]:       41707 :     if (stmt->partbound)
    1324                 :             :     {
    1325                 :        6276 :         Oid         parentId = linitial_oid(inheritOids);
    1326                 :             :         Relation    parent;
    1327                 :             :         List       *idxlist;
    1328                 :             :         ListCell   *cell;
    1329                 :             : 
    1330                 :             :         /* Already have strong enough lock on the parent */
    1331                 :        6276 :         parent = table_open(parentId, NoLock);
    1332                 :        6276 :         idxlist = RelationGetIndexList(parent);
    1333                 :             : 
    1334                 :             :         /*
    1335                 :             :          * For each index in the parent table, create one in the partition
    1336                 :             :          */
    1337   [ +  +  +  +  :        7366 :         foreach(cell, idxlist)
                   +  + ]
    1338                 :             :         {
    1339                 :        1102 :             Relation    idxRel = index_open(lfirst_oid(cell), AccessShareLock);
    1340                 :             :             AttrMap    *attmap;
    1341                 :             :             IndexStmt  *idxstmt;
    1342                 :             :             Oid         constraintOid;
    1343                 :             : 
    1344         [ +  + ]:        1102 :             if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    1345                 :             :             {
    1346         [ +  + ]:          24 :                 if (idxRel->rd_index->indisunique)
    1347         [ +  - ]:           8 :                     ereport(ERROR,
    1348                 :             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1349                 :             :                              errmsg("cannot create foreign partition of partitioned table \"%s\"",
    1350                 :             :                                     RelationGetRelationName(parent)),
    1351                 :             :                              errdetail("Table \"%s\" contains indexes that are unique.",
    1352                 :             :                                        RelationGetRelationName(parent))));
    1353                 :             :                 else
    1354                 :             :                 {
    1355                 :          16 :                     index_close(idxRel, AccessShareLock);
    1356                 :          16 :                     continue;
    1357                 :             :                 }
    1358                 :             :             }
    1359                 :             : 
    1360                 :        1078 :             attmap = build_attrmap_by_name(RelationGetDescr(rel),
    1361                 :             :                                            RelationGetDescr(parent),
    1362                 :             :                                            false);
    1363                 :             :             idxstmt =
    1364                 :        1078 :                 generateClonedIndexStmt(NULL, idxRel,
    1365                 :             :                                         attmap, &constraintOid);
    1366                 :        1078 :             DefineIndex(NULL,
    1367                 :             :                         RelationGetRelid(rel),
    1368                 :             :                         idxstmt,
    1369                 :             :                         InvalidOid,
    1370                 :             :                         RelationGetRelid(idxRel),
    1371                 :             :                         constraintOid,
    1372                 :             :                         -1,
    1373                 :             :                         false, false, false, false, false);
    1374                 :             : 
    1375                 :        1074 :             index_close(idxRel, AccessShareLock);
    1376                 :             :         }
    1377                 :             : 
    1378                 :        6264 :         list_free(idxlist);
    1379                 :             : 
    1380                 :             :         /*
    1381                 :             :          * If there are any row-level triggers, clone them to the new
    1382                 :             :          * partition.
    1383                 :             :          */
    1384         [ +  + ]:        6264 :         if (parent->trigdesc != NULL)
    1385                 :         260 :             CloneRowTriggersToPartition(parent, rel);
    1386                 :             : 
    1387                 :             :         /*
    1388                 :             :          * And foreign keys too.  Note that because we're freshly creating the
    1389                 :             :          * table, there is no need to verify these new constraints.
    1390                 :             :          */
    1391                 :        6264 :         CloneForeignKeyConstraints(NULL, parent, rel);
    1392                 :             : 
    1393                 :        6264 :         table_close(parent, NoLock);
    1394                 :             :     }
    1395                 :             : 
    1396                 :             :     /*
    1397                 :             :      * Now add any newly specified CHECK constraints to the new relation. Same
    1398                 :             :      * as for defaults above, but these need to come after partitioning is set
    1399                 :             :      * up.  We save the constraint names that were used, to avoid dupes below.
    1400                 :             :      */
    1401         [ +  + ]:       41695 :     if (stmt->constraints)
    1402                 :             :     {
    1403                 :             :         List       *conlist;
    1404                 :             : 
    1405                 :         526 :         conlist = AddRelationNewConstraints(rel, NIL, stmt->constraints,
    1406                 :             :                                             true, true, false, queryString);
    1407   [ +  +  +  +  :        1581 :         foreach_ptr(CookedConstraint, cons, conlist)
                   +  + ]
    1408                 :             :         {
    1409         [ +  - ]:         577 :             if (cons->name != NULL)
    1410                 :         577 :                 connames = lappend(connames, cons->name);
    1411                 :             :         }
    1412                 :             :     }
    1413                 :             : 
    1414                 :             :     /*
    1415                 :             :      * Finally, merge the not-null constraints that are declared directly with
    1416                 :             :      * those that come from parent relations (making sure to count inheritance
    1417                 :             :      * appropriately for each), create them, and set the attnotnull flag on
    1418                 :             :      * columns that don't yet have it.
    1419                 :             :      */
    1420                 :       41671 :     nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
    1421                 :             :                                            old_notnulls, connames);
    1422   [ +  +  +  +  :       93555 :     foreach_int(attrnum, nncols)
                   +  + ]
    1423                 :       10317 :         set_attnotnull(NULL, rel, attrnum, false);
    1424                 :             : 
    1425                 :       41619 :     ObjectAddressSet(address, RelationRelationId, relationId);
    1426                 :             : 
    1427                 :             :     /*
    1428                 :             :      * Clean up.  We keep lock on new relation (although it shouldn't be
    1429                 :             :      * visible to anyone else anyway, until commit).
    1430                 :             :      */
    1431                 :       41619 :     relation_close(rel, NoLock);
    1432                 :             : 
    1433                 :       41619 :     return address;
    1434                 :             : }
    1435                 :             : 
    1436                 :             : /*
    1437                 :             :  * BuildDescForRelation
    1438                 :             :  *
    1439                 :             :  * Given a list of ColumnDef nodes, build a TupleDesc.
    1440                 :             :  *
    1441                 :             :  * Note: This is only for the limited purpose of table and view creation.  Not
    1442                 :             :  * everything is filled in.  A real tuple descriptor should be obtained from
    1443                 :             :  * the relcache.
    1444                 :             :  */
    1445                 :             : TupleDesc
    1446                 :       44932 : BuildDescForRelation(const List *columns)
    1447                 :             : {
    1448                 :             :     int         natts;
    1449                 :             :     AttrNumber  attnum;
    1450                 :             :     ListCell   *l;
    1451                 :             :     TupleDesc   desc;
    1452                 :             :     char       *attname;
    1453                 :             :     Oid         atttypid;
    1454                 :             :     int32       atttypmod;
    1455                 :             :     Oid         attcollation;
    1456                 :             :     int         attdim;
    1457                 :             : 
    1458                 :             :     /*
    1459                 :             :      * allocate a new tuple descriptor
    1460                 :             :      */
    1461                 :       44932 :     natts = list_length(columns);
    1462                 :       44932 :     desc = CreateTemplateTupleDesc(natts);
    1463                 :             : 
    1464                 :       44932 :     attnum = 0;
    1465                 :             : 
    1466   [ +  +  +  +  :      216530 :     foreach(l, columns)
                   +  + ]
    1467                 :             :     {
    1468                 :      171638 :         ColumnDef  *entry = lfirst(l);
    1469                 :             :         AclResult   aclresult;
    1470                 :             :         Form_pg_attribute att;
    1471                 :             : 
    1472                 :             :         /*
    1473                 :             :          * for each entry in the list, get the name and type information from
    1474                 :             :          * the list and have TupleDescInitEntry fill in the attribute
    1475                 :             :          * information we need.
    1476                 :             :          */
    1477                 :      171638 :         attnum++;
    1478                 :             : 
    1479                 :      171638 :         attname = entry->colname;
    1480                 :      171638 :         typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
    1481                 :             : 
    1482                 :      171638 :         aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
    1483         [ +  + ]:      171638 :         if (aclresult != ACLCHECK_OK)
    1484                 :          28 :             aclcheck_error_type(aclresult, atttypid);
    1485                 :             : 
    1486                 :      171610 :         attcollation = GetColumnDefCollation(NULL, entry, atttypid);
    1487                 :      171610 :         attdim = list_length(entry->typeName->arrayBounds);
    1488         [ -  + ]:      171610 :         if (attdim > PG_INT16_MAX)
    1489         [ #  # ]:           0 :             ereport(ERROR,
    1490                 :             :                     errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1491                 :             :                     errmsg("too many array dimensions"));
    1492                 :             : 
    1493         [ -  + ]:      171610 :         if (entry->typeName->setof)
    1494         [ #  # ]:           0 :             ereport(ERROR,
    1495                 :             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    1496                 :             :                      errmsg("column \"%s\" cannot be declared SETOF",
    1497                 :             :                             attname)));
    1498                 :             : 
    1499                 :      171610 :         TupleDescInitEntry(desc, attnum, attname,
    1500                 :             :                            atttypid, atttypmod, attdim);
    1501                 :      171610 :         att = TupleDescAttr(desc, attnum - 1);
    1502                 :             : 
    1503                 :             :         /* Override TupleDescInitEntry's settings as requested */
    1504                 :      171610 :         TupleDescInitEntryCollation(desc, attnum, attcollation);
    1505                 :             : 
    1506                 :             :         /* Fill in additional stuff not handled by TupleDescInitEntry */
    1507                 :      171610 :         att->attnotnull = entry->is_not_null;
    1508                 :      171610 :         att->attislocal = entry->is_local;
    1509                 :      171610 :         att->attinhcount = entry->inhcount;
    1510                 :      171610 :         att->attidentity = entry->identity;
    1511                 :      171610 :         att->attgenerated = entry->generated;
    1512                 :      171610 :         att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
    1513         [ +  + ]:      171602 :         if (entry->storage)
    1514                 :       17017 :             att->attstorage = entry->storage;
    1515         [ +  + ]:      154585 :         else if (entry->storage_name)
    1516                 :          39 :             att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
    1517                 :             : 
    1518                 :      171598 :         populate_compact_attribute(desc, attnum - 1);
    1519                 :             :     }
    1520                 :             : 
    1521                 :       44892 :     TupleDescFinalize(desc);
    1522                 :             : 
    1523                 :       44892 :     return desc;
    1524                 :             : }
    1525                 :             : 
    1526                 :             : /*
    1527                 :             :  * Emit the right error or warning message for a "DROP" command issued on a
    1528                 :             :  * non-existent relation
    1529                 :             :  */
    1530                 :             : static void
    1531                 :         641 : DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
    1532                 :             : {
    1533                 :             :     const struct dropmsgstrings *rentry;
    1534                 :             : 
    1535   [ +  +  +  + ]:         721 :     if (rel->schemaname != NULL &&
    1536                 :          80 :         !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
    1537                 :             :     {
    1538         [ -  + ]:          28 :         if (!missing_ok)
    1539                 :             :         {
    1540         [ #  # ]:           0 :             ereport(ERROR,
    1541                 :             :                     (errcode(ERRCODE_UNDEFINED_SCHEMA),
    1542                 :             :                      errmsg("schema \"%s\" does not exist", rel->schemaname)));
    1543                 :             :         }
    1544                 :             :         else
    1545                 :             :         {
    1546         [ +  - ]:          28 :             ereport(NOTICE,
    1547                 :             :                     (errmsg("schema \"%s\" does not exist, skipping",
    1548                 :             :                             rel->schemaname)));
    1549                 :             :         }
    1550                 :          28 :         return;
    1551                 :             :     }
    1552                 :             : 
    1553         [ +  - ]:         898 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1554                 :             :     {
    1555         [ +  + ]:         898 :         if (rentry->kind == rightkind)
    1556                 :             :         {
    1557         [ +  + ]:         613 :             if (!missing_ok)
    1558                 :             :             {
    1559         [ +  - ]:          90 :                 ereport(ERROR,
    1560                 :             :                         (errcode(rentry->nonexistent_code),
    1561                 :             :                          errmsg(rentry->nonexistent_msg, rel->relname)));
    1562                 :             :             }
    1563                 :             :             else
    1564                 :             :             {
    1565         [ +  - ]:         523 :                 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
    1566                 :         523 :                 break;
    1567                 :             :             }
    1568                 :             :         }
    1569                 :             :     }
    1570                 :             : 
    1571                 :             :     Assert(rentry->kind != '\0');    /* Should be impossible */
    1572                 :             : }
    1573                 :             : 
    1574                 :             : /*
    1575                 :             :  * Emit the right error message for a "DROP" command issued on a
    1576                 :             :  * relation of the wrong type
    1577                 :             :  */
    1578                 :             : static void
    1579                 :           4 : DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
    1580                 :             : {
    1581                 :             :     const struct dropmsgstrings *rentry;
    1582                 :             :     const struct dropmsgstrings *wentry;
    1583                 :             : 
    1584         [ +  - ]:           4 :     for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
    1585         [ +  - ]:           4 :         if (rentry->kind == rightkind)
    1586                 :           4 :             break;
    1587                 :             :     Assert(rentry->kind != '\0');
    1588                 :             : 
    1589         [ +  - ]:          40 :     for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
    1590         [ +  + ]:          40 :         if (wentry->kind == wrongkind)
    1591                 :           4 :             break;
    1592                 :             :     /* wrongkind could be something we don't have in our table... */
    1593                 :             : 
    1594   [ +  -  +  - ]:           4 :     ereport(ERROR,
    1595                 :             :             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    1596                 :             :              errmsg(rentry->nota_msg, relname),
    1597                 :             :              (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
    1598                 :             : }
    1599                 :             : 
    1600                 :             : /*
    1601                 :             :  * RemoveRelations
    1602                 :             :  *      Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
    1603                 :             :  *      DROP MATERIALIZED VIEW, DROP FOREIGN TABLE, DROP PROPERTY GRAPH
    1604                 :             :  */
    1605                 :             : void
    1606                 :       12001 : RemoveRelations(DropStmt *drop)
    1607                 :             : {
    1608                 :             :     ObjectAddresses *objects;
    1609                 :             :     char        relkind;
    1610                 :             :     ListCell   *cell;
    1611                 :       12001 :     int         flags = 0;
    1612                 :       12001 :     LOCKMODE    lockmode = AccessExclusiveLock;
    1613                 :             : 
    1614                 :             :     /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
    1615         [ +  + ]:       12001 :     if (drop->concurrent)
    1616                 :             :     {
    1617                 :             :         /*
    1618                 :             :          * Note that for temporary relations this lock may get upgraded later
    1619                 :             :          * on, but as no other session can access a temporary relation, this
    1620                 :             :          * is actually fine.
    1621                 :             :          */
    1622                 :         111 :         lockmode = ShareUpdateExclusiveLock;
    1623                 :             :         Assert(drop->removeType == OBJECT_INDEX);
    1624         [ +  + ]:         111 :         if (list_length(drop->objects) != 1)
    1625         [ +  - ]:           4 :             ereport(ERROR,
    1626                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1627                 :             :                      errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
    1628         [ -  + ]:         107 :         if (drop->behavior == DROP_CASCADE)
    1629         [ #  # ]:           0 :             ereport(ERROR,
    1630                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1631                 :             :                      errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
    1632                 :             :     }
    1633                 :             : 
    1634                 :             :     /*
    1635                 :             :      * First we identify all the relations, then we delete them in a single
    1636                 :             :      * performMultipleDeletions() call.  This is to avoid unwanted DROP
    1637                 :             :      * RESTRICT errors if one of the relations depends on another.
    1638                 :             :      */
    1639                 :             : 
    1640                 :             :     /* Determine required relkind */
    1641   [ +  +  +  +  :       11997 :     switch (drop->removeType)
             +  +  +  - ]
    1642                 :             :     {
    1643                 :       10374 :         case OBJECT_TABLE:
    1644                 :       10374 :             relkind = RELKIND_RELATION;
    1645                 :       10374 :             break;
    1646                 :             : 
    1647                 :         557 :         case OBJECT_INDEX:
    1648                 :         557 :             relkind = RELKIND_INDEX;
    1649                 :         557 :             break;
    1650                 :             : 
    1651                 :         121 :         case OBJECT_SEQUENCE:
    1652                 :         121 :             relkind = RELKIND_SEQUENCE;
    1653                 :         121 :             break;
    1654                 :             : 
    1655                 :         704 :         case OBJECT_VIEW:
    1656                 :         704 :             relkind = RELKIND_VIEW;
    1657                 :         704 :             break;
    1658                 :             : 
    1659                 :          83 :         case OBJECT_MATVIEW:
    1660                 :          83 :             relkind = RELKIND_MATVIEW;
    1661                 :          83 :             break;
    1662                 :             : 
    1663                 :         108 :         case OBJECT_FOREIGN_TABLE:
    1664                 :         108 :             relkind = RELKIND_FOREIGN_TABLE;
    1665                 :         108 :             break;
    1666                 :             : 
    1667                 :          50 :         case OBJECT_PROPGRAPH:
    1668                 :          50 :             relkind = RELKIND_PROPGRAPH;
    1669                 :          50 :             break;
    1670                 :             : 
    1671                 :           0 :         default:
    1672         [ #  # ]:           0 :             elog(ERROR, "unrecognized drop object type: %d",
    1673                 :             :                  (int) drop->removeType);
    1674                 :             :             relkind = 0;        /* keep compiler quiet */
    1675                 :             :             break;
    1676                 :             :     }
    1677                 :             : 
    1678                 :             :     /* Lock and validate each relation; build a list of object addresses */
    1679                 :       11997 :     objects = new_object_addresses();
    1680                 :             : 
    1681   [ +  -  +  +  :       26431 :     foreach(cell, drop->objects)
                   +  + ]
    1682                 :             :     {
    1683                 :       14549 :         RangeVar   *rel = makeRangeVarFromNameList((List *) lfirst(cell));
    1684                 :             :         Oid         relOid;
    1685                 :             :         ObjectAddress obj;
    1686                 :             :         struct DropRelationCallbackState state;
    1687                 :             : 
    1688                 :             :         /*
    1689                 :             :          * These next few steps are a great deal like relation_openrv, but we
    1690                 :             :          * don't bother building a relcache entry since we don't need it.
    1691                 :             :          *
    1692                 :             :          * Check for shared-cache-inval messages before trying to access the
    1693                 :             :          * relation.  This is needed to cover the case where the name
    1694                 :             :          * identifies a rel that has been dropped and recreated since the
    1695                 :             :          * start of our transaction: if we don't flush the old syscache entry,
    1696                 :             :          * then we'll latch onto that entry and suffer an error later.
    1697                 :             :          */
    1698                 :       14549 :         AcceptInvalidationMessages();
    1699                 :             : 
    1700                 :             :         /* Look up the appropriate relation using namespace search. */
    1701                 :       14549 :         state.expected_relkind = relkind;
    1702                 :       29098 :         state.heap_lockmode = drop->concurrent ?
    1703         [ +  + ]:       14549 :             ShareUpdateExclusiveLock : AccessExclusiveLock;
    1704                 :             :         /* We must initialize these fields to show that no locks are held: */
    1705                 :       14549 :         state.heapOid = InvalidOid;
    1706                 :       14549 :         state.partParentOid = InvalidOid;
    1707                 :             : 
    1708                 :       14549 :         relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
    1709                 :             :                                           RangeVarCallbackForDropRelation,
    1710                 :             :                                           &state);
    1711                 :             : 
    1712                 :             :         /* Not there? */
    1713         [ +  + ]:       14528 :         if (!OidIsValid(relOid))
    1714                 :             :         {
    1715                 :         641 :             DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
    1716                 :         551 :             continue;
    1717                 :             :         }
    1718                 :             : 
    1719                 :             :         /*
    1720                 :             :          * Decide if concurrent mode needs to be used here or not.  The
    1721                 :             :          * callback retrieved the rel's persistence for us.
    1722                 :             :          */
    1723         [ +  + ]:       13887 :         if (drop->concurrent &&
    1724         [ +  + ]:         103 :             state.actual_relpersistence != RELPERSISTENCE_TEMP)
    1725                 :             :         {
    1726                 :             :             Assert(list_length(drop->objects) == 1 &&
    1727                 :             :                    drop->removeType == OBJECT_INDEX);
    1728                 :          91 :             flags |= PERFORM_DELETION_CONCURRENTLY;
    1729                 :             :         }
    1730                 :             : 
    1731                 :             :         /*
    1732                 :             :          * Concurrent index drop cannot be used with partitioned indexes,
    1733                 :             :          * either.
    1734                 :             :          */
    1735         [ +  + ]:       13887 :         if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
    1736         [ +  + ]:          91 :             state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1737         [ +  - ]:           4 :             ereport(ERROR,
    1738                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    1739                 :             :                      errmsg("cannot drop partitioned index \"%s\" concurrently",
    1740                 :             :                             rel->relname)));
    1741                 :             : 
    1742                 :             :         /*
    1743                 :             :          * If we're told to drop a partitioned index, we must acquire lock on
    1744                 :             :          * all the children of its parent partitioned table before proceeding.
    1745                 :             :          * Otherwise we'd try to lock the child index partitions before their
    1746                 :             :          * tables, leading to potential deadlock against other sessions that
    1747                 :             :          * will lock those objects in the other order.
    1748                 :             :          */
    1749         [ +  + ]:       13883 :         if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
    1750                 :          50 :             (void) find_all_inheritors(state.heapOid,
    1751                 :             :                                        state.heap_lockmode,
    1752                 :             :                                        NULL);
    1753                 :             : 
    1754                 :             :         /* OK, we're ready to delete this one */
    1755                 :       13883 :         obj.classId = RelationRelationId;
    1756                 :       13883 :         obj.objectId = relOid;
    1757                 :       13883 :         obj.objectSubId = 0;
    1758                 :             : 
    1759                 :       13883 :         add_exact_object_address(&obj, objects);
    1760                 :             :     }
    1761                 :             : 
    1762                 :       11882 :     performMultipleDeletions(objects, drop->behavior, flags);
    1763                 :             : 
    1764                 :       11779 :     free_object_addresses(objects);
    1765                 :       11779 : }
    1766                 :             : 
    1767                 :             : /*
    1768                 :             :  * Before acquiring a table lock, check whether we have sufficient rights.
    1769                 :             :  * In the case of DROP INDEX, also try to lock the table before the index.
    1770                 :             :  * Also, if the table to be dropped is a partition, we try to lock the parent
    1771                 :             :  * first.
    1772                 :             :  */
    1773                 :             : static void
    1774                 :       14775 : RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
    1775                 :             :                                 void *arg)
    1776                 :             : {
    1777                 :             :     HeapTuple   tuple;
    1778                 :             :     struct DropRelationCallbackState *state;
    1779                 :             :     char        expected_relkind;
    1780                 :             :     bool        is_partition;
    1781                 :             :     Form_pg_class classform;
    1782                 :             :     LOCKMODE    heap_lockmode;
    1783                 :       14775 :     bool        invalid_system_index = false;
    1784                 :             : 
    1785                 :       14775 :     state = (struct DropRelationCallbackState *) arg;
    1786                 :       14775 :     heap_lockmode = state->heap_lockmode;
    1787                 :             : 
    1788                 :             :     /*
    1789                 :             :      * If we previously locked some other index's heap, and the name we're
    1790                 :             :      * looking up no longer refers to that relation, release the now-useless
    1791                 :             :      * lock.
    1792                 :             :      */
    1793   [ +  +  -  + ]:       14775 :     if (relOid != oldRelOid && OidIsValid(state->heapOid))
    1794                 :             :     {
    1795                 :           0 :         UnlockRelationOid(state->heapOid, heap_lockmode);
    1796                 :           0 :         state->heapOid = InvalidOid;
    1797                 :             :     }
    1798                 :             : 
    1799                 :             :     /*
    1800                 :             :      * Similarly, if we previously locked some other partition's heap, and the
    1801                 :             :      * name we're looking up no longer refers to that relation, release the
    1802                 :             :      * now-useless lock.
    1803                 :             :      */
    1804   [ +  +  -  + ]:       14775 :     if (relOid != oldRelOid && OidIsValid(state->partParentOid))
    1805                 :             :     {
    1806                 :           0 :         UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
    1807                 :           0 :         state->partParentOid = InvalidOid;
    1808                 :             :     }
    1809                 :             : 
    1810                 :             :     /* Didn't find a relation, so no need for locking or permission checks. */
    1811         [ +  + ]:       14775 :     if (!OidIsValid(relOid))
    1812                 :         643 :         return;
    1813                 :             : 
    1814                 :       14132 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
    1815         [ -  + ]:       14132 :     if (!HeapTupleIsValid(tuple))
    1816                 :           0 :         return;                 /* concurrently dropped, so nothing to do */
    1817                 :       14132 :     classform = (Form_pg_class) GETSTRUCT(tuple);
    1818                 :       14132 :     is_partition = classform->relispartition;
    1819                 :             : 
    1820                 :             :     /* Pass back some data to save lookups in RemoveRelations */
    1821                 :       14132 :     state->actual_relkind = classform->relkind;
    1822                 :       14132 :     state->actual_relpersistence = classform->relpersistence;
    1823                 :             : 
    1824                 :             :     /*
    1825                 :             :      * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
    1826                 :             :      * but RemoveRelations() can only pass one relkind for a given relation.
    1827                 :             :      * It chooses RELKIND_RELATION for both regular and partitioned tables.
    1828                 :             :      * That means we must be careful before giving the wrong type error when
    1829                 :             :      * the relation is RELKIND_PARTITIONED_TABLE.  An equivalent problem
    1830                 :             :      * exists with indexes.
    1831                 :             :      */
    1832         [ +  + ]:       14132 :     if (classform->relkind == RELKIND_PARTITIONED_TABLE)
    1833                 :        2261 :         expected_relkind = RELKIND_RELATION;
    1834         [ +  + ]:       11871 :     else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
    1835                 :          56 :         expected_relkind = RELKIND_INDEX;
    1836                 :             :     else
    1837                 :       11815 :         expected_relkind = classform->relkind;
    1838                 :             : 
    1839         [ +  + ]:       14132 :     if (state->expected_relkind != expected_relkind)
    1840                 :           4 :         DropErrorMsgWrongType(rel->relname, classform->relkind,
    1841                 :           4 :                               state->expected_relkind);
    1842                 :             : 
    1843                 :             :     /* Allow DROP to either table owner or schema owner */
    1844         [ +  + ]:       14128 :     if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
    1845         [ +  - ]:          12 :         !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
    1846                 :          12 :         aclcheck_error(ACLCHECK_NOT_OWNER,
    1847                 :          12 :                        get_relkind_objtype(classform->relkind),
    1848                 :          12 :                        rel->relname);
    1849                 :             : 
    1850                 :             :     /*
    1851                 :             :      * Check the case of a system index that might have been invalidated by a
    1852                 :             :      * failed concurrent process and allow its drop. For the time being, this
    1853                 :             :      * only concerns indexes of toast relations that became invalid during a
    1854                 :             :      * REINDEX CONCURRENTLY process.
    1855                 :             :      */
    1856   [ +  +  -  + ]:       14116 :     if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
    1857                 :             :     {
    1858                 :             :         HeapTuple   locTuple;
    1859                 :             :         Form_pg_index indexform;
    1860                 :             :         bool        indisvalid;
    1861                 :             : 
    1862                 :           0 :         locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
    1863         [ #  # ]:           0 :         if (!HeapTupleIsValid(locTuple))
    1864                 :             :         {
    1865                 :           0 :             ReleaseSysCache(tuple);
    1866                 :           0 :             return;
    1867                 :             :         }
    1868                 :             : 
    1869                 :           0 :         indexform = (Form_pg_index) GETSTRUCT(locTuple);
    1870                 :           0 :         indisvalid = indexform->indisvalid;
    1871                 :           0 :         ReleaseSysCache(locTuple);
    1872                 :             : 
    1873                 :             :         /* Mark object as being an invalid index of system catalogs */
    1874         [ #  # ]:           0 :         if (!indisvalid)
    1875                 :           0 :             invalid_system_index = true;
    1876                 :             :     }
    1877                 :             : 
    1878                 :             :     /* In the case of an invalid index, it is fine to bypass this check */
    1879   [ +  -  +  +  :       14116 :     if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
                   +  + ]
    1880         [ +  - ]:           5 :         ereport(ERROR,
    1881                 :             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    1882                 :             :                  errmsg("permission denied: \"%s\" is a system catalog",
    1883                 :             :                         rel->relname)));
    1884                 :             : 
    1885                 :       14111 :     ReleaseSysCache(tuple);
    1886                 :             : 
    1887                 :             :     /*
    1888                 :             :      * In DROP INDEX, attempt to acquire lock on the parent table before
    1889                 :             :      * locking the index.  index_drop() will need this anyway, and since
    1890                 :             :      * regular queries lock tables before their indexes, we risk deadlock if
    1891                 :             :      * we do it the other way around.  No error if we don't find a pg_index
    1892                 :             :      * entry, though --- the relation may have been dropped.  Note that this
    1893                 :             :      * code will execute for either plain or partitioned indexes.
    1894                 :             :      */
    1895   [ +  +  +  + ]:       14111 :     if (expected_relkind == RELKIND_INDEX &&
    1896                 :             :         relOid != oldRelOid)
    1897                 :             :     {
    1898                 :         549 :         state->heapOid = IndexGetRelation(relOid, true);
    1899         [ +  - ]:         549 :         if (OidIsValid(state->heapOid))
    1900                 :         549 :             LockRelationOid(state->heapOid, heap_lockmode);
    1901                 :             :     }
    1902                 :             : 
    1903                 :             :     /*
    1904                 :             :      * Similarly, if the relation is a partition, we must acquire lock on its
    1905                 :             :      * parent before locking the partition.  That's because queries lock the
    1906                 :             :      * parent before its partitions, so we risk deadlock if we do it the other
    1907                 :             :      * way around.
    1908                 :             :      */
    1909   [ +  +  +  + ]:       14111 :     if (is_partition && relOid != oldRelOid)
    1910                 :             :     {
    1911                 :         397 :         state->partParentOid = get_partition_parent(relOid, true);
    1912         [ +  - ]:         397 :         if (OidIsValid(state->partParentOid))
    1913                 :         397 :             LockRelationOid(state->partParentOid, AccessExclusiveLock);
    1914                 :             :     }
    1915                 :             : }
    1916                 :             : 
    1917                 :             : /*
    1918                 :             :  * ExecuteTruncate
    1919                 :             :  *      Executes a TRUNCATE command.
    1920                 :             :  *
    1921                 :             :  * This is a multi-relation truncate.  We first open and grab exclusive
    1922                 :             :  * lock on all relations involved, checking permissions and otherwise
    1923                 :             :  * verifying that the relation is OK for truncation.  Note that if relations
    1924                 :             :  * are foreign tables, at this stage, we have not yet checked that their
    1925                 :             :  * foreign data in external data sources are OK for truncation.  These are
    1926                 :             :  * checked when foreign data are actually truncated later.  In CASCADE mode,
    1927                 :             :  * relations having FK references to the targeted relations are automatically
    1928                 :             :  * added to the group; in RESTRICT mode, we check that all FK references are
    1929                 :             :  * internal to the group that's being truncated.  Finally all the relations
    1930                 :             :  * are truncated and reindexed.
    1931                 :             :  */
    1932                 :             : void
    1933                 :        1186 : ExecuteTruncate(TruncateStmt *stmt)
    1934                 :             : {
    1935                 :        1186 :     List       *rels = NIL;
    1936                 :        1186 :     List       *relids = NIL;
    1937                 :        1186 :     List       *relids_logged = NIL;
    1938                 :             :     ListCell   *cell;
    1939                 :             : 
    1940                 :             :     /*
    1941                 :             :      * Open, exclusive-lock, and check all the explicitly-specified relations
    1942                 :             :      */
    1943   [ +  -  +  +  :        2532 :     foreach(cell, stmt->relations)
                   +  + ]
    1944                 :             :     {
    1945                 :        1382 :         RangeVar   *rv = lfirst(cell);
    1946                 :             :         Relation    rel;
    1947                 :        1382 :         bool        recurse = rv->inh;
    1948                 :             :         Oid         myrelid;
    1949                 :        1382 :         LOCKMODE    lockmode = AccessExclusiveLock;
    1950                 :             : 
    1951                 :        1382 :         myrelid = RangeVarGetRelidExtended(rv, lockmode,
    1952                 :             :                                            0, RangeVarCallbackForTruncate,
    1953                 :             :                                            NULL);
    1954                 :             : 
    1955                 :             :         /* don't throw error for "TRUNCATE foo, foo" */
    1956         [ +  + ]:        1359 :         if (list_member_oid(relids, myrelid))
    1957                 :           1 :             continue;
    1958                 :             : 
    1959                 :             :         /* open the relation, we already hold a lock on it */
    1960                 :        1358 :         rel = table_open(myrelid, NoLock);
    1961                 :             : 
    1962                 :             :         /*
    1963                 :             :          * RangeVarGetRelidExtended() has done most checks with its callback,
    1964                 :             :          * but other checks with the now-opened Relation remain.
    1965                 :             :          */
    1966                 :        1358 :         truncate_check_activity(rel);
    1967                 :             : 
    1968                 :        1353 :         rels = lappend(rels, rel);
    1969                 :        1353 :         relids = lappend_oid(relids, myrelid);
    1970                 :             : 
    1971                 :             :         /* Log this relation only if needed for logical decoding */
    1972   [ +  +  -  +  :        1353 :         if (RelationIsLogicallyLogged(rel))
          +  -  -  +  -  
          -  -  -  +  -  
                   +  + ]
    1973                 :          39 :             relids_logged = lappend_oid(relids_logged, myrelid);
    1974                 :             : 
    1975         [ +  + ]:        1353 :         if (recurse)
    1976                 :             :         {
    1977                 :             :             ListCell   *child;
    1978                 :             :             List       *children;
    1979                 :             : 
    1980                 :        1317 :             children = find_all_inheritors(myrelid, lockmode, NULL);
    1981                 :             : 
    1982   [ +  -  +  +  :        3866 :             foreach(child, children)
                   +  + ]
    1983                 :             :             {
    1984                 :        2549 :                 Oid         childrelid = lfirst_oid(child);
    1985                 :             : 
    1986         [ +  + ]:        2549 :                 if (list_member_oid(relids, childrelid))
    1987                 :        1329 :                     continue;
    1988                 :             : 
    1989                 :             :                 /* find_all_inheritors already got lock */
    1990                 :        1220 :                 rel = table_open(childrelid, NoLock);
    1991                 :             : 
    1992                 :             :                 /*
    1993                 :             :                  * It is possible that the parent table has children that are
    1994                 :             :                  * temp tables of other backends.  We cannot safely access
    1995                 :             :                  * such tables (because of buffering issues), and the best
    1996                 :             :                  * thing to do is to silently ignore them.  Note that this
    1997                 :             :                  * check is the same as one of the checks done in
    1998                 :             :                  * truncate_check_activity() called below, still it is kept
    1999                 :             :                  * here for simplicity.
    2000                 :             :                  */
    2001   [ +  +  +  + ]:        1220 :                 if (RELATION_IS_OTHER_TEMP(rel))
    2002                 :             :                 {
    2003                 :           4 :                     table_close(rel, lockmode);
    2004                 :           4 :                     continue;
    2005                 :             :                 }
    2006                 :             : 
    2007                 :             :                 /*
    2008                 :             :                  * Inherited TRUNCATE commands perform access permission
    2009                 :             :                  * checks on the parent table only. So we skip checking the
    2010                 :             :                  * children's permissions and don't call
    2011                 :             :                  * truncate_check_perms() here.
    2012                 :             :                  */
    2013                 :        1216 :                 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
    2014                 :        1216 :                 truncate_check_activity(rel);
    2015                 :             : 
    2016                 :        1216 :                 rels = lappend(rels, rel);
    2017                 :        1216 :                 relids = lappend_oid(relids, childrelid);
    2018                 :             : 
    2019                 :             :                 /* Log this relation only if needed for logical decoding */
    2020   [ +  +  -  +  :        1216 :                 if (RelationIsLogicallyLogged(rel))
          +  -  -  +  -  
          -  -  -  +  -  
                   +  - ]
    2021                 :          11 :                     relids_logged = lappend_oid(relids_logged, childrelid);
    2022                 :             :             }
    2023                 :             :         }
    2024         [ +  + ]:          36 :         else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    2025         [ +  - ]:           8 :             ereport(ERROR,
    2026                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2027                 :             :                      errmsg("cannot truncate only a partitioned table"),
    2028                 :             :                      errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
    2029                 :             :     }
    2030                 :             : 
    2031                 :        1150 :     ExecuteTruncateGuts(rels, relids, relids_logged,
    2032                 :        1150 :                         stmt->behavior, stmt->restart_seqs, false);
    2033                 :             : 
    2034                 :             :     /* And close the rels */
    2035   [ +  -  +  +  :        3557 :     foreach(cell, rels)
                   +  + ]
    2036                 :             :     {
    2037                 :        2460 :         Relation    rel = (Relation) lfirst(cell);
    2038                 :             : 
    2039                 :        2460 :         table_close(rel, NoLock);
    2040                 :             :     }
    2041                 :        1097 : }
    2042                 :             : 
    2043                 :             : /*
    2044                 :             :  * ExecuteTruncateGuts
    2045                 :             :  *
    2046                 :             :  * Internal implementation of TRUNCATE.  This is called by the actual TRUNCATE
    2047                 :             :  * command (see above) as well as replication subscribers that execute a
    2048                 :             :  * replicated TRUNCATE action.
    2049                 :             :  *
    2050                 :             :  * explicit_rels is the list of Relations to truncate that the command
    2051                 :             :  * specified.  relids is the list of Oids corresponding to explicit_rels.
    2052                 :             :  * relids_logged is the list of Oids (a subset of relids) that require
    2053                 :             :  * WAL-logging.  This is all a bit redundant, but the existing callers have
    2054                 :             :  * this information handy in this form.
    2055                 :             :  */
    2056                 :             : void
    2057                 :        1170 : ExecuteTruncateGuts(List *explicit_rels,
    2058                 :             :                     List *relids,
    2059                 :             :                     List *relids_logged,
    2060                 :             :                     DropBehavior behavior, bool restart_seqs,
    2061                 :             :                     bool run_as_table_owner)
    2062                 :             : {
    2063                 :             :     List       *rels;
    2064                 :        1170 :     List       *seq_relids = NIL;
    2065                 :        1170 :     HTAB       *ft_htab = NULL;
    2066                 :             :     EState     *estate;
    2067                 :             :     ResultRelInfo *resultRelInfos;
    2068                 :             :     ResultRelInfo *resultRelInfo;
    2069                 :             :     SubTransactionId mySubid;
    2070                 :             :     ListCell   *cell;
    2071                 :             :     Oid        *logrelids;
    2072                 :             : 
    2073                 :             :     /*
    2074                 :             :      * Check the explicitly-specified relations.
    2075                 :             :      *
    2076                 :             :      * In CASCADE mode, suck in all referencing relations as well.  This
    2077                 :             :      * requires multiple iterations to find indirectly-dependent relations. At
    2078                 :             :      * each phase, we need to exclusive-lock new rels before looking for their
    2079                 :             :      * dependencies, else we might miss something.  Also, we check each rel as
    2080                 :             :      * soon as we open it, to avoid a faux pas such as holding lock for a long
    2081                 :             :      * time on a rel we have no permissions for.
    2082                 :             :      */
    2083                 :        1170 :     rels = list_copy(explicit_rels);
    2084         [ +  + ]:        1170 :     if (behavior == DROP_CASCADE)
    2085                 :             :     {
    2086                 :             :         for (;;)
    2087                 :          26 :         {
    2088                 :             :             List       *newrelids;
    2089                 :             : 
    2090                 :          51 :             newrelids = heap_truncate_find_FKs(relids);
    2091         [ +  + ]:          51 :             if (newrelids == NIL)
    2092                 :          25 :                 break;          /* nothing else to add */
    2093                 :             : 
    2094   [ +  -  +  +  :          88 :             foreach(cell, newrelids)
                   +  + ]
    2095                 :             :             {
    2096                 :          62 :                 Oid         relid = lfirst_oid(cell);
    2097                 :             :                 Relation    rel;
    2098                 :             : 
    2099                 :          62 :                 rel = table_open(relid, AccessExclusiveLock);
    2100         [ +  - ]:          62 :                 ereport(NOTICE,
    2101                 :             :                         (errmsg("truncate cascades to table \"%s\"",
    2102                 :             :                                 RelationGetRelationName(rel))));
    2103                 :          62 :                 truncate_check_rel(relid, rel->rd_rel);
    2104                 :          62 :                 truncate_check_perms(relid, rel->rd_rel);
    2105                 :          62 :                 truncate_check_activity(rel);
    2106                 :          62 :                 rels = lappend(rels, rel);
    2107                 :          62 :                 relids = lappend_oid(relids, relid);
    2108                 :             : 
    2109                 :             :                 /* Log this relation only if needed for logical decoding */
    2110   [ +  -  -  +  :          62 :                 if (RelationIsLogicallyLogged(rel))
          -  -  -  -  -  
          -  -  -  -  -  
                   -  - ]
    2111                 :           0 :                     relids_logged = lappend_oid(relids_logged, relid);
    2112                 :             :             }
    2113                 :             :         }
    2114                 :             :     }
    2115                 :             : 
    2116                 :             :     /*
    2117                 :             :      * Check foreign key references.  In CASCADE mode, this should be
    2118                 :             :      * unnecessary since we just pulled in all the references; but as a
    2119                 :             :      * cross-check, do it anyway if in an Assert-enabled build.
    2120                 :             :      */
    2121                 :             : #ifdef USE_ASSERT_CHECKING
    2122                 :             :     heap_truncate_check_FKs(rels, false);
    2123                 :             : #else
    2124         [ +  + ]:        1170 :     if (behavior == DROP_RESTRICT)
    2125                 :        1145 :         heap_truncate_check_FKs(rels, false);
    2126                 :             : #endif
    2127                 :             : 
    2128                 :             :     /*
    2129                 :             :      * If we are asked to restart sequences, find all the sequences, lock them
    2130                 :             :      * (we need AccessExclusiveLock for ResetSequence), and check permissions.
    2131                 :             :      * We want to do this early since it's pointless to do all the truncation
    2132                 :             :      * work only to fail on sequence permissions.
    2133                 :             :      */
    2134         [ +  + ]:        1121 :     if (restart_seqs)
    2135                 :             :     {
    2136   [ +  -  +  +  :          32 :         foreach(cell, rels)
                   +  + ]
    2137                 :             :         {
    2138                 :          16 :             Relation    rel = (Relation) lfirst(cell);
    2139                 :          16 :             List       *seqlist = getOwnedSequences(RelationGetRelid(rel));
    2140                 :             :             ListCell   *seqcell;
    2141                 :             : 
    2142   [ +  +  +  +  :          39 :             foreach(seqcell, seqlist)
                   +  + ]
    2143                 :             :             {
    2144                 :          23 :                 Oid         seq_relid = lfirst_oid(seqcell);
    2145                 :             :                 Relation    seq_rel;
    2146                 :             : 
    2147                 :          23 :                 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
    2148                 :             : 
    2149                 :             :                 /* This check must match AlterSequence! */
    2150         [ -  + ]:          23 :                 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
    2151                 :           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
    2152                 :           0 :                                    RelationGetRelationName(seq_rel));
    2153                 :             : 
    2154                 :          23 :                 seq_relids = lappend_oid(seq_relids, seq_relid);
    2155                 :             : 
    2156                 :          23 :                 relation_close(seq_rel, NoLock);
    2157                 :             :             }
    2158                 :             :         }
    2159                 :             :     }
    2160                 :             : 
    2161                 :             :     /* Prepare to catch AFTER triggers. */
    2162                 :        1121 :     AfterTriggerBeginQuery();
    2163                 :             : 
    2164                 :             :     /*
    2165                 :             :      * To fire triggers, we'll need an EState as well as a ResultRelInfo for
    2166                 :             :      * each relation.  We don't need to call ExecOpenIndices, though.
    2167                 :             :      *
    2168                 :             :      * We put the ResultRelInfos in the es_opened_result_relations list, even
    2169                 :             :      * though we don't have a range table and don't populate the
    2170                 :             :      * es_result_relations array.  That's a bit bogus, but it's enough to make
    2171                 :             :      * ExecGetTriggerResultRel() find them.
    2172                 :             :      */
    2173                 :        1121 :     estate = CreateExecutorState();
    2174                 :             :     resultRelInfos = (ResultRelInfo *)
    2175                 :        1121 :         palloc(list_length(rels) * sizeof(ResultRelInfo));
    2176                 :        1121 :     resultRelInfo = resultRelInfos;
    2177   [ +  -  +  +  :        3683 :     foreach(cell, rels)
                   +  + ]
    2178                 :             :     {
    2179                 :        2562 :         Relation    rel = (Relation) lfirst(cell);
    2180                 :             : 
    2181                 :        2562 :         InitResultRelInfo(resultRelInfo,
    2182                 :             :                           rel,
    2183                 :             :                           0,    /* dummy rangetable index */
    2184                 :             :                           NULL,
    2185                 :             :                           0);
    2186                 :        2562 :         estate->es_opened_result_relations =
    2187                 :        2562 :             lappend(estate->es_opened_result_relations, resultRelInfo);
    2188                 :        2562 :         resultRelInfo++;
    2189                 :             :     }
    2190                 :             : 
    2191                 :             :     /*
    2192                 :             :      * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
    2193                 :             :      * truncating (this is because one of them might throw an error). Also, if
    2194                 :             :      * we were to allow them to prevent statement execution, that would need
    2195                 :             :      * to be handled here.
    2196                 :             :      */
    2197                 :        1121 :     resultRelInfo = resultRelInfos;
    2198   [ +  -  +  +  :        3683 :     foreach(cell, rels)
                   +  + ]
    2199                 :             :     {
    2200                 :             :         UserContext ucxt;
    2201                 :             : 
    2202         [ +  + ]:        2562 :         if (run_as_table_owner)
    2203                 :          36 :             SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2204                 :             :                                   &ucxt);
    2205                 :        2562 :         ExecBSTruncateTriggers(estate, resultRelInfo);
    2206         [ +  + ]:        2562 :         if (run_as_table_owner)
    2207                 :          36 :             RestoreUserContext(&ucxt);
    2208                 :        2562 :         resultRelInfo++;
    2209                 :             :     }
    2210                 :             : 
    2211                 :             :     /*
    2212                 :             :      * OK, truncate each table.
    2213                 :             :      */
    2214                 :        1121 :     mySubid = GetCurrentSubTransactionId();
    2215                 :             : 
    2216   [ +  -  +  +  :        3683 :     foreach(cell, rels)
                   +  + ]
    2217                 :             :     {
    2218                 :        2562 :         Relation    rel = (Relation) lfirst(cell);
    2219                 :             : 
    2220                 :             :         /* Skip partitioned tables as there is nothing to do */
    2221         [ +  + ]:        2562 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    2222                 :         477 :             continue;
    2223                 :             : 
    2224                 :             :         /*
    2225                 :             :          * Build the lists of foreign tables belonging to each foreign server
    2226                 :             :          * and pass each list to the foreign data wrapper's callback function,
    2227                 :             :          * so that each server can truncate its all foreign tables in bulk.
    2228                 :             :          * Each list is saved as a single entry in a hash table that uses the
    2229                 :             :          * server OID as lookup key.
    2230                 :             :          */
    2231         [ +  + ]:        2085 :         if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    2232                 :          17 :         {
    2233                 :          17 :             Oid         serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
    2234                 :             :             bool        found;
    2235                 :             :             ForeignTruncateInfo *ft_info;
    2236                 :             : 
    2237                 :             :             /* First time through, initialize hashtable for foreign tables */
    2238         [ +  + ]:          17 :             if (!ft_htab)
    2239                 :             :             {
    2240                 :             :                 HASHCTL     hctl;
    2241                 :             : 
    2242                 :          15 :                 memset(&hctl, 0, sizeof(HASHCTL));
    2243                 :          15 :                 hctl.keysize = sizeof(Oid);
    2244                 :          15 :                 hctl.entrysize = sizeof(ForeignTruncateInfo);
    2245                 :          15 :                 hctl.hcxt = CurrentMemoryContext;
    2246                 :             : 
    2247                 :          15 :                 ft_htab = hash_create("TRUNCATE for Foreign Tables",
    2248                 :             :                                       32,   /* start small and extend */
    2249                 :             :                                       &hctl,
    2250                 :             :                                       HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
    2251                 :             :             }
    2252                 :             : 
    2253                 :             :             /* Find or create cached entry for the foreign table */
    2254                 :          17 :             ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
    2255         [ +  + ]:          17 :             if (!found)
    2256                 :          15 :                 ft_info->rels = NIL;
    2257                 :             : 
    2258                 :             :             /*
    2259                 :             :              * Save the foreign table in the entry of the server that the
    2260                 :             :              * foreign table belongs to.
    2261                 :             :              */
    2262                 :          17 :             ft_info->rels = lappend(ft_info->rels, rel);
    2263                 :          17 :             continue;
    2264                 :             :         }
    2265                 :             : 
    2266                 :             :         /*
    2267                 :             :          * Normally, we need a transaction-safe truncation here.  However, if
    2268                 :             :          * the table was either created in the current (sub)transaction or has
    2269                 :             :          * a new relfilenumber in the current (sub)transaction, then we can
    2270                 :             :          * just truncate it in-place, because a rollback would cause the whole
    2271                 :             :          * table or the current physical file to be thrown away anyway.
    2272                 :             :          */
    2273         [ +  + ]:        2068 :         if (rel->rd_createSubid == mySubid ||
    2274         [ +  + ]:        2050 :             rel->rd_newRelfilelocatorSubid == mySubid)
    2275                 :             :         {
    2276                 :             :             /* Immediate, non-rollbackable truncation is OK */
    2277                 :          50 :             heap_truncate_one_rel(rel);
    2278                 :             :         }
    2279                 :             :         else
    2280                 :             :         {
    2281                 :             :             Oid         heap_relid;
    2282                 :             :             Oid         toast_relid;
    2283                 :        2018 :             ReindexParams reindex_params = {0};
    2284                 :             : 
    2285                 :             :             /*
    2286                 :             :              * This effectively deletes all rows in the table, and may be done
    2287                 :             :              * in a serializable transaction.  In that case we must record a
    2288                 :             :              * rw-conflict in to this transaction from each transaction
    2289                 :             :              * holding a predicate lock on the table.
    2290                 :             :              */
    2291                 :        2018 :             CheckTableForSerializableConflictIn(rel);
    2292                 :             : 
    2293                 :             :             /*
    2294                 :             :              * Need the full transaction-safe pushups.
    2295                 :             :              *
    2296                 :             :              * Create a new empty storage file for the relation, and assign it
    2297                 :             :              * as the relfilenumber value. The old storage file is scheduled
    2298                 :             :              * for deletion at commit.
    2299                 :             :              */
    2300                 :        2018 :             RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
    2301                 :             : 
    2302                 :        2018 :             heap_relid = RelationGetRelid(rel);
    2303                 :             : 
    2304                 :             :             /*
    2305                 :             :              * The same for the toast table, if any.
    2306                 :             :              */
    2307                 :        2018 :             toast_relid = rel->rd_rel->reltoastrelid;
    2308         [ +  + ]:        2018 :             if (OidIsValid(toast_relid))
    2309                 :             :             {
    2310                 :        1237 :                 Relation    toastrel = relation_open(toast_relid,
    2311                 :             :                                                      AccessExclusiveLock);
    2312                 :             : 
    2313                 :        1237 :                 RelationSetNewRelfilenumber(toastrel,
    2314                 :        1237 :                                             toastrel->rd_rel->relpersistence);
    2315                 :        1237 :                 table_close(toastrel, NoLock);
    2316                 :             :             }
    2317                 :             : 
    2318                 :             :             /*
    2319                 :             :              * Reconstruct the indexes to match, and we're done.
    2320                 :             :              */
    2321                 :        2018 :             reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
    2322                 :             :                              &reindex_params);
    2323                 :             :         }
    2324                 :             : 
    2325                 :        2068 :         pgstat_count_truncate(rel);
    2326                 :             :     }
    2327                 :             : 
    2328                 :             :     /* Now go through the hash table, and truncate foreign tables */
    2329         [ +  + ]:        1121 :     if (ft_htab)
    2330                 :             :     {
    2331                 :             :         ForeignTruncateInfo *ft_info;
    2332                 :             :         HASH_SEQ_STATUS seq;
    2333                 :             : 
    2334                 :          15 :         hash_seq_init(&seq, ft_htab);
    2335                 :             : 
    2336         [ +  + ]:          15 :         PG_TRY();
    2337                 :             :         {
    2338         [ +  + ]:          26 :             while ((ft_info = hash_seq_search(&seq)) != NULL)
    2339                 :             :             {
    2340                 :          15 :                 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
    2341                 :             : 
    2342                 :             :                 /* truncate_check_rel() has checked that already */
    2343                 :             :                 Assert(routine->ExecForeignTruncate != NULL);
    2344                 :             : 
    2345                 :          15 :                 routine->ExecForeignTruncate(ft_info->rels,
    2346                 :             :                                              behavior,
    2347                 :             :                                              restart_seqs);
    2348                 :             :             }
    2349                 :             :         }
    2350                 :           4 :         PG_FINALLY();
    2351                 :             :         {
    2352                 :          15 :             hash_destroy(ft_htab);
    2353                 :             :         }
    2354         [ +  + ]:          15 :         PG_END_TRY();
    2355                 :             :     }
    2356                 :             : 
    2357                 :             :     /*
    2358                 :             :      * Restart owned sequences if we were asked to.
    2359                 :             :      */
    2360   [ +  +  +  +  :        1140 :     foreach(cell, seq_relids)
                   +  + ]
    2361                 :             :     {
    2362                 :          23 :         Oid         seq_relid = lfirst_oid(cell);
    2363                 :             : 
    2364                 :          23 :         ResetSequence(seq_relid);
    2365                 :             :     }
    2366                 :             : 
    2367                 :             :     /*
    2368                 :             :      * Write a WAL record to allow this set of actions to be logically
    2369                 :             :      * decoded.
    2370                 :             :      *
    2371                 :             :      * Assemble an array of relids so we can write a single WAL record for the
    2372                 :             :      * whole action.
    2373                 :             :      */
    2374         [ +  + ]:        1117 :     if (relids_logged != NIL)
    2375                 :             :     {
    2376                 :             :         xl_heap_truncate xlrec;
    2377                 :          33 :         int         i = 0;
    2378                 :             : 
    2379                 :             :         /* should only get here if effective_wal_level is 'logical' */
    2380                 :             :         Assert(XLogLogicalInfoActive());
    2381                 :             : 
    2382                 :          33 :         logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
    2383   [ +  -  +  +  :          84 :         foreach(cell, relids_logged)
                   +  + ]
    2384                 :          51 :             logrelids[i++] = lfirst_oid(cell);
    2385                 :             : 
    2386                 :          33 :         xlrec.dbId = MyDatabaseId;
    2387                 :          33 :         xlrec.nrelids = list_length(relids_logged);
    2388                 :          33 :         xlrec.flags = 0;
    2389         [ +  + ]:          33 :         if (behavior == DROP_CASCADE)
    2390                 :           1 :             xlrec.flags |= XLH_TRUNCATE_CASCADE;
    2391         [ +  + ]:          33 :         if (restart_seqs)
    2392                 :           2 :             xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
    2393                 :             : 
    2394                 :          33 :         XLogBeginInsert();
    2395                 :          33 :         XLogRegisterData(&xlrec, SizeOfHeapTruncate);
    2396                 :          33 :         XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
    2397                 :             : 
    2398                 :          33 :         XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
    2399                 :             : 
    2400                 :          33 :         (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
    2401                 :             :     }
    2402                 :             : 
    2403                 :             :     /*
    2404                 :             :      * Process all AFTER STATEMENT TRUNCATE triggers.
    2405                 :             :      */
    2406                 :        1117 :     resultRelInfo = resultRelInfos;
    2407   [ +  -  +  +  :        3675 :     foreach(cell, rels)
                   +  + ]
    2408                 :             :     {
    2409                 :             :         UserContext ucxt;
    2410                 :             : 
    2411         [ +  + ]:        2558 :         if (run_as_table_owner)
    2412                 :          36 :             SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
    2413                 :             :                                   &ucxt);
    2414                 :        2558 :         ExecASTruncateTriggers(estate, resultRelInfo);
    2415         [ +  + ]:        2558 :         if (run_as_table_owner)
    2416                 :          36 :             RestoreUserContext(&ucxt);
    2417                 :        2558 :         resultRelInfo++;
    2418                 :             :     }
    2419                 :             : 
    2420                 :             :     /* Handle queued AFTER triggers */
    2421                 :        1117 :     AfterTriggerEndQuery(estate);
    2422                 :             : 
    2423                 :             :     /* We can clean up the EState now */
    2424                 :        1117 :     FreeExecutorState(estate);
    2425                 :             : 
    2426                 :             :     /*
    2427                 :             :      * Close any rels opened by CASCADE (can't do this while EState still
    2428                 :             :      * holds refs)
    2429                 :             :      */
    2430                 :        1117 :     rels = list_difference_ptr(rels, explicit_rels);
    2431   [ +  +  +  +  :        1179 :     foreach(cell, rels)
                   +  + ]
    2432                 :             :     {
    2433                 :          62 :         Relation    rel = (Relation) lfirst(cell);
    2434                 :             : 
    2435                 :          62 :         table_close(rel, NoLock);
    2436                 :             :     }
    2437                 :        1117 : }
    2438                 :             : 
    2439                 :             : /*
    2440                 :             :  * Check that a given relation is safe to truncate.  Subroutine for
    2441                 :             :  * ExecuteTruncate() and RangeVarCallbackForTruncate().
    2442                 :             :  */
    2443                 :             : static void
    2444                 :        2757 : truncate_check_rel(Oid relid, Form_pg_class reltuple)
    2445                 :             : {
    2446                 :        2757 :     char       *relname = NameStr(reltuple->relname);
    2447                 :             : 
    2448                 :             :     /*
    2449                 :             :      * Only allow truncate on regular tables, foreign tables using foreign
    2450                 :             :      * data wrappers supporting TRUNCATE and partitioned tables (although, the
    2451                 :             :      * latter are only being included here for the following checks; no
    2452                 :             :      * physical truncation will occur in their case.).
    2453                 :             :      */
    2454         [ +  + ]:        2757 :     if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
    2455                 :             :     {
    2456                 :          19 :         Oid         serverid = GetForeignServerIdByRelId(relid);
    2457                 :          19 :         FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
    2458                 :             : 
    2459         [ +  + ]:          18 :         if (!fdwroutine->ExecForeignTruncate)
    2460         [ +  - ]:           1 :             ereport(ERROR,
    2461                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2462                 :             :                      errmsg("cannot truncate foreign table \"%s\"",
    2463                 :             :                             relname)));
    2464                 :             :     }
    2465         [ +  + ]:        2738 :     else if (reltuple->relkind != RELKIND_RELATION &&
    2466         [ -  + ]:         487 :              reltuple->relkind != RELKIND_PARTITIONED_TABLE)
    2467         [ #  # ]:           0 :         ereport(ERROR,
    2468                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2469                 :             :                  errmsg("\"%s\" is not a table", relname)));
    2470                 :             : 
    2471                 :             :     /*
    2472                 :             :      * Most system catalogs can't be truncated at all, or at least not unless
    2473                 :             :      * allow_system_table_mods=on. As an exception, however, we allow
    2474                 :             :      * pg_largeobject and pg_largeobject_metadata to be truncated as part of
    2475                 :             :      * pg_upgrade, because we need to change its relfilenode to match the old
    2476                 :             :      * cluster, and allowing a TRUNCATE command to be executed is the easiest
    2477                 :             :      * way of doing that. We also allow TRUNCATE on the conflict log tables,
    2478                 :             :      * to permit users to manually prune conflict data to manage disk space.
    2479                 :             :      */
    2480   [ +  +  +  + ]:        2755 :     if (!allowSystemTableMods && IsSystemClass(relid, reltuple) &&
    2481         [ +  + ]:          73 :         !IsConflictLogTableClass(reltuple)
    2482   [ +  +  +  + ]:          65 :         && (!IsBinaryUpgrade ||
    2483         [ -  + ]:          32 :             (relid != LargeObjectRelationId &&
    2484                 :             :              relid != LargeObjectMetadataRelationId)))
    2485         [ +  - ]:           1 :         ereport(ERROR,
    2486                 :             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    2487                 :             :                  errmsg("permission denied: \"%s\" is a system catalog",
    2488                 :             :                         relname)));
    2489                 :             : 
    2490         [ -  + ]:        2754 :     InvokeObjectTruncateHook(relid);
    2491                 :        2754 : }
    2492                 :             : 
    2493                 :             : /*
    2494                 :             :  * Check that current user has the permission to truncate given relation.
    2495                 :             :  */
    2496                 :             : static void
    2497                 :        1538 : truncate_check_perms(Oid relid, Form_pg_class reltuple)
    2498                 :             : {
    2499                 :        1538 :     char       *relname = NameStr(reltuple->relname);
    2500                 :             :     AclResult   aclresult;
    2501                 :             : 
    2502                 :             :     /* Permissions checks */
    2503                 :        1538 :     aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
    2504         [ +  + ]:        1538 :     if (aclresult != ACLCHECK_OK)
    2505                 :          20 :         aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
    2506                 :             :                        relname);
    2507                 :        1518 : }
    2508                 :             : 
    2509                 :             : /*
    2510                 :             :  * Set of extra sanity checks to check if a given relation is safe to
    2511                 :             :  * truncate.  This is split with truncate_check_rel() as
    2512                 :             :  * RangeVarCallbackForTruncate() cannot open a Relation yet.
    2513                 :             :  */
    2514                 :             : static void
    2515                 :        2636 : truncate_check_activity(Relation rel)
    2516                 :             : {
    2517                 :             :     /*
    2518                 :             :      * Don't allow truncate on temp tables of other backends ... their local
    2519                 :             :      * buffer manager is not going to cope.
    2520                 :             :      */
    2521   [ +  +  +  + ]:        2636 :     if (RELATION_IS_OTHER_TEMP(rel))
    2522         [ +  - ]:           1 :         ereport(ERROR,
    2523                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2524                 :             :                  errmsg("cannot truncate temporary tables of other sessions")));
    2525                 :             : 
    2526                 :             :     /*
    2527                 :             :      * Also check for active uses of the relation in the current transaction,
    2528                 :             :      * including open scans and pending AFTER trigger events.
    2529                 :             :      */
    2530                 :        2635 :     CheckTableNotInUse(rel, "TRUNCATE");
    2531                 :        2631 : }
    2532                 :             : 
    2533                 :             : /*
    2534                 :             :  * storage_name
    2535                 :             :  *    returns the name corresponding to a typstorage/attstorage enum value
    2536                 :             :  */
    2537                 :             : static const char *
    2538                 :          16 : storage_name(char c)
    2539                 :             : {
    2540   [ -  -  +  +  :          16 :     switch (c)
                      - ]
    2541                 :             :     {
    2542                 :           0 :         case TYPSTORAGE_PLAIN:
    2543                 :           0 :             return "PLAIN";
    2544                 :           0 :         case TYPSTORAGE_EXTERNAL:
    2545                 :           0 :             return "EXTERNAL";
    2546                 :           8 :         case TYPSTORAGE_EXTENDED:
    2547                 :           8 :             return "EXTENDED";
    2548                 :           8 :         case TYPSTORAGE_MAIN:
    2549                 :           8 :             return "MAIN";
    2550                 :           0 :         default:
    2551                 :           0 :             return "???";
    2552                 :             :     }
    2553                 :             : }
    2554                 :             : 
    2555                 :             : /*----------
    2556                 :             :  * MergeAttributes
    2557                 :             :  *      Returns new schema given initial schema and superclasses.
    2558                 :             :  *
    2559                 :             :  * Input arguments:
    2560                 :             :  * 'columns' is the column/attribute definition for the table. (It's a list
    2561                 :             :  *      of ColumnDef's.) It is destructively changed.
    2562                 :             :  * 'supers' is a list of OIDs of parent relations, already locked by caller.
    2563                 :             :  * 'relpersistence' is the persistence type of the table.
    2564                 :             :  * 'is_partition' tells if the table is a partition.
    2565                 :             :  *
    2566                 :             :  * Output arguments:
    2567                 :             :  * 'supconstr' receives a list of CookedConstraint representing
    2568                 :             :  *      CHECK constraints belonging to parent relations, updated as
    2569                 :             :  *      necessary to be valid for the child.
    2570                 :             :  * 'supnotnulls' receives a list of CookedConstraint representing
    2571                 :             :  *      not-null constraints based on those from parent relations.
    2572                 :             :  *
    2573                 :             :  * Return value:
    2574                 :             :  * Completed schema list.
    2575                 :             :  *
    2576                 :             :  * Notes:
    2577                 :             :  *    The order in which the attributes are inherited is very important.
    2578                 :             :  *    Intuitively, the inherited attributes should come first. If a table
    2579                 :             :  *    inherits from multiple parents, the order of those attributes are
    2580                 :             :  *    according to the order of the parents specified in CREATE TABLE.
    2581                 :             :  *
    2582                 :             :  *    Here's an example:
    2583                 :             :  *
    2584                 :             :  *      create table person (name text, age int4, location point);
    2585                 :             :  *      create table emp (salary int4, manager text) inherits(person);
    2586                 :             :  *      create table student (gpa float8) inherits (person);
    2587                 :             :  *      create table stud_emp (percent int4) inherits (emp, student);
    2588                 :             :  *
    2589                 :             :  *    The order of the attributes of stud_emp is:
    2590                 :             :  *
    2591                 :             :  *                          person {1:name, 2:age, 3:location}
    2592                 :             :  *                          /    \
    2593                 :             :  *             {6:gpa}  student   emp {4:salary, 5:manager}
    2594                 :             :  *                          \    /
    2595                 :             :  *                         stud_emp {7:percent}
    2596                 :             :  *
    2597                 :             :  *     If the same attribute name appears multiple times, then it appears
    2598                 :             :  *     in the result table in the proper location for its first appearance.
    2599                 :             :  *
    2600                 :             :  *     Constraints (including not-null constraints) for the child table
    2601                 :             :  *     are the union of all relevant constraints, from both the child schema
    2602                 :             :  *     and parent tables.  In addition, in legacy inheritance, each column that
    2603                 :             :  *     appears in a primary key in any of the parents also gets a NOT NULL
    2604                 :             :  *     constraint (partitioning doesn't need this, because the PK itself gets
    2605                 :             :  *     inherited.)
    2606                 :             :  *
    2607                 :             :  *     The default value for a child column is defined as:
    2608                 :             :  *      (1) If the child schema specifies a default, that value is used.
    2609                 :             :  *      (2) If neither the child nor any parent specifies a default, then
    2610                 :             :  *          the column will not have a default.
    2611                 :             :  *      (3) If conflicting defaults are inherited from different parents
    2612                 :             :  *          (and not overridden by the child), an error is raised.
    2613                 :             :  *      (4) Otherwise the inherited default is used.
    2614                 :             :  *
    2615                 :             :  *      Note that the default-value infrastructure is used for generated
    2616                 :             :  *      columns' expressions too, so most of the preceding paragraph applies
    2617                 :             :  *      to generation expressions too.  We insist that a child column be
    2618                 :             :  *      generated if and only if its parent(s) are, but it need not have
    2619                 :             :  *      the same generation expression.
    2620                 :             :  *----------
    2621                 :             :  */
    2622                 :             : static List *
    2623                 :       42431 : MergeAttributes(List *columns, const List *supers, char relpersistence,
    2624                 :             :                 bool is_partition, List **supconstr, List **supnotnulls)
    2625                 :             : {
    2626                 :       42431 :     List       *inh_columns = NIL;
    2627                 :       42431 :     List       *constraints = NIL;
    2628                 :       42431 :     List       *nnconstraints = NIL;
    2629                 :       42431 :     bool        have_bogus_defaults = false;
    2630                 :             :     int         child_attno;
    2631                 :             :     static Node bogus_marker = {0}; /* marks conflicting defaults */
    2632                 :       42431 :     List       *saved_columns = NIL;
    2633                 :             :     ListCell   *lc;
    2634                 :             : 
    2635                 :             :     /*
    2636                 :             :      * Check for and reject tables with too many columns. We perform this
    2637                 :             :      * check relatively early for two reasons: (a) we don't run the risk of
    2638                 :             :      * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
    2639                 :             :      * okay if we're processing <= 1600 columns, but could take minutes to
    2640                 :             :      * execute if the user attempts to create a table with hundreds of
    2641                 :             :      * thousands of columns.
    2642                 :             :      *
    2643                 :             :      * Note that we also need to check that we do not exceed this figure after
    2644                 :             :      * including columns from inherited relations.
    2645                 :             :      */
    2646         [ -  + ]:       42431 :     if (list_length(columns) > MaxHeapAttributeNumber)
    2647         [ #  # ]:           0 :         ereport(ERROR,
    2648                 :             :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    2649                 :             :                  errmsg("tables can have at most %d columns",
    2650                 :             :                         MaxHeapAttributeNumber)));
    2651                 :             : 
    2652                 :             :     /*
    2653                 :             :      * Check for duplicate names in the explicit list of attributes.
    2654                 :             :      *
    2655                 :             :      * Although we might consider merging such entries in the same way that we
    2656                 :             :      * handle name conflicts for inherited attributes, it seems to make more
    2657                 :             :      * sense to assume such conflicts are errors.
    2658                 :             :      *
    2659                 :             :      * We don't use foreach() here because we have two nested loops over the
    2660                 :             :      * columns list, with possible element deletions in the inner one.  If we
    2661                 :             :      * used foreach_delete_current() it could only fix up the state of one of
    2662                 :             :      * the loops, so it seems cleaner to use looping over list indexes for
    2663                 :             :      * both loops.  Note that any deletion will happen beyond where the outer
    2664                 :             :      * loop is, so its index never needs adjustment.
    2665                 :             :      */
    2666         [ +  + ]:      195219 :     for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
    2667                 :             :     {
    2668                 :      152804 :         ColumnDef  *coldef = list_nth_node(ColumnDef, columns, coldefpos);
    2669                 :             : 
    2670   [ +  +  +  + ]:      152804 :         if (!is_partition && coldef->typeName == NULL)
    2671                 :             :         {
    2672                 :             :             /*
    2673                 :             :              * Typed table column option that does not belong to a column from
    2674                 :             :              * the type.  This works because the columns from the type come
    2675                 :             :              * first in the list.  (We omit this check for partition column
    2676                 :             :              * lists; those are processed separately below.)
    2677                 :             :              */
    2678         [ +  - ]:           4 :             ereport(ERROR,
    2679                 :             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    2680                 :             :                      errmsg("column \"%s\" does not exist",
    2681                 :             :                             coldef->colname)));
    2682                 :             :         }
    2683                 :             : 
    2684                 :             :         /* restpos scans all entries beyond coldef; incr is in loop body */
    2685         [ +  + ]:     4112497 :         for (int restpos = coldefpos + 1; restpos < list_length(columns);)
    2686                 :             :         {
    2687                 :     3959709 :             ColumnDef  *restdef = list_nth_node(ColumnDef, columns, restpos);
    2688                 :             : 
    2689         [ +  + ]:     3959709 :             if (strcmp(coldef->colname, restdef->colname) == 0)
    2690                 :             :             {
    2691         [ +  + ]:          33 :                 if (coldef->is_from_type)
    2692                 :             :                 {
    2693                 :             :                     /*
    2694                 :             :                      * merge the column options into the column from the type
    2695                 :             :                      */
    2696                 :          21 :                     coldef->is_not_null = restdef->is_not_null;
    2697                 :          21 :                     coldef->raw_default = restdef->raw_default;
    2698                 :          21 :                     coldef->cooked_default = restdef->cooked_default;
    2699                 :          21 :                     coldef->constraints = restdef->constraints;
    2700                 :          21 :                     coldef->is_from_type = false;
    2701                 :          21 :                     columns = list_delete_nth_cell(columns, restpos);
    2702                 :             :                 }
    2703                 :             :                 else
    2704         [ +  - ]:          12 :                     ereport(ERROR,
    2705                 :             :                             (errcode(ERRCODE_DUPLICATE_COLUMN),
    2706                 :             :                              errmsg("column \"%s\" specified more than once",
    2707                 :             :                                     coldef->colname)));
    2708                 :             :             }
    2709                 :             :             else
    2710                 :     3959676 :                 restpos++;
    2711                 :             :         }
    2712                 :             :     }
    2713                 :             : 
    2714                 :             :     /*
    2715                 :             :      * In case of a partition, there are no new column definitions, only dummy
    2716                 :             :      * ColumnDefs created for column constraints.  Set them aside for now and
    2717                 :             :      * process them at the end.
    2718                 :             :      */
    2719         [ +  + ]:       42415 :     if (is_partition)
    2720                 :             :     {
    2721                 :        6560 :         saved_columns = columns;
    2722                 :        6560 :         columns = NIL;
    2723                 :             :     }
    2724                 :             : 
    2725                 :             :     /*
    2726                 :             :      * Scan the parents left-to-right, and merge their attributes to form a
    2727                 :             :      * list of inherited columns (inh_columns).
    2728                 :             :      */
    2729                 :       42415 :     child_attno = 0;
    2730   [ +  +  +  +  :       50612 :     foreach(lc, supers)
                   +  + ]
    2731                 :             :     {
    2732                 :        8257 :         Oid         parent = lfirst_oid(lc);
    2733                 :             :         Relation    relation;
    2734                 :             :         TupleDesc   tupleDesc;
    2735                 :             :         TupleConstr *constr;
    2736                 :             :         AttrMap    *newattmap;
    2737                 :             :         List       *inherited_defaults;
    2738                 :             :         List       *cols_with_defaults;
    2739                 :             :         List       *nnconstrs;
    2740                 :             :         ListCell   *lc1;
    2741                 :             :         ListCell   *lc2;
    2742                 :        8257 :         Bitmapset  *nncols = NULL;
    2743                 :             : 
    2744                 :             :         /* caller already got lock */
    2745                 :        8257 :         relation = table_open(parent, NoLock);
    2746                 :             : 
    2747                 :             :         /*
    2748                 :             :          * Check for active uses of the parent partitioned table in the
    2749                 :             :          * current transaction, such as being used in some manner by an
    2750                 :             :          * enclosing command.
    2751                 :             :          */
    2752         [ +  + ]:        8257 :         if (is_partition)
    2753                 :        6560 :             CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
    2754                 :             : 
    2755                 :             :         /*
    2756                 :             :          * We do not allow partitioned tables and partitions to participate in
    2757                 :             :          * regular inheritance.
    2758                 :             :          */
    2759   [ +  +  +  + ]:        8253 :         if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
    2760         [ +  - ]:           4 :             ereport(ERROR,
    2761                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2762                 :             :                      errmsg("cannot inherit from partitioned table \"%s\"",
    2763                 :             :                             RelationGetRelationName(relation))));
    2764   [ +  +  +  + ]:        8249 :         if (relation->rd_rel->relispartition && !is_partition)
    2765         [ +  - ]:           4 :             ereport(ERROR,
    2766                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2767                 :             :                      errmsg("cannot inherit from partition \"%s\"",
    2768                 :             :                             RelationGetRelationName(relation))));
    2769                 :             : 
    2770                 :             :         /*
    2771                 :             :          * Conflict log tables are managed by the system for logical
    2772                 :             :          * replication and should not be used as parent tables, as inheritance
    2773                 :             :          * could interfere with the logging behavior.
    2774                 :             :          */
    2775         [ +  + ]:        8245 :         if (IsConflictLogTableNamespace(relation->rd_rel->relnamespace))
    2776         [ +  - ]:           4 :             ereport(ERROR,
    2777                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2778                 :             :                      errmsg("cannot inherit from conflict log table \"%s\"",
    2779                 :             :                             RelationGetRelationName(relation)),
    2780                 :             :                      errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
    2781                 :             : 
    2782         [ +  + ]:        8241 :         if (relation->rd_rel->relkind != RELKIND_RELATION &&
    2783         [ +  + ]:        6556 :             relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
    2784         [ -  + ]:        6544 :             relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
    2785         [ #  # ]:           0 :             ereport(ERROR,
    2786                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2787                 :             :                      errmsg("inherited relation \"%s\" is not a table or foreign table",
    2788                 :             :                             RelationGetRelationName(relation))));
    2789                 :             : 
    2790                 :             :         /*
    2791                 :             :          * If the parent is permanent, so must be all of its partitions.  Note
    2792                 :             :          * that inheritance allows that case.
    2793                 :             :          */
    2794         [ +  + ]:        8241 :         if (is_partition &&
    2795   [ +  +  +  + ]:        6556 :             relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
    2796                 :             :             relpersistence == RELPERSISTENCE_TEMP)
    2797         [ +  - ]:           4 :             ereport(ERROR,
    2798                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2799                 :             :                      errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
    2800                 :             :                             RelationGetRelationName(relation))));
    2801                 :             : 
    2802                 :             :         /* Permanent rels cannot inherit from temporary ones */
    2803         [ +  + ]:        8237 :         if (relpersistence != RELPERSISTENCE_TEMP &&
    2804         [ +  + ]:        7967 :             relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
    2805   [ +  -  +  + ]:          16 :             ereport(ERROR,
    2806                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2807                 :             :                      errmsg(!is_partition
    2808                 :             :                             ? "cannot inherit from temporary relation \"%s\""
    2809                 :             :                             : "cannot create a permanent relation as partition of temporary relation \"%s\"",
    2810                 :             :                             RelationGetRelationName(relation))));
    2811                 :             : 
    2812                 :             :         /* If existing rel is temp, it must belong to this session */
    2813   [ +  +  -  + ]:        8221 :         if (RELATION_IS_OTHER_TEMP(relation))
    2814   [ #  #  #  # ]:           0 :             ereport(ERROR,
    2815                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2816                 :             :                      errmsg(!is_partition
    2817                 :             :                             ? "cannot inherit from temporary relation of another session"
    2818                 :             :                             : "cannot create as partition of temporary relation of another session")));
    2819                 :             : 
    2820                 :             :         /*
    2821                 :             :          * We should have an UNDER permission flag for this, but for now,
    2822                 :             :          * demand that creator of a child table own the parent.
    2823                 :             :          */
    2824         [ -  + ]:        8221 :         if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
    2825                 :           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
    2826                 :           0 :                            RelationGetRelationName(relation));
    2827                 :             : 
    2828                 :        8221 :         tupleDesc = RelationGetDescr(relation);
    2829                 :        8221 :         constr = tupleDesc->constr;
    2830                 :             : 
    2831                 :             :         /*
    2832                 :             :          * newattmap->attnums[] will contain the child-table attribute numbers
    2833                 :             :          * for the attributes of this parent table.  (They are not the same
    2834                 :             :          * for parents after the first one, nor if we have dropped columns.)
    2835                 :             :          */
    2836                 :        8221 :         newattmap = make_attrmap(tupleDesc->natts);
    2837                 :             : 
    2838                 :             :         /* We can't process inherited defaults until newattmap is complete. */
    2839                 :        8221 :         inherited_defaults = cols_with_defaults = NIL;
    2840                 :             : 
    2841                 :             :         /*
    2842                 :             :          * Request attnotnull on columns that have a not-null constraint
    2843                 :             :          * that's not marked NO INHERIT (even if not valid).
    2844                 :             :          */
    2845                 :        8221 :         nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
    2846                 :             :                                                   true, false);
    2847   [ +  +  +  +  :       18186 :         foreach_ptr(CookedConstraint, cc, nnconstrs)
                   +  + ]
    2848                 :        1744 :             nncols = bms_add_member(nncols, cc->attnum);
    2849                 :             : 
    2850         [ +  + ]:       24560 :         for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
    2851                 :       16339 :              parent_attno++)
    2852                 :             :         {
    2853                 :       16363 :             Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
    2854                 :             :                                                         parent_attno - 1);
    2855                 :       16363 :             char       *attributeName = NameStr(attribute->attname);
    2856                 :             :             int         exist_attno;
    2857                 :             :             ColumnDef  *newdef;
    2858                 :             :             ColumnDef  *mergeddef;
    2859                 :             : 
    2860                 :             :             /*
    2861                 :             :              * Ignore dropped columns in the parent.
    2862                 :             :              */
    2863         [ +  + ]:       16363 :             if (attribute->attisdropped)
    2864                 :         132 :                 continue;       /* leave newattmap->attnums entry as zero */
    2865                 :             : 
    2866                 :             :             /*
    2867                 :             :              * Create new column definition
    2868                 :             :              */
    2869                 :       16231 :             newdef = makeColumnDef(attributeName, attribute->atttypid,
    2870                 :             :                                    attribute->atttypmod, attribute->attcollation);
    2871                 :       16231 :             newdef->storage = attribute->attstorage;
    2872                 :       16231 :             newdef->generated = attribute->attgenerated;
    2873         [ +  + ]:       16231 :             if (CompressionMethodIsValid(attribute->attcompression))
    2874                 :          24 :                 newdef->compression =
    2875                 :          24 :                     pstrdup(GetCompressionMethodName(attribute->attcompression));
    2876                 :             : 
    2877                 :             :             /*
    2878                 :             :              * Regular inheritance children are independent enough not to
    2879                 :             :              * inherit identity columns.  But partitions are integral part of
    2880                 :             :              * a partitioned table and inherit identity column.
    2881                 :             :              */
    2882         [ +  + ]:       16231 :             if (is_partition)
    2883                 :       13189 :                 newdef->identity = attribute->attidentity;
    2884                 :             : 
    2885                 :             :             /*
    2886                 :             :              * Does it match some previously considered column from another
    2887                 :             :              * parent?
    2888                 :             :              */
    2889                 :       16231 :             exist_attno = findAttrByName(attributeName, inh_columns);
    2890         [ +  + ]:       16231 :             if (exist_attno > 0)
    2891                 :             :             {
    2892                 :             :                 /*
    2893                 :             :                  * Yes, try to merge the two column definitions.
    2894                 :             :                  */
    2895                 :         253 :                 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
    2896                 :             : 
    2897                 :         229 :                 newattmap->attnums[parent_attno - 1] = exist_attno;
    2898                 :             : 
    2899                 :             :                 /*
    2900                 :             :                  * Partitions have only one parent, so conflict should never
    2901                 :             :                  * occur.
    2902                 :             :                  */
    2903                 :             :                 Assert(!is_partition);
    2904                 :             :             }
    2905                 :             :             else
    2906                 :             :             {
    2907                 :             :                 /*
    2908                 :             :                  * No, create a new inherited column
    2909                 :             :                  */
    2910                 :       15978 :                 newdef->inhcount = 1;
    2911                 :       15978 :                 newdef->is_local = false;
    2912                 :       15978 :                 inh_columns = lappend(inh_columns, newdef);
    2913                 :             : 
    2914                 :       15978 :                 newattmap->attnums[parent_attno - 1] = ++child_attno;
    2915                 :       15978 :                 mergeddef = newdef;
    2916                 :             :             }
    2917                 :             : 
    2918                 :             :             /*
    2919                 :             :              * mark attnotnull if parent has it
    2920                 :             :              */
    2921         [ +  + ]:       16207 :             if (bms_is_member(parent_attno, nncols))
    2922                 :        1744 :                 mergeddef->is_not_null = true;
    2923                 :             : 
    2924                 :             :             /*
    2925                 :             :              * Locate default/generation expression if any
    2926                 :             :              */
    2927         [ +  + ]:       16207 :             if (attribute->atthasdef)
    2928                 :             :             {
    2929                 :             :                 Node       *this_default;
    2930                 :             : 
    2931                 :         566 :                 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
    2932         [ -  + ]:         566 :                 if (this_default == NULL)
    2933         [ #  # ]:           0 :                     elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
    2934                 :             :                          parent_attno, RelationGetRelationName(relation));
    2935                 :             : 
    2936                 :             :                 /*
    2937                 :             :                  * If it's a GENERATED default, it might contain Vars that
    2938                 :             :                  * need to be mapped to the inherited column(s)' new numbers.
    2939                 :             :                  * We can't do that till newattmap is ready, so just remember
    2940                 :             :                  * all the inherited default expressions for the moment.
    2941                 :             :                  */
    2942                 :         566 :                 inherited_defaults = lappend(inherited_defaults, this_default);
    2943                 :         566 :                 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
    2944                 :             :             }
    2945                 :             :         }
    2946                 :             : 
    2947                 :             :         /*
    2948                 :             :          * Now process any inherited default expressions, adjusting attnos
    2949                 :             :          * using the completed newattmap map.
    2950                 :             :          */
    2951   [ +  +  +  +  :        8763 :         forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
          +  +  +  +  +  
             +  +  -  +  
                      + ]
    2952                 :             :         {
    2953                 :         566 :             Node       *this_default = (Node *) lfirst(lc1);
    2954                 :         566 :             ColumnDef  *def = (ColumnDef *) lfirst(lc2);
    2955                 :             :             bool        found_whole_row;
    2956                 :             : 
    2957                 :             :             /* Adjust Vars to match new table's column numbering */
    2958                 :         566 :             this_default = map_variable_attnos(this_default,
    2959                 :             :                                                1, 0,
    2960                 :             :                                                newattmap,
    2961                 :             :                                                InvalidOid, &found_whole_row);
    2962                 :             : 
    2963                 :             :             /*
    2964                 :             :              * For the moment we have to reject whole-row variables.  We could
    2965                 :             :              * convert them, if we knew the new table's rowtype OID, but that
    2966                 :             :              * hasn't been assigned yet.  (A variable could only appear in a
    2967                 :             :              * generation expression, so the error message is correct.)
    2968                 :             :              */
    2969         [ -  + ]:         566 :             if (found_whole_row)
    2970         [ #  # ]:           0 :                 ereport(ERROR,
    2971                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    2972                 :             :                          errmsg("cannot convert whole-row table reference"),
    2973                 :             :                          errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
    2974                 :             :                                    def->colname,
    2975                 :             :                                    RelationGetRelationName(relation))));
    2976                 :             : 
    2977                 :             :             /*
    2978                 :             :              * If we already had a default from some prior parent, check to
    2979                 :             :              * see if they are the same.  If so, no problem; if not, mark the
    2980                 :             :              * column as having a bogus default.  Below, we will complain if
    2981                 :             :              * the bogus default isn't overridden by the child columns.
    2982                 :             :              */
    2983                 :             :             Assert(def->raw_default == NULL);
    2984         [ +  + ]:         566 :             if (def->cooked_default == NULL)
    2985                 :         538 :                 def->cooked_default = this_default;
    2986         [ +  + ]:          28 :             else if (!equal(def->cooked_default, this_default))
    2987                 :             :             {
    2988                 :          24 :                 def->cooked_default = &bogus_marker;
    2989                 :          24 :                 have_bogus_defaults = true;
    2990                 :             :             }
    2991                 :             :         }
    2992                 :             : 
    2993                 :             :         /*
    2994                 :             :          * Now copy the CHECK constraints of this parent, adjusting attnos
    2995                 :             :          * using the completed newattmap map.  Identically named constraints
    2996                 :             :          * are merged if possible, else we throw error.
    2997                 :             :          */
    2998   [ +  +  +  + ]:        8197 :         if (constr && constr->num_check > 0)
    2999                 :             :         {
    3000                 :         293 :             ConstrCheck *check = constr->check;
    3001                 :             : 
    3002         [ +  + ]:         866 :             for (int i = 0; i < constr->num_check; i++)
    3003                 :             :             {
    3004                 :         573 :                 char       *name = check[i].ccname;
    3005                 :             :                 Node       *expr;
    3006                 :             :                 bool        found_whole_row;
    3007                 :             : 
    3008                 :             :                 /* ignore if the constraint is non-inheritable */
    3009         [ +  + ]:         573 :                 if (check[i].ccnoinherit)
    3010                 :          32 :                     continue;
    3011                 :             : 
    3012                 :             :                 /* Adjust Vars to match new table's column numbering */
    3013                 :         541 :                 expr = map_variable_attnos(stringToNode(check[i].ccbin),
    3014                 :             :                                            1, 0,
    3015                 :             :                                            newattmap,
    3016                 :             :                                            InvalidOid, &found_whole_row);
    3017                 :             : 
    3018                 :             :                 /*
    3019                 :             :                  * For the moment we have to reject whole-row variables. We
    3020                 :             :                  * could convert them, if we knew the new table's rowtype OID,
    3021                 :             :                  * but that hasn't been assigned yet.
    3022                 :             :                  */
    3023         [ -  + ]:         541 :                 if (found_whole_row)
    3024         [ #  # ]:           0 :                     ereport(ERROR,
    3025                 :             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3026                 :             :                              errmsg("cannot convert whole-row table reference"),
    3027                 :             :                              errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
    3028                 :             :                                        name,
    3029                 :             :                                        RelationGetRelationName(relation))));
    3030                 :             : 
    3031                 :         541 :                 constraints = MergeCheckConstraint(constraints, name, expr,
    3032                 :         541 :                                                    check[i].ccenforced);
    3033                 :             :             }
    3034                 :             :         }
    3035                 :             : 
    3036                 :             :         /*
    3037                 :             :          * Also copy the not-null constraints from this parent.  The
    3038                 :             :          * attnotnull markings were already installed above.
    3039                 :             :          */
    3040   [ +  +  +  +  :       18138 :         foreach_ptr(CookedConstraint, nn, nnconstrs)
                   +  + ]
    3041                 :             :         {
    3042                 :             :             Assert(nn->contype == CONSTR_NOTNULL);
    3043                 :             : 
    3044                 :        1744 :             nn->attnum = newattmap->attnums[nn->attnum - 1];
    3045                 :             : 
    3046                 :        1744 :             nnconstraints = lappend(nnconstraints, nn);
    3047                 :             :         }
    3048                 :             : 
    3049                 :        8197 :         free_attrmap(newattmap);
    3050                 :             : 
    3051                 :             :         /*
    3052                 :             :          * Close the parent rel, but keep our lock on it until xact commit.
    3053                 :             :          * That will prevent someone else from deleting or ALTERing the parent
    3054                 :             :          * before the child is committed.
    3055                 :             :          */
    3056                 :        8197 :         table_close(relation, NoLock);
    3057                 :             :     }
    3058                 :             : 
    3059                 :             :     /*
    3060                 :             :      * If we had no inherited attributes, the result columns are just the
    3061                 :             :      * explicitly declared columns.  Otherwise, we need to merge the declared
    3062                 :             :      * columns into the inherited column list.  Although, we never have any
    3063                 :             :      * explicitly declared columns if the table is a partition.
    3064                 :             :      */
    3065         [ +  + ]:       42355 :     if (inh_columns != NIL)
    3066                 :             :     {
    3067                 :        7895 :         int         newcol_attno = 0;
    3068                 :             : 
    3069   [ +  +  +  +  :        8547 :         foreach(lc, columns)
                   +  + ]
    3070                 :             :         {
    3071                 :         704 :             ColumnDef  *newdef = lfirst_node(ColumnDef, lc);
    3072                 :         704 :             char       *attributeName = newdef->colname;
    3073                 :             :             int         exist_attno;
    3074                 :             : 
    3075                 :             :             /*
    3076                 :             :              * Partitions have only one parent and have no column definitions
    3077                 :             :              * of their own, so conflict should never occur.
    3078                 :             :              */
    3079                 :             :             Assert(!is_partition);
    3080                 :             : 
    3081                 :         704 :             newcol_attno++;
    3082                 :             : 
    3083                 :             :             /*
    3084                 :             :              * Does it match some inherited column?
    3085                 :             :              */
    3086                 :         704 :             exist_attno = findAttrByName(attributeName, inh_columns);
    3087         [ +  + ]:         704 :             if (exist_attno > 0)
    3088                 :             :             {
    3089                 :             :                 /*
    3090                 :             :                  * Yes, try to merge the two column definitions.
    3091                 :             :                  */
    3092                 :         251 :                 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
    3093                 :             :             }
    3094                 :             :             else
    3095                 :             :             {
    3096                 :             :                 /*
    3097                 :             :                  * No, attach new column unchanged to result columns.
    3098                 :             :                  */
    3099                 :         453 :                 inh_columns = lappend(inh_columns, newdef);
    3100                 :             :             }
    3101                 :             :         }
    3102                 :             : 
    3103                 :        7843 :         columns = inh_columns;
    3104                 :             : 
    3105                 :             :         /*
    3106                 :             :          * Check that we haven't exceeded the legal # of columns after merging
    3107                 :             :          * in inherited columns.
    3108                 :             :          */
    3109         [ -  + ]:        7843 :         if (list_length(columns) > MaxHeapAttributeNumber)
    3110         [ #  # ]:           0 :             ereport(ERROR,
    3111                 :             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
    3112                 :             :                      errmsg("tables can have at most %d columns",
    3113                 :             :                             MaxHeapAttributeNumber)));
    3114                 :             :     }
    3115                 :             : 
    3116                 :             :     /*
    3117                 :             :      * Now that we have the column definition list for a partition, we can
    3118                 :             :      * check whether the columns referenced in the column constraint specs
    3119                 :             :      * actually exist.  Also, merge column defaults.
    3120                 :             :      */
    3121         [ +  + ]:       42303 :     if (is_partition)
    3122                 :             :     {
    3123   [ +  +  +  +  :        6667 :         foreach(lc, saved_columns)
                   +  + ]
    3124                 :             :         {
    3125                 :         151 :             ColumnDef  *restdef = lfirst(lc);
    3126                 :         151 :             bool        found = false;
    3127                 :             :             ListCell   *l;
    3128                 :             : 
    3129   [ +  -  +  +  :         549 :             foreach(l, columns)
                   +  + ]
    3130                 :             :             {
    3131                 :         422 :                 ColumnDef  *coldef = lfirst(l);
    3132                 :             : 
    3133         [ +  + ]:         422 :                 if (strcmp(coldef->colname, restdef->colname) == 0)
    3134                 :             :                 {
    3135                 :         151 :                     found = true;
    3136                 :             : 
    3137                 :             :                     /*
    3138                 :             :                      * Check for conflicts related to generated columns.
    3139                 :             :                      *
    3140                 :             :                      * Same rules as above: generated-ness has to match the
    3141                 :             :                      * parent, but the contents of the generation expression
    3142                 :             :                      * can be different.
    3143                 :             :                      */
    3144         [ +  + ]:         151 :                     if (coldef->generated)
    3145                 :             :                     {
    3146   [ +  -  +  + ]:          80 :                         if (restdef->raw_default && !restdef->generated)
    3147         [ +  - ]:           8 :                             ereport(ERROR,
    3148                 :             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3149                 :             :                                      errmsg("column \"%s\" inherits from generated column but specifies default",
    3150                 :             :                                             restdef->colname)));
    3151         [ -  + ]:          72 :                         if (restdef->identity)
    3152         [ #  # ]:           0 :                             ereport(ERROR,
    3153                 :             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3154                 :             :                                      errmsg("column \"%s\" inherits from generated column but specifies identity",
    3155                 :             :                                             restdef->colname)));
    3156                 :             :                     }
    3157                 :             :                     else
    3158                 :             :                     {
    3159         [ +  + ]:          71 :                         if (restdef->generated)
    3160         [ +  - ]:           8 :                             ereport(ERROR,
    3161                 :             :                                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3162                 :             :                                      errmsg("child column \"%s\" specifies generation expression",
    3163                 :             :                                             restdef->colname),
    3164                 :             :                                      errhint("A child table column cannot be generated unless its parent column is.")));
    3165                 :             :                     }
    3166                 :             : 
    3167   [ +  +  +  -  :         135 :                     if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
                   +  + ]
    3168   [ +  -  +  +  :           8 :                         ereport(ERROR,
                   +  + ]
    3169                 :             :                                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3170                 :             :                                  errmsg("column \"%s\" inherits from generated column of different kind",
    3171                 :             :                                         restdef->colname),
    3172                 :             :                                  errdetail("Parent column is %s, child column is %s.",
    3173                 :             :                                            coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
    3174                 :             :                                            restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
    3175                 :             : 
    3176                 :             :                     /*
    3177                 :             :                      * Override the parent's default value for this column
    3178                 :             :                      * (coldef->cooked_default) with the partition's local
    3179                 :             :                      * definition (restdef->raw_default), if there's one. It
    3180                 :             :                      * should be physically impossible to get a cooked default
    3181                 :             :                      * in the local definition or a raw default in the
    3182                 :             :                      * inherited definition, but make sure they're nulls, for
    3183                 :             :                      * future-proofing.
    3184                 :             :                      */
    3185                 :             :                     Assert(restdef->cooked_default == NULL);
    3186                 :             :                     Assert(coldef->raw_default == NULL);
    3187         [ +  + ]:         127 :                     if (restdef->raw_default)
    3188                 :             :                     {
    3189                 :          79 :                         coldef->raw_default = restdef->raw_default;
    3190                 :          79 :                         coldef->cooked_default = NULL;
    3191                 :             :                     }
    3192                 :             :                 }
    3193                 :             :             }
    3194                 :             : 
    3195                 :             :             /* complain for constraints on columns not in parent */
    3196         [ -  + ]:         127 :             if (!found)
    3197         [ #  # ]:           0 :                 ereport(ERROR,
    3198                 :             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
    3199                 :             :                          errmsg("column \"%s\" does not exist",
    3200                 :             :                                 restdef->colname)));
    3201                 :             :         }
    3202                 :             :     }
    3203                 :             : 
    3204                 :             :     /*
    3205                 :             :      * If we found any conflicting parent default values, check to make sure
    3206                 :             :      * they were overridden by the child.
    3207                 :             :      */
    3208         [ +  + ]:       42279 :     if (have_bogus_defaults)
    3209                 :             :     {
    3210   [ +  -  +  +  :          60 :         foreach(lc, columns)
                   +  + ]
    3211                 :             :         {
    3212                 :          48 :             ColumnDef  *def = lfirst(lc);
    3213                 :             : 
    3214         [ +  + ]:          48 :             if (def->cooked_default == &bogus_marker)
    3215                 :             :             {
    3216         [ +  + ]:          12 :                 if (def->generated)
    3217         [ +  - ]:           8 :                     ereport(ERROR,
    3218                 :             :                             (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3219                 :             :                              errmsg("column \"%s\" inherits conflicting generation expressions",
    3220                 :             :                                     def->colname),
    3221                 :             :                              errhint("To resolve the conflict, specify a generation expression explicitly.")));
    3222                 :             :                 else
    3223         [ +  - ]:           4 :                     ereport(ERROR,
    3224                 :             :                             (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3225                 :             :                              errmsg("column \"%s\" inherits conflicting default values",
    3226                 :             :                                     def->colname),
    3227                 :             :                              errhint("To resolve the conflict, specify a default explicitly.")));
    3228                 :             :             }
    3229                 :             :         }
    3230                 :             :     }
    3231                 :             : 
    3232                 :       42267 :     *supconstr = constraints;
    3233                 :       42267 :     *supnotnulls = nnconstraints;
    3234                 :             : 
    3235                 :       42267 :     return columns;
    3236                 :             : }
    3237                 :             : 
    3238                 :             : 
    3239                 :             : /*
    3240                 :             :  * MergeCheckConstraint
    3241                 :             :  *      Try to merge an inherited CHECK constraint with previous ones
    3242                 :             :  *
    3243                 :             :  * If we inherit identically-named constraints from multiple parents, we must
    3244                 :             :  * merge them, or throw an error if they don't have identical definitions.
    3245                 :             :  *
    3246                 :             :  * constraints is a list of CookedConstraint structs for previous constraints.
    3247                 :             :  *
    3248                 :             :  * If the new constraint matches an existing one, then the existing
    3249                 :             :  * constraint's inheritance count is updated.  If there is a conflict (same
    3250                 :             :  * name but different expression), throw an error.  If the constraint neither
    3251                 :             :  * matches nor conflicts with an existing one, a new constraint is appended to
    3252                 :             :  * the list.
    3253                 :             :  */
    3254                 :             : static List *
    3255                 :         541 : MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
    3256                 :             : {
    3257                 :             :     ListCell   *lc;
    3258                 :             :     CookedConstraint *newcon;
    3259                 :             : 
    3260   [ +  +  +  +  :        1569 :     foreach(lc, constraints)
                   +  + ]
    3261                 :             :     {
    3262                 :        1136 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
    3263                 :             : 
    3264                 :             :         Assert(ccon->contype == CONSTR_CHECK);
    3265                 :             : 
    3266                 :             :         /* Non-matching names never conflict */
    3267         [ +  + ]:        1136 :         if (strcmp(ccon->name, name) != 0)
    3268                 :        1028 :             continue;
    3269                 :             : 
    3270         [ +  - ]:         108 :         if (equal(expr, ccon->expr))
    3271                 :             :         {
    3272                 :             :             /* OK to merge constraint with existing */
    3273         [ -  + ]:         108 :             if (pg_add_s16_overflow(ccon->inhcount, 1,
    3274                 :             :                                     &ccon->inhcount))
    3275         [ #  # ]:           0 :                 ereport(ERROR,
    3276                 :             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3277                 :             :                         errmsg("too many inheritance parents"));
    3278                 :             : 
    3279                 :             :             /*
    3280                 :             :              * When enforceability differs, the merged constraint should be
    3281                 :             :              * marked as ENFORCED because one of the parents is ENFORCED.
    3282                 :             :              */
    3283   [ +  +  +  + ]:         108 :             if (!ccon->is_enforced && is_enforced)
    3284                 :             :             {
    3285                 :          32 :                 ccon->is_enforced = true;
    3286                 :          32 :                 ccon->skip_validation = false;
    3287                 :             :             }
    3288                 :             : 
    3289                 :         108 :             return constraints;
    3290                 :             :         }
    3291                 :             : 
    3292         [ #  # ]:           0 :         ereport(ERROR,
    3293                 :             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
    3294                 :             :                  errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
    3295                 :             :                         name)));
    3296                 :             :     }
    3297                 :             : 
    3298                 :             :     /*
    3299                 :             :      * Constraint couldn't be merged with an existing one and also didn't
    3300                 :             :      * conflict with an existing one, so add it as a new one to the list.
    3301                 :             :      */
    3302                 :         433 :     newcon = palloc0_object(CookedConstraint);
    3303                 :         433 :     newcon->contype = CONSTR_CHECK;
    3304                 :         433 :     newcon->name = pstrdup(name);
    3305                 :         433 :     newcon->expr = expr;
    3306                 :         433 :     newcon->inhcount = 1;
    3307                 :         433 :     newcon->is_enforced = is_enforced;
    3308                 :         433 :     newcon->skip_validation = !is_enforced;
    3309                 :         433 :     return lappend(constraints, newcon);
    3310                 :             : }
    3311                 :             : 
    3312                 :             : /*
    3313                 :             :  * MergeChildAttribute
    3314                 :             :  *      Merge given child attribute definition into given inherited attribute.
    3315                 :             :  *
    3316                 :             :  * Input arguments:
    3317                 :             :  * 'inh_columns' is the list of inherited ColumnDefs.
    3318                 :             :  * 'exist_attno' is the number of the inherited attribute in inh_columns
    3319                 :             :  * 'newcol_attno' is the attribute number in child table's schema definition
    3320                 :             :  * 'newdef' is the column/attribute definition from the child table.
    3321                 :             :  *
    3322                 :             :  * The ColumnDef in 'inh_columns' list is modified.  The child attribute's
    3323                 :             :  * ColumnDef remains unchanged.
    3324                 :             :  *
    3325                 :             :  * Notes:
    3326                 :             :  * - The attribute is merged according to the rules laid out in the prologue
    3327                 :             :  *   of MergeAttributes().
    3328                 :             :  * - If matching inherited attribute exists but the child attribute can not be
    3329                 :             :  *   merged into it, the function throws respective errors.
    3330                 :             :  * - A partition can not have its own column definitions. Hence this function
    3331                 :             :  *   is applicable only to a regular inheritance child.
    3332                 :             :  */
    3333                 :             : static void
    3334                 :         251 : MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
    3335                 :             : {
    3336                 :         251 :     char       *attributeName = newdef->colname;
    3337                 :             :     ColumnDef  *inhdef;
    3338                 :             :     Oid         inhtypeid,
    3339                 :             :                 newtypeid;
    3340                 :             :     int32       inhtypmod,
    3341                 :             :                 newtypmod;
    3342                 :             :     Oid         inhcollid,
    3343                 :             :                 newcollid;
    3344                 :             : 
    3345         [ +  + ]:         251 :     if (exist_attno == newcol_attno)
    3346         [ +  - ]:         229 :         ereport(NOTICE,
    3347                 :             :                 (errmsg("merging column \"%s\" with inherited definition",
    3348                 :             :                         attributeName)));
    3349                 :             :     else
    3350         [ +  - ]:          22 :         ereport(NOTICE,
    3351                 :             :                 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
    3352                 :             :                  errdetail("User-specified column moved to the position of the inherited column.")));
    3353                 :             : 
    3354                 :         251 :     inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
    3355                 :             : 
    3356                 :             :     /*
    3357                 :             :      * Must have the same type and typmod
    3358                 :             :      */
    3359                 :         251 :     typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
    3360                 :         251 :     typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
    3361   [ +  +  +  + ]:         251 :     if (inhtypeid != newtypeid || inhtypmod != newtypmod)
    3362         [ +  - ]:           8 :         ereport(ERROR,
    3363                 :             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3364                 :             :                  errmsg("column \"%s\" has a type conflict",
    3365                 :             :                         attributeName),
    3366                 :             :                  errdetail("%s versus %s",
    3367                 :             :                            format_type_with_typemod(inhtypeid, inhtypmod),
    3368                 :             :                            format_type_with_typemod(newtypeid, newtypmod))));
    3369                 :             : 
    3370                 :             :     /*
    3371                 :             :      * Must have the same collation
    3372                 :             :      */
    3373                 :         243 :     inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
    3374                 :         243 :     newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
    3375         [ +  + ]:         243 :     if (inhcollid != newcollid)
    3376         [ +  - ]:           4 :         ereport(ERROR,
    3377                 :             :                 (errcode(ERRCODE_COLLATION_MISMATCH),
    3378                 :             :                  errmsg("column \"%s\" has a collation conflict",
    3379                 :             :                         attributeName),
    3380                 :             :                  errdetail("\"%s\" versus \"%s\"",
    3381                 :             :                            get_collation_name(inhcollid),
    3382                 :             :                            get_collation_name(newcollid))));
    3383                 :             : 
    3384                 :             :     /*
    3385                 :             :      * Identity is never inherited by a regular inheritance child. Pick
    3386                 :             :      * child's identity definition if there's one.
    3387                 :             :      */
    3388                 :         239 :     inhdef->identity = newdef->identity;
    3389                 :             : 
    3390                 :             :     /*
    3391                 :             :      * Copy storage parameter
    3392                 :             :      */
    3393         [ -  + ]:         239 :     if (inhdef->storage == 0)
    3394                 :           0 :         inhdef->storage = newdef->storage;
    3395   [ +  +  +  + ]:         239 :     else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
    3396         [ +  - ]:           4 :         ereport(ERROR,
    3397                 :             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3398                 :             :                  errmsg("column \"%s\" has a storage parameter conflict",
    3399                 :             :                         attributeName),
    3400                 :             :                  errdetail("%s versus %s",
    3401                 :             :                            storage_name(inhdef->storage),
    3402                 :             :                            storage_name(newdef->storage))));
    3403                 :             : 
    3404                 :             :     /*
    3405                 :             :      * Copy compression parameter
    3406                 :             :      */
    3407         [ +  + ]:         235 :     if (inhdef->compression == NULL)
    3408                 :         231 :         inhdef->compression = newdef->compression;
    3409         [ +  - ]:           4 :     else if (newdef->compression != NULL)
    3410                 :             :     {
    3411         [ +  - ]:           4 :         if (strcmp(inhdef->compression, newdef->compression) != 0)
    3412         [ +  - ]:           4 :             ereport(ERROR,
    3413                 :             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    3414                 :             :                      errmsg("column \"%s\" has a compression method conflict",
    3415                 :             :                             attributeName),
    3416                 :             :                      errdetail("%s versus %s", inhdef->compression, newdef->compression)));
    3417                 :             :     }
    3418                 :             : 
    3419                 :             :     /*
    3420                 :             :      * Merge of not-null constraints = OR 'em together
    3421                 :             :      */
    3422                 :         231 :     inhdef->is_not_null |= newdef->is_not_null;
    3423                 :             : 
    3424                 :             :     /*
    3425                 :             :      * Check for conflicts related to generated columns.
    3426                 :             :      *
    3427                 :             :      * If the parent column is generated, the child column will be made a
    3428                 :             :      * generated column if it isn't already.  If it is a generated column,
    3429                 :             :      * we'll take its generation expression in preference to the parent's.  We
    3430                 :             :      * must check that the child column doesn't specify a default value or
    3431                 :             :      * identity, which matches the rules for a single column in
    3432                 :             :      * parse_utilcmd.c.
    3433                 :             :      *
    3434                 :             :      * Conversely, if the parent column is not generated, the child column
    3435                 :             :      * can't be either.  (We used to allow that, but it results in being able
    3436                 :             :      * to override the generation expression via UPDATEs through the parent.)
    3437                 :             :      */
    3438         [ +  + ]:         231 :     if (inhdef->generated)
    3439                 :             :     {
    3440   [ +  +  +  + ]:          41 :         if (newdef->raw_default && !newdef->generated)
    3441         [ +  - ]:           8 :             ereport(ERROR,
    3442                 :             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3443                 :             :                      errmsg("column \"%s\" inherits from generated column but specifies default",
    3444                 :             :                             inhdef->colname)));
    3445         [ +  + ]:          33 :         if (newdef->identity)
    3446         [ +  - ]:           8 :             ereport(ERROR,
    3447                 :             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3448                 :             :                      errmsg("column \"%s\" inherits from generated column but specifies identity",
    3449                 :             :                             inhdef->colname)));
    3450                 :             :     }
    3451                 :             :     else
    3452                 :             :     {
    3453         [ +  + ]:         190 :         if (newdef->generated)
    3454         [ +  - ]:           8 :             ereport(ERROR,
    3455                 :             :                     (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3456                 :             :                      errmsg("child column \"%s\" specifies generation expression",
    3457                 :             :                             inhdef->colname),
    3458                 :             :                      errhint("A child table column cannot be generated unless its parent column is.")));
    3459                 :             :     }
    3460                 :             : 
    3461   [ +  +  +  +  :         207 :     if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
                   +  + ]
    3462   [ +  -  +  +  :           8 :         ereport(ERROR,
                   +  + ]
    3463                 :             :                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
    3464                 :             :                  errmsg("column \"%s\" inherits from generated column of different kind",
    3465                 :             :                         inhdef->colname),
    3466                 :             :                  errdetail("Parent column is %s, child column is %s.",
    3467                 :             :                            inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
    3468                 :             :                            newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
    3469                 :             : 
    3470                 :             :     /*
    3471                 :             :      * If new def has a default, override previous default
    3472                 :             :      */
    3473         [ +  + ]:         199 :     if (newdef->raw_default != NULL)
    3474                 :             :     {
    3475                 :          20 :         inhdef->raw_default = newdef->raw_default;
    3476                 :          20 :         inhdef->cooked_default = newdef->cooked_default;
    3477                 :             :     }
    3478                 :             : 
    3479                 :             :     /* Mark the column as locally defined */
    3480                 :         199 :     inhdef->is_local = true;
    3481                 :         199 : }
    3482                 :             : 
    3483                 :             : /*
    3484                 :             :  * MergeInheritedAttribute
    3485                 :             :  *      Merge given parent attribute definition into specified attribute
    3486                 :             :  *      inherited from the previous parents.
    3487                 :             :  *
    3488                 :             :  * Input arguments:
    3489                 :             :  * 'inh_columns' is the list of previously inherited ColumnDefs.
    3490                 :             :  * 'exist_attno' is the number the existing matching attribute in inh_columns.
    3491                 :             :  * 'newdef' is the new parent column/attribute definition to be merged.
    3492                 :             :  *
    3493                 :             :  * The matching ColumnDef in 'inh_columns' list is modified and returned.
    3494                 :             :  *
    3495                 :             :  * Notes:
    3496                 :             :  * - The attribute is merged according to the rules laid out in the prologue
    3497                 :             :  *   of MergeAttributes().
    3498                 :             :  * - If matching inherited attribute exists but the new attribute can not be
    3499                 :             :  *   merged into it, the function throws respective errors.
    3500                 :             :  * - A partition inherits from only a single parent. Hence this function is
    3501                 :             :  *   applicable only to a regular inheritance.
    3502                 :             :  */
    3503                 :             : static ColumnDef *
    3504                 :         253 : MergeInheritedAttribute(List *inh_columns,
    3505                 :             :                         int exist_attno,
    3506                 :             :                         const ColumnDef *newdef)
    3507                 :             : {
    3508                 :         253 :     char       *attributeName = newdef->colname;
    3509                 :             :     ColumnDef  *prevdef;
    3510                 :             :     Oid         prevtypeid,
    3511                 :             :                 newtypeid;
    3512                 :             :     int32       prevtypmod,
    3513                 :             :                 newtypmod;
    3514                 :             :     Oid         prevcollid,
    3515                 :             :                 newcollid;
    3516                 :             : 
    3517         [ +  - ]:         253 :     ereport(NOTICE,
    3518                 :             :             (errmsg("merging multiple inherited definitions of column \"%s\"",
    3519                 :             :                     attributeName)));
    3520                 :         253 :     prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
    3521                 :             : 
    3522                 :             :     /*
    3523                 :             :      * Must have the same type and typmod
    3524                 :             :      */
    3525                 :         253 :     typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
    3526                 :         253 :     typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
    3527   [ +  -  -  + ]:         253 :     if (prevtypeid != newtypeid || prevtypmod != newtypmod)
    3528         [ #  # ]:           0 :         ereport(ERROR,
    3529                 :             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3530                 :             :                  errmsg("inherited column \"%s\" has a type conflict",
    3531                 :             :                         attributeName),
    3532                 :             :                  errdetail("%s versus %s",
    3533                 :             :                            format_type_with_typemod(prevtypeid, prevtypmod),
    3534                 :             :                            format_type_with_typemod(newtypeid, newtypmod))));
    3535                 :             : 
    3536                 :             :     /*
    3537                 :             :      * Must have the same collation
    3538                 :             :      */
    3539                 :         253 :     prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
    3540                 :         253 :     newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
    3541         [ -  + ]:         253 :     if (prevcollid != newcollid)
    3542         [ #  # ]:           0 :         ereport(ERROR,
    3543                 :             :                 (errcode(ERRCODE_COLLATION_MISMATCH),
    3544                 :             :                  errmsg("inherited column \"%s\" has a collation conflict",
    3545                 :             :                         attributeName),
    3546                 :             :                  errdetail("\"%s\" versus \"%s\"",
    3547                 :             :                            get_collation_name(prevcollid),
    3548                 :             :                            get_collation_name(newcollid))));
    3549                 :             : 
    3550                 :             :     /*
    3551                 :             :      * Copy/check storage parameter
    3552                 :             :      */
    3553         [ -  + ]:         253 :     if (prevdef->storage == 0)
    3554                 :           0 :         prevdef->storage = newdef->storage;
    3555         [ +  + ]:         253 :     else if (prevdef->storage != newdef->storage)
    3556         [ +  - ]:           4 :         ereport(ERROR,
    3557                 :             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3558                 :             :                  errmsg("inherited column \"%s\" has a storage parameter conflict",
    3559                 :             :                         attributeName),
    3560                 :             :                  errdetail("%s versus %s",
    3561                 :             :                            storage_name(prevdef->storage),
    3562                 :             :                            storage_name(newdef->storage))));
    3563                 :             : 
    3564                 :             :     /*
    3565                 :             :      * Copy/check compression parameter
    3566                 :             :      */
    3567         [ +  + ]:         249 :     if (prevdef->compression == NULL)
    3568                 :         237 :         prevdef->compression = newdef->compression;
    3569         [ +  + ]:          12 :     else if (newdef->compression != NULL)
    3570                 :             :     {
    3571         [ +  - ]:           4 :         if (strcmp(prevdef->compression, newdef->compression) != 0)
    3572         [ +  - ]:           4 :             ereport(ERROR,
    3573                 :             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
    3574                 :             :                      errmsg("column \"%s\" has a compression method conflict",
    3575                 :             :                             attributeName),
    3576                 :             :                      errdetail("%s versus %s",
    3577                 :             :                                prevdef->compression, newdef->compression)));
    3578                 :             :     }
    3579                 :             : 
    3580                 :             :     /*
    3581                 :             :      * Check for GENERATED conflicts
    3582                 :             :      */
    3583         [ +  + ]:         245 :     if (prevdef->generated != newdef->generated)
    3584         [ +  - ]:          16 :         ereport(ERROR,
    3585                 :             :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
    3586                 :             :                  errmsg("inherited column \"%s\" has a generation conflict",
    3587                 :             :                         attributeName)));
    3588                 :             : 
    3589                 :             :     /*
    3590                 :             :      * Default and other constraints are handled by the caller.
    3591                 :             :      */
    3592                 :             : 
    3593         [ -  + ]:         229 :     if (pg_add_s16_overflow(prevdef->inhcount, 1,
    3594                 :             :                             &prevdef->inhcount))
    3595         [ #  # ]:           0 :         ereport(ERROR,
    3596                 :             :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    3597                 :             :                 errmsg("too many inheritance parents"));
    3598                 :             : 
    3599                 :         229 :     return prevdef;
    3600                 :             : }
    3601                 :             : 
    3602                 :             : /*
    3603                 :             :  * StoreCatalogInheritance
    3604                 :             :  *      Updates the system catalogs with proper inheritance information.
    3605                 :             :  *
    3606                 :             :  * supers is a list of the OIDs of the new relation's direct ancestors.
    3607                 :             :  */
    3608                 :             : static void
    3609                 :       41819 : StoreCatalogInheritance(Oid relationId, List *supers,
    3610                 :             :                         bool child_is_partition)
    3611                 :             : {
    3612                 :             :     Relation    relation;
    3613                 :             :     int32       seqNumber;
    3614                 :             :     ListCell   *entry;
    3615                 :             : 
    3616                 :             :     /*
    3617                 :             :      * sanity checks
    3618                 :             :      */
    3619                 :             :     Assert(OidIsValid(relationId));
    3620                 :             : 
    3621         [ +  + ]:       41819 :     if (supers == NIL)
    3622                 :       34216 :         return;
    3623                 :             : 
    3624                 :             :     /*
    3625                 :             :      * Store INHERITS information in pg_inherits using direct ancestors only.
    3626                 :             :      * Also enter dependencies on the direct ancestors, and make sure they are
    3627                 :             :      * marked with relhassubclass = true.
    3628                 :             :      *
    3629                 :             :      * (Once upon a time, both direct and indirect ancestors were found here
    3630                 :             :      * and then entered into pg_ipl.  Since that catalog doesn't exist
    3631                 :             :      * anymore, there's no need to look for indirect ancestors.)
    3632                 :             :      */
    3633                 :        7603 :     relation = table_open(InheritsRelationId, RowExclusiveLock);
    3634                 :             : 
    3635                 :        7603 :     seqNumber = 1;
    3636   [ +  -  +  +  :       15440 :     foreach(entry, supers)
                   +  + ]
    3637                 :             :     {
    3638                 :        7837 :         Oid         parentOid = lfirst_oid(entry);
    3639                 :             : 
    3640                 :        7837 :         StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
    3641                 :             :                                  child_is_partition);
    3642                 :        7837 :         seqNumber++;
    3643                 :             :     }
    3644                 :             : 
    3645                 :        7603 :     table_close(relation, RowExclusiveLock);
    3646                 :             : }
    3647                 :             : 
    3648                 :             : /*
    3649                 :             :  * Make catalog entries showing relationId as being an inheritance child
    3650                 :             :  * of parentOid.  inhRelation is the already-opened pg_inherits catalog.
    3651                 :             :  */
    3652                 :             : static void
    3653                 :        9967 : StoreCatalogInheritance1(Oid relationId, Oid parentOid,
    3654                 :             :                          int32 seqNumber, Relation inhRelation,
    3655                 :             :                          bool child_is_partition)
    3656                 :             : {
    3657                 :             :     ObjectAddress childobject,
    3658                 :             :                 parentobject;
    3659                 :             : 
    3660                 :             :     /* store the pg_inherits row */
    3661                 :        9967 :     StoreSingleInheritance(relationId, parentOid, seqNumber);
    3662                 :             : 
    3663                 :             :     /*
    3664                 :             :      * Store a dependency too
    3665                 :             :      */
    3666                 :        9967 :     parentobject.classId = RelationRelationId;
    3667                 :        9967 :     parentobject.objectId = parentOid;
    3668                 :        9967 :     parentobject.objectSubId = 0;
    3669                 :        9967 :     childobject.classId = RelationRelationId;
    3670                 :        9967 :     childobject.objectId = relationId;
    3671                 :        9967 :     childobject.objectSubId = 0;
    3672                 :             : 
    3673         [ +  + ]:        9967 :     recordDependencyOn(&childobject, &parentobject,
    3674                 :             :                        child_dependency_type(child_is_partition));
    3675                 :             : 
    3676                 :             :     /*
    3677                 :             :      * Post creation hook of this inheritance. Since object_access_hook
    3678                 :             :      * doesn't take multiple object identifiers, we relay oid of parent
    3679                 :             :      * relation using auxiliary_id argument.
    3680                 :             :      */
    3681         [ -  + ]:        9967 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
    3682                 :             :                                  relationId, 0,
    3683                 :             :                                  parentOid, false);
    3684                 :             : 
    3685                 :             :     /*
    3686                 :             :      * Mark the parent as having subclasses.
    3687                 :             :      */
    3688                 :        9967 :     SetRelationHasSubclass(parentOid, true);
    3689                 :        9967 : }
    3690                 :             : 
    3691                 :             : /*
    3692                 :             :  * Look for an existing column entry with the given name.
    3693                 :             :  *
    3694                 :             :  * Returns the index (starting with 1) if attribute already exists in columns,
    3695                 :             :  * 0 if it doesn't.
    3696                 :             :  */
    3697                 :             : static int
    3698                 :       16935 : findAttrByName(const char *attributeName, const List *columns)
    3699                 :             : {
    3700                 :             :     ListCell   *lc;
    3701                 :       16935 :     int         i = 1;
    3702                 :             : 
    3703   [ +  +  +  +  :       30694 :     foreach(lc, columns)
                   +  + ]
    3704                 :             :     {
    3705         [ +  + ]:       14263 :         if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
    3706                 :         504 :             return i;
    3707                 :             : 
    3708                 :       13759 :         i++;
    3709                 :             :     }
    3710                 :       16431 :     return 0;
    3711                 :             : }
    3712                 :             : 
    3713                 :             : 
    3714                 :             : /*
    3715                 :             :  * SetRelationHasSubclass
    3716                 :             :  *      Set the value of the relation's relhassubclass field in pg_class.
    3717                 :             :  *
    3718                 :             :  * It's always safe to set this field to true, because all SQL commands are
    3719                 :             :  * ready to see true and then find no children.  On the other hand, commands
    3720                 :             :  * generally assume zero children if this is false.
    3721                 :             :  *
    3722                 :             :  * Caller must hold any self-exclusive lock until end of transaction.  If the
    3723                 :             :  * new value is false, caller must have acquired that lock before reading the
    3724                 :             :  * evidence that justified the false value.  That way, it properly waits if
    3725                 :             :  * another backend is simultaneously concluding no need to change the tuple
    3726                 :             :  * (new and old values are true).
    3727                 :             :  *
    3728                 :             :  * NOTE: an important side-effect of this operation is that an SI invalidation
    3729                 :             :  * message is sent out to all backends --- including me --- causing plans
    3730                 :             :  * referencing the relation to be rebuilt with the new list of children.
    3731                 :             :  * This must happen even if we find that no change is needed in the pg_class
    3732                 :             :  * row.
    3733                 :             :  */
    3734                 :             : void
    3735                 :       12472 : SetRelationHasSubclass(Oid relationId, bool relhassubclass)
    3736                 :             : {
    3737                 :             :     Relation    relationRelation;
    3738                 :             :     HeapTuple   tuple;
    3739                 :             :     Form_pg_class classtuple;
    3740                 :             : 
    3741                 :             :     Assert(CheckRelationOidLockedByMe(relationId,
    3742                 :             :                                       ShareUpdateExclusiveLock, false) ||
    3743                 :             :            CheckRelationOidLockedByMe(relationId,
    3744                 :             :                                       ShareRowExclusiveLock, true));
    3745                 :             : 
    3746                 :             :     /*
    3747                 :             :      * Fetch a modifiable copy of the tuple, modify it, update pg_class.
    3748                 :             :      */
    3749                 :       12472 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
    3750                 :       12472 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
    3751         [ -  + ]:       12472 :     if (!HeapTupleIsValid(tuple))
    3752         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", relationId);
    3753                 :       12472 :     classtuple = (Form_pg_class) GETSTRUCT(tuple);
    3754                 :             : 
    3755         [ +  + ]:       12472 :     if (classtuple->relhassubclass != relhassubclass)
    3756                 :             :     {
    3757                 :        5646 :         classtuple->relhassubclass = relhassubclass;
    3758                 :        5646 :         CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
    3759                 :             :     }
    3760                 :             :     else
    3761                 :             :     {
    3762                 :             :         /* no need to change tuple, but force relcache rebuild anyway */
    3763                 :        6826 :         CacheInvalidateRelcacheByTuple(tuple);
    3764                 :             :     }
    3765                 :             : 
    3766                 :       12472 :     heap_freetuple(tuple);
    3767                 :       12472 :     table_close(relationRelation, RowExclusiveLock);
    3768                 :       12472 : }
    3769                 :             : 
    3770                 :             : /*
    3771                 :             :  * CheckRelationTableSpaceMove
    3772                 :             :  *      Check if relation can be moved to new tablespace.
    3773                 :             :  *
    3774                 :             :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3775                 :             :  *
    3776                 :             :  * Returns true if the relation can be moved to the new tablespace; raises
    3777                 :             :  * an error if it is not possible to do the move; returns false if the move
    3778                 :             :  * would have no effect.
    3779                 :             :  */
    3780                 :             : bool
    3781                 :         157 : CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
    3782                 :             : {
    3783                 :             :     Oid         oldTableSpaceId;
    3784                 :             : 
    3785                 :             :     /*
    3786                 :             :      * No work if no change in tablespace.  Note that MyDatabaseTableSpace is
    3787                 :             :      * stored as 0.
    3788                 :             :      */
    3789                 :         157 :     oldTableSpaceId = rel->rd_rel->reltablespace;
    3790         [ +  + ]:         157 :     if (newTableSpaceId == oldTableSpaceId ||
    3791   [ +  +  +  + ]:         152 :         (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
    3792                 :          10 :         return false;
    3793                 :             : 
    3794                 :             :     /*
    3795                 :             :      * We cannot support moving mapped relations into different tablespaces.
    3796                 :             :      * (In particular this eliminates all shared catalogs.)
    3797                 :             :      */
    3798   [ +  +  +  +  :         147 :     if (RelationIsMapped(rel))
          +  -  +  +  +  
                +  -  + ]
    3799         [ #  # ]:           0 :         ereport(ERROR,
    3800                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3801                 :             :                  errmsg("cannot move system relation \"%s\"",
    3802                 :             :                         RelationGetRelationName(rel))));
    3803                 :             : 
    3804                 :             :     /* Cannot move a non-shared relation into pg_global */
    3805         [ +  + ]:         147 :     if (newTableSpaceId == GLOBALTABLESPACE_OID)
    3806         [ +  - ]:           8 :         ereport(ERROR,
    3807                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    3808                 :             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
    3809                 :             : 
    3810                 :             :     /*
    3811                 :             :      * Do not allow moving temp tables of other backends ... their local
    3812                 :             :      * buffer manager is not going to cope.
    3813                 :             :      */
    3814   [ +  +  -  + ]:         139 :     if (RELATION_IS_OTHER_TEMP(rel))
    3815         [ #  # ]:           0 :         ereport(ERROR,
    3816                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    3817                 :             :                  errmsg("cannot move temporary tables of other sessions")));
    3818                 :             : 
    3819                 :         139 :     return true;
    3820                 :             : }
    3821                 :             : 
    3822                 :             : /*
    3823                 :             :  * SetRelationTableSpace
    3824                 :             :  *      Set new reltablespace and relfilenumber in pg_class entry.
    3825                 :             :  *
    3826                 :             :  * newTableSpaceId is the new tablespace for the relation, and
    3827                 :             :  * newRelFilenumber its new filenumber.  If newRelFilenumber is
    3828                 :             :  * InvalidRelFileNumber, this field is not updated.
    3829                 :             :  *
    3830                 :             :  * NOTE: The caller must hold AccessExclusiveLock on the relation.
    3831                 :             :  *
    3832                 :             :  * The caller of this routine had better check if a relation can be
    3833                 :             :  * moved to this new tablespace by calling CheckRelationTableSpaceMove()
    3834                 :             :  * first, and is responsible for making the change visible with
    3835                 :             :  * CommandCounterIncrement().
    3836                 :             :  */
    3837                 :             : void
    3838                 :         139 : SetRelationTableSpace(Relation rel,
    3839                 :             :                       Oid newTableSpaceId,
    3840                 :             :                       RelFileNumber newRelFilenumber)
    3841                 :             : {
    3842                 :             :     Relation    pg_class;
    3843                 :             :     HeapTuple   tuple;
    3844                 :             :     ItemPointerData otid;
    3845                 :             :     Form_pg_class rd_rel;
    3846                 :         139 :     Oid         reloid = RelationGetRelid(rel);
    3847                 :             : 
    3848                 :             :     Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
    3849                 :             : 
    3850                 :             :     /* Get a modifiable copy of the relation's pg_class row. */
    3851                 :         139 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
    3852                 :             : 
    3853                 :         139 :     tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
    3854         [ -  + ]:         139 :     if (!HeapTupleIsValid(tuple))
    3855         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", reloid);
    3856                 :         139 :     otid = tuple->t_self;
    3857                 :         139 :     rd_rel = (Form_pg_class) GETSTRUCT(tuple);
    3858                 :             : 
    3859                 :             :     /* Update the pg_class row. */
    3860                 :         278 :     rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
    3861         [ +  + ]:         139 :         InvalidOid : newTableSpaceId;
    3862         [ +  + ]:         139 :     if (RelFileNumberIsValid(newRelFilenumber))
    3863                 :         110 :         rd_rel->relfilenode = newRelFilenumber;
    3864                 :         139 :     CatalogTupleUpdate(pg_class, &otid, tuple);
    3865                 :         139 :     UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
    3866                 :             : 
    3867                 :             :     /*
    3868                 :             :      * Record dependency on tablespace.  This is only required for relations
    3869                 :             :      * that have no physical storage.
    3870                 :             :      */
    3871   [ +  +  +  +  :         139 :     if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
          +  -  +  +  +  
                      + ]
    3872                 :          20 :         changeDependencyOnTablespace(RelationRelationId, reloid,
    3873                 :             :                                      rd_rel->reltablespace);
    3874                 :             : 
    3875                 :         139 :     heap_freetuple(tuple);
    3876                 :         139 :     table_close(pg_class, RowExclusiveLock);
    3877                 :         139 : }
    3878                 :             : 
    3879                 :             : /*
    3880                 :             :  *      renameatt_check         - basic sanity checks before attribute rename
    3881                 :             :  */
    3882                 :             : static void
    3883                 :         692 : renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
    3884                 :             : {
    3885                 :         692 :     char        relkind = classform->relkind;
    3886                 :             : 
    3887   [ +  +  +  + ]:         692 :     if (classform->reloftype && !recursing)
    3888         [ +  - ]:           4 :         ereport(ERROR,
    3889                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3890                 :             :                  errmsg("cannot rename column of typed table")));
    3891                 :             : 
    3892                 :             :     /*
    3893                 :             :      * Renaming the columns of sequences or toast tables doesn't actually
    3894                 :             :      * break anything from the system's point of view, since internal
    3895                 :             :      * references are by attnum.  But it doesn't seem right to allow users to
    3896                 :             :      * change names that are hardcoded into the system, hence the following
    3897                 :             :      * restriction.
    3898                 :             :      */
    3899   [ +  +  +  + ]:         688 :     if (relkind != RELKIND_RELATION &&
    3900         [ +  - ]:          56 :         relkind != RELKIND_VIEW &&
    3901         [ +  + ]:          56 :         relkind != RELKIND_MATVIEW &&
    3902         [ +  - ]:          24 :         relkind != RELKIND_COMPOSITE_TYPE &&
    3903         [ +  - ]:          24 :         relkind != RELKIND_INDEX &&
    3904         [ -  + ]:          24 :         relkind != RELKIND_PARTITIONED_INDEX &&
    3905         [ #  # ]:           0 :         relkind != RELKIND_FOREIGN_TABLE &&
    3906                 :             :         relkind != RELKIND_PARTITIONED_TABLE)
    3907         [ #  # ]:           0 :         ereport(ERROR,
    3908                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3909                 :             :                  errmsg("cannot rename columns of relation \"%s\"",
    3910                 :             :                         NameStr(classform->relname)),
    3911                 :             :                  errdetail_relkind_not_supported(relkind)));
    3912                 :             : 
    3913                 :             :     /*
    3914                 :             :      * permissions checking.  only the owner of a class can change its schema.
    3915                 :             :      */
    3916         [ -  + ]:         688 :     if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
    3917                 :           0 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
    3918                 :           0 :                        NameStr(classform->relname));
    3919                 :             : 
    3920                 :             :     /*
    3921                 :             :      * Conflict log tables are used internally for logical replication
    3922                 :             :      * conflict logging and should not be modified directly, as it could
    3923                 :             :      * disrupt conflict logging.
    3924                 :             :      */
    3925         [ +  + ]:         688 :     if (IsConflictLogTableClass(classform))
    3926         [ +  - ]:           4 :         ereport(ERROR,
    3927                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    3928                 :             :                  errmsg("cannot rename columns of conflict log table \"%s\"",
    3929                 :             :                         NameStr(classform->relname)),
    3930                 :             :                  errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
    3931                 :             : 
    3932   [ +  +  +  + ]:         684 :     if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
    3933         [ +  - ]:           1 :         ereport(ERROR,
    3934                 :             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    3935                 :             :                  errmsg("permission denied: \"%s\" is a system catalog",
    3936                 :             :                         NameStr(classform->relname))));
    3937                 :         683 : }
    3938                 :             : 
    3939                 :             : /*
    3940                 :             :  *      renameatt_internal      - workhorse for renameatt
    3941                 :             :  *
    3942                 :             :  * Return value is the attribute number in the 'myrelid' relation.
    3943                 :             :  */
    3944                 :             : static AttrNumber
    3945                 :         375 : renameatt_internal(Oid myrelid,
    3946                 :             :                    const char *oldattname,
    3947                 :             :                    const char *newattname,
    3948                 :             :                    bool recurse,
    3949                 :             :                    bool recursing,
    3950                 :             :                    int expected_parents,
    3951                 :             :                    DropBehavior behavior)
    3952                 :             : {
    3953                 :             :     Relation    targetrelation;
    3954                 :             :     Relation    attrelation;
    3955                 :             :     HeapTuple   atttup;
    3956                 :             :     Form_pg_attribute attform;
    3957                 :             :     AttrNumber  attnum;
    3958                 :             : 
    3959                 :             :     /*
    3960                 :             :      * Grab an exclusive lock on the target table, which we will NOT release
    3961                 :             :      * until end of transaction.
    3962                 :             :      */
    3963                 :         375 :     targetrelation = relation_open(myrelid, AccessExclusiveLock);
    3964                 :         375 :     renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
    3965                 :             : 
    3966                 :             :     /*
    3967                 :             :      * if the 'recurse' flag is set then we are supposed to rename this
    3968                 :             :      * attribute in all classes that inherit from 'relname' (as well as in
    3969                 :             :      * 'relname').
    3970                 :             :      *
    3971                 :             :      * any permissions or problems with duplicate attributes will cause the
    3972                 :             :      * whole transaction to abort, which is what we want -- all or nothing.
    3973                 :             :      */
    3974         [ +  + ]:         375 :     if (recurse)
    3975                 :             :     {
    3976                 :             :         List       *child_oids,
    3977                 :             :                    *child_numparents;
    3978                 :             :         ListCell   *lo,
    3979                 :             :                    *li;
    3980                 :             : 
    3981                 :             :         /*
    3982                 :             :          * we need the number of parents for each child so that the recursive
    3983                 :             :          * calls to renameatt() can determine whether there are any parents
    3984                 :             :          * outside the inheritance hierarchy being processed.
    3985                 :             :          */
    3986                 :         173 :         child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    3987                 :             :                                          &child_numparents);
    3988                 :             : 
    3989                 :             :         /*
    3990                 :             :          * find_all_inheritors does the recursive search of the inheritance
    3991                 :             :          * hierarchy, so all we have to do is process all of the relids in the
    3992                 :             :          * list that it returns.
    3993                 :             :          */
    3994   [ +  -  +  +  :         504 :         forboth(lo, child_oids, li, child_numparents)
          +  -  +  +  +  
             +  +  -  +  
                      + ]
    3995                 :             :         {
    3996                 :         351 :             Oid         childrelid = lfirst_oid(lo);
    3997                 :         351 :             int         numparents = lfirst_int(li);
    3998                 :             : 
    3999         [ +  + ]:         351 :             if (childrelid == myrelid)
    4000                 :         173 :                 continue;
    4001                 :             :             /* note we need not recurse again */
    4002                 :         178 :             renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
    4003                 :             :         }
    4004                 :             :     }
    4005                 :             :     else
    4006                 :             :     {
    4007                 :             :         /*
    4008                 :             :          * If we are told not to recurse, there had better not be any child
    4009                 :             :          * tables; else the rename would put them out of step.
    4010                 :             :          *
    4011                 :             :          * expected_parents will only be 0 if we are not already recursing.
    4012                 :             :          */
    4013   [ +  +  +  + ]:         226 :         if (expected_parents == 0 &&
    4014                 :          24 :             find_inheritance_children(myrelid, NoLock) != NIL)
    4015         [ +  - ]:           8 :             ereport(ERROR,
    4016                 :             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4017                 :             :                      errmsg("inherited column \"%s\" must be renamed in child tables too",
    4018                 :             :                             oldattname)));
    4019                 :             :     }
    4020                 :             : 
    4021                 :             :     /* rename attributes in typed tables of composite type */
    4022         [ +  + ]:         347 :     if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    4023                 :             :     {
    4024                 :             :         List       *child_oids;
    4025                 :             :         ListCell   *lo;
    4026                 :             : 
    4027                 :          16 :         child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
    4028                 :          16 :                                                    RelationGetRelationName(targetrelation),
    4029                 :             :                                                    behavior);
    4030                 :             : 
    4031   [ +  +  +  +  :          16 :         foreach(lo, child_oids)
                   +  + ]
    4032                 :           4 :             renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
    4033                 :             :     }
    4034                 :             : 
    4035                 :         343 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    4036                 :             : 
    4037                 :         343 :     atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
    4038         [ +  + ]:         343 :     if (!HeapTupleIsValid(atttup))
    4039         [ +  - ]:          16 :         ereport(ERROR,
    4040                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    4041                 :             :                  errmsg("column \"%s\" does not exist",
    4042                 :             :                         oldattname)));
    4043                 :         327 :     attform = (Form_pg_attribute) GETSTRUCT(atttup);
    4044                 :             : 
    4045                 :         327 :     attnum = attform->attnum;
    4046         [ -  + ]:         327 :     if (attnum <= 0)
    4047         [ #  # ]:           0 :         ereport(ERROR,
    4048                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4049                 :             :                  errmsg("cannot rename system column \"%s\"",
    4050                 :             :                         oldattname)));
    4051                 :             : 
    4052                 :             :     /*
    4053                 :             :      * if the attribute is inherited, forbid the renaming.  if this is a
    4054                 :             :      * top-level call to renameatt(), then expected_parents will be 0, so the
    4055                 :             :      * effect of this code will be to prohibit the renaming if the attribute
    4056                 :             :      * is inherited at all.  if this is a recursive call to renameatt(),
    4057                 :             :      * expected_parents will be the number of parents the current relation has
    4058                 :             :      * within the inheritance hierarchy being processed, so we'll prohibit the
    4059                 :             :      * renaming only if there are additional parents from elsewhere.
    4060                 :             :      */
    4061         [ +  + ]:         327 :     if (attform->attinhcount > expected_parents)
    4062         [ +  - ]:          20 :         ereport(ERROR,
    4063                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4064                 :             :                  errmsg("cannot rename inherited column \"%s\"",
    4065                 :             :                         oldattname)));
    4066                 :             : 
    4067                 :             :     /* new name should not already exist */
    4068                 :         307 :     (void) check_for_column_name_collision(targetrelation, newattname, false);
    4069                 :             : 
    4070                 :             :     /* apply the update */
    4071                 :         299 :     namestrcpy(&(attform->attname), newattname);
    4072                 :             : 
    4073                 :         299 :     CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
    4074                 :             : 
    4075         [ -  + ]:         299 :     InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
    4076                 :             : 
    4077                 :         299 :     heap_freetuple(atttup);
    4078                 :             : 
    4079                 :         299 :     table_close(attrelation, RowExclusiveLock);
    4080                 :             : 
    4081                 :         299 :     relation_close(targetrelation, NoLock); /* close rel but keep lock */
    4082                 :             : 
    4083                 :         299 :     return attnum;
    4084                 :             : }
    4085                 :             : 
    4086                 :             : /*
    4087                 :             :  * Perform permissions and integrity checks before acquiring a relation lock.
    4088                 :             :  */
    4089                 :             : static void
    4090                 :         286 : RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
    4091                 :             :                                    void *arg)
    4092                 :             : {
    4093                 :             :     HeapTuple   tuple;
    4094                 :             :     Form_pg_class form;
    4095                 :             : 
    4096                 :         286 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
    4097         [ +  + ]:         286 :     if (!HeapTupleIsValid(tuple))
    4098                 :          25 :         return;                 /* concurrently dropped */
    4099                 :         261 :     form = (Form_pg_class) GETSTRUCT(tuple);
    4100                 :         261 :     renameatt_check(relid, form, false);
    4101                 :         252 :     ReleaseSysCache(tuple);
    4102                 :             : }
    4103                 :             : 
    4104                 :             : /*
    4105                 :             :  *      renameatt       - changes the name of an attribute in a relation
    4106                 :             :  *
    4107                 :             :  * The returned ObjectAddress is that of the renamed column.
    4108                 :             :  */
    4109                 :             : ObjectAddress
    4110                 :         222 : renameatt(RenameStmt *stmt)
    4111                 :             : {
    4112                 :             :     Oid         relid;
    4113                 :             :     AttrNumber  attnum;
    4114                 :             :     ObjectAddress address;
    4115                 :             : 
    4116                 :             :     /* lock level taken here should match renameatt_internal */
    4117                 :         222 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    4118                 :         222 :                                      stmt->missing_ok ? RVR_MISSING_OK : 0,
    4119                 :             :                                      RangeVarCallbackForRenameAttribute,
    4120                 :             :                                      NULL);
    4121                 :             : 
    4122         [ +  + ]:         209 :     if (!OidIsValid(relid))
    4123                 :             :     {
    4124         [ +  - ]:          16 :         ereport(NOTICE,
    4125                 :             :                 (errmsg("relation \"%s\" does not exist, skipping",
    4126                 :             :                         stmt->relation->relname)));
    4127                 :          16 :         return InvalidObjectAddress;
    4128                 :             :     }
    4129                 :             : 
    4130                 :             :     attnum =
    4131                 :         193 :         renameatt_internal(relid,
    4132                 :         193 :                            stmt->subname,    /* old att name */
    4133                 :         193 :                            stmt->newname,    /* new att name */
    4134                 :         193 :                            stmt->relation->inh, /* recursive? */
    4135                 :             :                            false,   /* recursing? */
    4136                 :             :                            0,   /* expected inhcount */
    4137                 :             :                            stmt->behavior);
    4138                 :             : 
    4139                 :         137 :     ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
    4140                 :             : 
    4141                 :         137 :     return address;
    4142                 :             : }
    4143                 :             : 
    4144                 :             : /*
    4145                 :             :  * same logic as renameatt_internal
    4146                 :             :  */
    4147                 :             : static ObjectAddress
    4148                 :          60 : rename_constraint_internal(Oid myrelid,
    4149                 :             :                            Oid mytypid,
    4150                 :             :                            const char *oldconname,
    4151                 :             :                            const char *newconname,
    4152                 :             :                            bool recurse,
    4153                 :             :                            bool recursing,
    4154                 :             :                            int expected_parents)
    4155                 :             : {
    4156                 :          60 :     Relation    targetrelation = NULL;
    4157                 :             :     Oid         constraintOid;
    4158                 :             :     HeapTuple   tuple;
    4159                 :             :     Form_pg_constraint con;
    4160                 :             :     ObjectAddress address;
    4161                 :             : 
    4162                 :             :     Assert(!myrelid || !mytypid);
    4163                 :             : 
    4164         [ +  + ]:          60 :     if (mytypid)
    4165                 :             :     {
    4166                 :           4 :         constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
    4167                 :             :     }
    4168                 :             :     else
    4169                 :             :     {
    4170                 :          56 :         targetrelation = relation_open(myrelid, AccessExclusiveLock);
    4171                 :             : 
    4172                 :             :         /*
    4173                 :             :          * don't tell it whether we're recursing; we allow changing typed
    4174                 :             :          * tables here
    4175                 :             :          */
    4176                 :          56 :         renameatt_check(myrelid, RelationGetForm(targetrelation), false);
    4177                 :             : 
    4178                 :          56 :         constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
    4179                 :             :     }
    4180                 :             : 
    4181                 :          60 :     tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
    4182         [ -  + ]:          60 :     if (!HeapTupleIsValid(tuple))
    4183         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for constraint %u",
    4184                 :             :              constraintOid);
    4185                 :          60 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
    4186                 :             : 
    4187         [ +  + ]:          60 :     if (myrelid &&
    4188         [ +  + ]:          56 :         (con->contype == CONSTRAINT_CHECK ||
    4189         [ +  + ]:          16 :          con->contype == CONSTRAINT_NOTNULL) &&
    4190         [ +  + ]:          44 :         !con->connoinherit)
    4191                 :             :     {
    4192         [ +  + ]:          36 :         if (recurse)
    4193                 :             :         {
    4194                 :             :             List       *child_oids,
    4195                 :             :                        *child_numparents;
    4196                 :             :             ListCell   *lo,
    4197                 :             :                        *li;
    4198                 :             : 
    4199                 :          24 :             child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
    4200                 :             :                                              &child_numparents);
    4201                 :             : 
    4202   [ +  -  +  +  :          56 :             forboth(lo, child_oids, li, child_numparents)
          +  -  +  +  +  
             +  +  -  +  
                      + ]
    4203                 :             :             {
    4204                 :          32 :                 Oid         childrelid = lfirst_oid(lo);
    4205                 :          32 :                 int         numparents = lfirst_int(li);
    4206                 :             : 
    4207         [ +  + ]:          32 :                 if (childrelid == myrelid)
    4208                 :          24 :                     continue;
    4209                 :             : 
    4210                 :           8 :                 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
    4211                 :             :             }
    4212                 :             :         }
    4213                 :             :         else
    4214                 :             :         {
    4215   [ +  +  +  - ]:          16 :             if (expected_parents == 0 &&
    4216                 :           4 :                 find_inheritance_children(myrelid, NoLock) != NIL)
    4217         [ +  - ]:           4 :                 ereport(ERROR,
    4218                 :             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4219                 :             :                          errmsg("inherited constraint \"%s\" must be renamed in child tables too",
    4220                 :             :                                 oldconname)));
    4221                 :             :         }
    4222                 :             : 
    4223         [ +  + ]:          32 :         if (con->coninhcount > expected_parents)
    4224         [ +  - ]:           4 :             ereport(ERROR,
    4225                 :             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    4226                 :             :                      errmsg("cannot rename inherited constraint \"%s\"",
    4227                 :             :                             oldconname)));
    4228                 :             :     }
    4229                 :             : 
    4230         [ +  + ]:          52 :     if (con->conindid
    4231         [ +  + ]:          12 :         && (con->contype == CONSTRAINT_PRIMARY
    4232         [ -  + ]:           4 :             || con->contype == CONSTRAINT_UNIQUE
    4233         [ #  # ]:           0 :             || con->contype == CONSTRAINT_EXCLUSION))
    4234                 :             :         /* rename the index; this renames the constraint as well */
    4235                 :          12 :         RenameRelationInternal(con->conindid, newconname, false, true);
    4236                 :             :     else
    4237                 :          40 :         RenameConstraintById(constraintOid, newconname);
    4238                 :             : 
    4239                 :          52 :     ObjectAddressSet(address, ConstraintRelationId, constraintOid);
    4240                 :             : 
    4241                 :          52 :     ReleaseSysCache(tuple);
    4242                 :             : 
    4243         [ +  + ]:          52 :     if (targetrelation)
    4244                 :             :     {
    4245                 :             :         /*
    4246                 :             :          * Invalidate relcache so as others can see the new constraint name.
    4247                 :             :          */
    4248                 :          48 :         CacheInvalidateRelcache(targetrelation);
    4249                 :             : 
    4250                 :          48 :         relation_close(targetrelation, NoLock); /* close rel but keep lock */
    4251                 :             :     }
    4252                 :             : 
    4253                 :          52 :     return address;
    4254                 :             : }
    4255                 :             : 
    4256                 :             : ObjectAddress
    4257                 :          56 : RenameConstraint(RenameStmt *stmt)
    4258                 :             : {
    4259                 :          56 :     Oid         relid = InvalidOid;
    4260                 :          56 :     Oid         typid = InvalidOid;
    4261                 :             : 
    4262         [ +  + ]:          56 :     if (stmt->renameType == OBJECT_DOMCONSTRAINT)
    4263                 :             :     {
    4264                 :             :         Relation    rel;
    4265                 :             :         HeapTuple   tup;
    4266                 :             : 
    4267                 :           4 :         typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
    4268                 :           4 :         rel = table_open(TypeRelationId, RowExclusiveLock);
    4269                 :           4 :         tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
    4270         [ -  + ]:           4 :         if (!HeapTupleIsValid(tup))
    4271         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for type %u", typid);
    4272                 :           4 :         checkDomainOwner(tup);
    4273                 :           4 :         ReleaseSysCache(tup);
    4274                 :           4 :         table_close(rel, NoLock);
    4275                 :             :     }
    4276                 :             :     else
    4277                 :             :     {
    4278                 :             :         /* lock level taken here should match rename_constraint_internal */
    4279                 :          52 :         relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
    4280                 :          52 :                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4281                 :             :                                          RangeVarCallbackForRenameAttribute,
    4282                 :             :                                          NULL);
    4283         [ +  + ]:          52 :         if (!OidIsValid(relid))
    4284                 :             :         {
    4285         [ +  - ]:           4 :             ereport(NOTICE,
    4286                 :             :                     (errmsg("relation \"%s\" does not exist, skipping",
    4287                 :             :                             stmt->relation->relname)));
    4288                 :           4 :             return InvalidObjectAddress;
    4289                 :             :         }
    4290                 :             :     }
    4291                 :             : 
    4292                 :             :     return
    4293                 :          52 :         rename_constraint_internal(relid, typid,
    4294                 :          52 :                                    stmt->subname,
    4295                 :          52 :                                    stmt->newname,
    4296         [ +  + ]:         100 :                                    (stmt->relation &&
    4297         [ +  + ]:          48 :                                     stmt->relation->inh), /* recursive? */
    4298                 :             :                                    false,   /* recursing? */
    4299                 :          52 :                                    0 /* expected inhcount */ );
    4300                 :             : }
    4301                 :             : 
    4302                 :             : /*
    4303                 :             :  * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE/PROPERTY GRAPH
    4304                 :             :  * RENAME
    4305                 :             :  */
    4306                 :             : ObjectAddress
    4307                 :         327 : RenameRelation(RenameStmt *stmt)
    4308                 :             : {
    4309                 :         327 :     bool        is_index_stmt = stmt->renameType == OBJECT_INDEX;
    4310                 :             :     Oid         relid;
    4311                 :             :     ObjectAddress address;
    4312                 :             : 
    4313                 :             :     /*
    4314                 :             :      * Grab an exclusive lock on the target table, index, sequence, view,
    4315                 :             :      * materialized view, or foreign table, which we will NOT release until
    4316                 :             :      * end of transaction.
    4317                 :             :      *
    4318                 :             :      * Lock level used here should match RenameRelationInternal, to avoid lock
    4319                 :             :      * escalation.  However, because ALTER INDEX can be used with any relation
    4320                 :             :      * type, we mustn't believe without verification.
    4321                 :             :      */
    4322                 :             :     for (;;)
    4323                 :           8 :     {
    4324                 :             :         LOCKMODE    lockmode;
    4325                 :             :         char        relkind;
    4326                 :             :         bool        obj_is_index;
    4327                 :             : 
    4328         [ +  + ]:         335 :         lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
    4329                 :             : 
    4330                 :         335 :         relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
    4331                 :         335 :                                          stmt->missing_ok ? RVR_MISSING_OK : 0,
    4332                 :             :                                          RangeVarCallbackForAlterRelation,
    4333                 :             :                                          stmt);
    4334                 :             : 
    4335         [ +  + ]:         298 :         if (!OidIsValid(relid))
    4336                 :             :         {
    4337         [ +  - ]:          12 :             ereport(NOTICE,
    4338                 :             :                     (errmsg("relation \"%s\" does not exist, skipping",
    4339                 :             :                             stmt->relation->relname)));
    4340                 :          12 :             return InvalidObjectAddress;
    4341                 :             :         }
    4342                 :             : 
    4343                 :             :         /*
    4344                 :             :          * We allow mismatched statement and object types (e.g., ALTER INDEX
    4345                 :             :          * to rename a table), but we might've used the wrong lock level.  If
    4346                 :             :          * that happens, retry with the correct lock level.  We don't bother
    4347                 :             :          * if we already acquired AccessExclusiveLock with an index, however.
    4348                 :             :          */
    4349                 :         286 :         relkind = get_rel_relkind(relid);
    4350   [ +  +  +  + ]:         286 :         obj_is_index = (relkind == RELKIND_INDEX ||
    4351                 :             :                         relkind == RELKIND_PARTITIONED_INDEX);
    4352   [ +  +  +  + ]:         286 :         if (obj_is_index || is_index_stmt == obj_is_index)
    4353                 :             :             break;
    4354                 :             : 
    4355                 :           8 :         UnlockRelationOid(relid, lockmode);
    4356                 :           8 :         is_index_stmt = obj_is_index;
    4357                 :             :     }
    4358                 :             : 
    4359                 :             :     /* Do the work */
    4360                 :         278 :     RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
    4361                 :             : 
    4362                 :         266 :     ObjectAddressSet(address, RelationRelationId, relid);
    4363                 :             : 
    4364                 :         266 :     return address;
    4365                 :             : }
    4366                 :             : 
    4367                 :             : /*
    4368                 :             :  *      RenameRelationInternal - change the name of a relation
    4369                 :             :  */
    4370                 :             : void
    4371                 :        1124 : RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
    4372                 :             : {
    4373                 :             :     Relation    targetrelation;
    4374                 :             :     Relation    relrelation;    /* for RELATION relation */
    4375                 :             :     ItemPointerData otid;
    4376                 :             :     HeapTuple   reltup;
    4377                 :             :     Form_pg_class relform;
    4378                 :             :     Oid         namespaceId;
    4379                 :             : 
    4380                 :             :     /*
    4381                 :             :      * Grab a lock on the target relation, which we will NOT release until end
    4382                 :             :      * of transaction.  We need at least a self-exclusive lock so that
    4383                 :             :      * concurrent DDL doesn't overwrite the rename if they start updating
    4384                 :             :      * while still seeing the old version.  The lock also guards against
    4385                 :             :      * triggering relcache reloads in concurrent sessions, which might not
    4386                 :             :      * handle this information changing under them.  For indexes, we can use a
    4387                 :             :      * reduced lock level because RelationReloadIndexInfo() handles indexes
    4388                 :             :      * specially.
    4389                 :             :      */
    4390         [ +  + ]:        1124 :     targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
    4391                 :        1124 :     namespaceId = RelationGetNamespace(targetrelation);
    4392                 :             : 
    4393                 :             :     /*
    4394                 :             :      * Find relation's pg_class tuple, and make sure newrelname isn't in use.
    4395                 :             :      */
    4396                 :        1124 :     relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4397                 :             : 
    4398                 :        1124 :     reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4399         [ -  + ]:        1124 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4400         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4401                 :        1124 :     otid = reltup->t_self;
    4402                 :        1124 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    4403                 :             : 
    4404         [ +  + ]:        1124 :     if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
    4405         [ +  - ]:          12 :         ereport(ERROR,
    4406                 :             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
    4407                 :             :                  errmsg("relation \"%s\" already exists",
    4408                 :             :                         newrelname)));
    4409                 :             : 
    4410                 :             :     /*
    4411                 :             :      * RenameRelation is careful not to believe the caller's idea of the
    4412                 :             :      * relation kind being handled.  We don't have to worry about this, but
    4413                 :             :      * let's not be totally oblivious to it.  We can process an index as
    4414                 :             :      * not-an-index, but not the other way around.
    4415                 :             :      */
    4416                 :             :     Assert(!is_index ||
    4417                 :             :            is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4418                 :             :                         targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
    4419                 :             : 
    4420                 :             :     /*
    4421                 :             :      * Update pg_class tuple with new relname.  (Scribbling on reltup is OK
    4422                 :             :      * because it's a copy...)
    4423                 :             :      */
    4424                 :        1112 :     namestrcpy(&(relform->relname), newrelname);
    4425                 :             : 
    4426                 :        1112 :     CatalogTupleUpdate(relrelation, &otid, reltup);
    4427                 :        1112 :     UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
    4428                 :             : 
    4429         [ -  + ]:        1112 :     InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
    4430                 :             :                                  InvalidOid, is_internal);
    4431                 :             : 
    4432                 :        1112 :     heap_freetuple(reltup);
    4433                 :        1112 :     table_close(relrelation, RowExclusiveLock);
    4434                 :             : 
    4435                 :             :     /*
    4436                 :             :      * Also rename the associated type, if any.
    4437                 :             :      */
    4438         [ +  + ]:        1112 :     if (OidIsValid(targetrelation->rd_rel->reltype))
    4439                 :         128 :         RenameTypeInternal(targetrelation->rd_rel->reltype,
    4440                 :             :                            newrelname, namespaceId);
    4441                 :             : 
    4442                 :             :     /*
    4443                 :             :      * Also rename the associated constraint, if any.
    4444                 :             :      */
    4445         [ +  + ]:        1112 :     if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
    4446         [ +  + ]:         614 :         targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    4447                 :             :     {
    4448                 :         510 :         Oid         constraintId = get_index_constraint(myrelid);
    4449                 :             : 
    4450         [ +  + ]:         510 :         if (OidIsValid(constraintId))
    4451                 :          24 :             RenameConstraintById(constraintId, newrelname);
    4452                 :             :     }
    4453                 :             : 
    4454                 :             :     /*
    4455                 :             :      * Close rel, but keep lock!
    4456                 :             :      */
    4457                 :        1112 :     relation_close(targetrelation, NoLock);
    4458                 :        1112 : }
    4459                 :             : 
    4460                 :             : /*
    4461                 :             :  *      ResetRelRewrite - reset relrewrite
    4462                 :             :  */
    4463                 :             : void
    4464                 :         389 : ResetRelRewrite(Oid myrelid)
    4465                 :             : {
    4466                 :             :     Relation    relrelation;    /* for RELATION relation */
    4467                 :             :     HeapTuple   reltup;
    4468                 :             :     Form_pg_class relform;
    4469                 :             : 
    4470                 :             :     /*
    4471                 :             :      * Find relation's pg_class tuple.
    4472                 :             :      */
    4473                 :         389 :     relrelation = table_open(RelationRelationId, RowExclusiveLock);
    4474                 :             : 
    4475                 :         389 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    4476         [ -  + ]:         389 :     if (!HeapTupleIsValid(reltup))  /* shouldn't happen */
    4477         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    4478                 :         389 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    4479                 :             : 
    4480                 :             :     /*
    4481                 :             :      * Update pg_class tuple.
    4482                 :             :      */
    4483                 :         389 :     relform->relrewrite = InvalidOid;
    4484                 :             : 
    4485                 :         389 :     CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
    4486                 :             : 
    4487                 :         389 :     heap_freetuple(reltup);
    4488                 :         389 :     table_close(relrelation, RowExclusiveLock);
    4489                 :         389 : }
    4490                 :             : 
    4491                 :             : /*
    4492                 :             :  * Disallow ALTER TABLE (and similar commands) when the current backend has
    4493                 :             :  * any open reference to the target table besides the one just acquired by
    4494                 :             :  * the calling command; this implies there's an open cursor or active plan.
    4495                 :             :  * We need this check because our lock doesn't protect us against stomping
    4496                 :             :  * on our own foot, only other people's feet!
    4497                 :             :  *
    4498                 :             :  * For ALTER TABLE, the only case known to cause serious trouble is ALTER
    4499                 :             :  * COLUMN TYPE, and some changes are obviously pretty benign, so this could
    4500                 :             :  * possibly be relaxed to only error out for certain types of alterations.
    4501                 :             :  * But the use-case for allowing any of these things is not obvious, so we
    4502                 :             :  * won't work hard at it for now.
    4503                 :             :  *
    4504                 :             :  * We also reject these commands if there are any pending AFTER trigger events
    4505                 :             :  * for the rel.  This is certainly necessary for the rewriting variants of
    4506                 :             :  * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
    4507                 :             :  * events would try to fetch the wrong tuples.  It might be overly cautious
    4508                 :             :  * in other cases, but again it seems better to err on the side of paranoia.
    4509                 :             :  *
    4510                 :             :  * REINDEX calls this with "rel" referencing the index to be rebuilt; here
    4511                 :             :  * we are worried about active indexscans on the index.  The trigger-event
    4512                 :             :  * check can be skipped, since we are doing no damage to the parent table.
    4513                 :             :  *
    4514                 :             :  * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
    4515                 :             :  */
    4516                 :             : void
    4517                 :      117241 : CheckTableNotInUse(Relation rel, const char *stmt)
    4518                 :             : {
    4519                 :             :     int         expected_refcnt;
    4520                 :             : 
    4521         [ +  + ]:      117241 :     expected_refcnt = rel->rd_isnailed ? 2 : 1;
    4522         [ +  + ]:      117241 :     if (rel->rd_refcnt != expected_refcnt)
    4523         [ +  - ]:          28 :         ereport(ERROR,
    4524                 :             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    4525                 :             :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    4526                 :             :                  errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
    4527                 :             :                         stmt, RelationGetRelationName(rel))));
    4528                 :             : 
    4529         [ +  + ]:      117213 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    4530   [ +  +  +  + ]:      191846 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    4531                 :       95168 :         AfterTriggerPendingOnRel(RelationGetRelid(rel)))
    4532         [ +  - ]:          12 :         ereport(ERROR,
    4533                 :             :                 (errcode(ERRCODE_OBJECT_IN_USE),
    4534                 :             :         /* translator: first %s is a SQL command, eg ALTER TABLE */
    4535                 :             :                  errmsg("cannot %s \"%s\" because it has pending trigger events",
    4536                 :             :                         stmt, RelationGetRelationName(rel))));
    4537                 :      117201 : }
    4538                 :             : 
    4539                 :             : /*
    4540                 :             :  * CheckAlterTableIsSafe
    4541                 :             :  *      Verify that it's safe to allow ALTER TABLE on this relation.
    4542                 :             :  *
    4543                 :             :  * This consists of CheckTableNotInUse() plus a check that the relation
    4544                 :             :  * isn't another session's temp table.  We must split out the temp-table
    4545                 :             :  * check because there are callers of CheckTableNotInUse() that don't want
    4546                 :             :  * that, notably DROP TABLE.  (We must allow DROP or we couldn't clean out
    4547                 :             :  * an orphaned temp schema.)  Compare truncate_check_activity().
    4548                 :             :  */
    4549                 :             : static void
    4550                 :       41319 : CheckAlterTableIsSafe(Relation rel)
    4551                 :             : {
    4552                 :             :     /*
    4553                 :             :      * Don't allow ALTER on temp tables of other backends.  Their local buffer
    4554                 :             :      * manager is not going to cope if we need to change the table's contents.
    4555                 :             :      * Even if we don't, there may be optimizations that assume temp tables
    4556                 :             :      * aren't subject to such interference.
    4557                 :             :      */
    4558   [ +  +  +  + ]:       41319 :     if (RELATION_IS_OTHER_TEMP(rel))
    4559         [ +  - ]:           2 :         ereport(ERROR,
    4560                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    4561                 :             :                  errmsg("cannot alter temporary tables of other sessions")));
    4562                 :             : 
    4563                 :             :     /*
    4564                 :             :      * Also check for active uses of the relation in the current transaction,
    4565                 :             :      * including open scans and pending AFTER trigger events.
    4566                 :             :      */
    4567                 :       41317 :     CheckTableNotInUse(rel, "ALTER TABLE");
    4568                 :       41293 : }
    4569                 :             : 
    4570                 :             : /*
    4571                 :             :  * AlterTableLookupRelation
    4572                 :             :  *      Look up, and lock, the OID for the relation named by an alter table
    4573                 :             :  *      statement.
    4574                 :             :  */
    4575                 :             : Oid
    4576                 :       21989 : AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
    4577                 :             : {
    4578                 :       43904 :     return RangeVarGetRelidExtended(stmt->relation, lockmode,
    4579                 :       21989 :                                     stmt->missing_ok ? RVR_MISSING_OK : 0,
    4580                 :             :                                     RangeVarCallbackForAlterRelation,
    4581                 :             :                                     stmt);
    4582                 :             : }
    4583                 :             : 
    4584                 :             : /*
    4585                 :             :  * AlterTable
    4586                 :             :  *      Execute ALTER TABLE, which can be a list of subcommands
    4587                 :             :  *
    4588                 :             :  * ALTER TABLE is performed in three phases:
    4589                 :             :  *      1. Examine subcommands and perform pre-transformation checking.
    4590                 :             :  *      2. Validate and transform subcommands, and update system catalogs.
    4591                 :             :  *      3. Scan table(s) to check new constraints, and optionally recopy
    4592                 :             :  *         the data into new table(s).
    4593                 :             :  * Phase 3 is not performed unless one or more of the subcommands requires
    4594                 :             :  * it.  The intention of this design is to allow multiple independent
    4595                 :             :  * updates of the table schema to be performed with only one pass over the
    4596                 :             :  * data.
    4597                 :             :  *
    4598                 :             :  * ATPrepCmd performs phase 1.  A "work queue" entry is created for
    4599                 :             :  * each table to be affected (there may be multiple affected tables if the
    4600                 :             :  * commands traverse a table inheritance hierarchy).  Also we do preliminary
    4601                 :             :  * validation of the subcommands.  Because earlier subcommands may change
    4602                 :             :  * the catalog state seen by later commands, there are limits to what can
    4603                 :             :  * be done in this phase.  Generally, this phase acquires table locks,
    4604                 :             :  * checks permissions and relkind, and recurses to find child tables.
    4605                 :             :  *
    4606                 :             :  * ATRewriteCatalogs performs phase 2 for each affected table.
    4607                 :             :  * Certain subcommands need to be performed before others to avoid
    4608                 :             :  * unnecessary conflicts; for example, DROP COLUMN should come before
    4609                 :             :  * ADD COLUMN.  Therefore phase 1 divides the subcommands into multiple
    4610                 :             :  * lists, one for each logical "pass" of phase 2.
    4611                 :             :  *
    4612                 :             :  * ATRewriteTables performs phase 3 for those tables that need it.
    4613                 :             :  *
    4614                 :             :  * For most subcommand types, phases 2 and 3 do no explicit recursion,
    4615                 :             :  * since phase 1 already does it.  However, for certain subcommand types
    4616                 :             :  * it is only possible to determine how to recurse at phase 2 time; for
    4617                 :             :  * those cases, phase 1 sets the cmd->recurse flag.
    4618                 :             :  *
    4619                 :             :  * Thanks to the magic of MVCC, an error anywhere along the way rolls back
    4620                 :             :  * the whole operation; we don't have to do anything special to clean up.
    4621                 :             :  *
    4622                 :             :  * The caller must lock the relation, with an appropriate lock level
    4623                 :             :  * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
    4624                 :             :  * or higher. We pass the lock level down
    4625                 :             :  * so that we can apply it recursively to inherited tables. Note that the
    4626                 :             :  * lock level we want as we recurse might well be higher than required for
    4627                 :             :  * that specific subcommand. So we pass down the overall lock requirement,
    4628                 :             :  * rather than reassess it at lower levels.
    4629                 :             :  *
    4630                 :             :  * The caller also provides a "context" which is to be passed back to
    4631                 :             :  * utility.c when we need to execute a subcommand such as CREATE INDEX.
    4632                 :             :  * Some of the fields therein, such as the relid, are used here as well.
    4633                 :             :  */
    4634                 :             : void
    4635                 :       21807 : AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
    4636                 :             :            AlterTableUtilityContext *context)
    4637                 :             : {
    4638                 :             :     Relation    rel;
    4639                 :             : 
    4640                 :             :     /* Caller is required to provide an adequate lock. */
    4641                 :       21807 :     rel = relation_open(context->relid, NoLock);
    4642                 :             : 
    4643                 :       21807 :     CheckAlterTableIsSafe(rel);
    4644                 :             : 
    4645                 :       21793 :     ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
    4646                 :       18844 : }
    4647                 :             : 
    4648                 :             : /*
    4649                 :             :  * AlterTableInternal
    4650                 :             :  *
    4651                 :             :  * ALTER TABLE with target specified by OID
    4652                 :             :  *
    4653                 :             :  * We do not reject if the relation is already open, because it's quite
    4654                 :             :  * likely that one or more layers of caller have it open.  That means it
    4655                 :             :  * is unsafe to use this entry point for alterations that could break
    4656                 :             :  * existing query plans.  On the assumption it's not used for such, we
    4657                 :             :  * don't have to reject pending AFTER triggers, either.
    4658                 :             :  *
    4659                 :             :  * Also, since we don't have an AlterTableUtilityContext, this cannot be
    4660                 :             :  * used for any subcommand types that require parse transformation or
    4661                 :             :  * could generate subcommands that have to be passed to ProcessUtility.
    4662                 :             :  */
    4663                 :             : void
    4664                 :         193 : AlterTableInternal(Oid relid, List *cmds, bool recurse)
    4665                 :             : {
    4666                 :             :     Relation    rel;
    4667                 :         193 :     LOCKMODE    lockmode = AlterTableGetLockLevel(cmds);
    4668                 :             : 
    4669                 :         193 :     rel = relation_open(relid, lockmode);
    4670                 :             : 
    4671                 :         193 :     EventTriggerAlterTableRelid(relid);
    4672                 :             : 
    4673                 :         193 :     ATController(NULL, rel, cmds, recurse, lockmode, NULL);
    4674                 :         193 : }
    4675                 :             : 
    4676                 :             : /*
    4677                 :             :  * AlterTableGetLockLevel
    4678                 :             :  *
    4679                 :             :  * Sets the overall lock level required for the supplied list of subcommands.
    4680                 :             :  * Policy for doing this set according to needs of AlterTable(), see
    4681                 :             :  * comments there for overall explanation.
    4682                 :             :  *
    4683                 :             :  * Function is called before and after parsing, so it must give same
    4684                 :             :  * answer each time it is called. Some subcommands are transformed
    4685                 :             :  * into other subcommand types, so the transform must never be made to a
    4686                 :             :  * lower lock level than previously assigned. All transforms are noted below.
    4687                 :             :  *
    4688                 :             :  * Since this is called before we lock the table we cannot use table metadata
    4689                 :             :  * to influence the type of lock we acquire.
    4690                 :             :  *
    4691                 :             :  * There should be no lockmodes hardcoded into the subcommand functions. All
    4692                 :             :  * lockmode decisions for ALTER TABLE are made here only. The one exception is
    4693                 :             :  * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
    4694                 :             :  * and does not travel through this section of code and cannot be combined with
    4695                 :             :  * any of the subcommands given here.
    4696                 :             :  *
    4697                 :             :  * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
    4698                 :             :  * so any changes that might affect SELECTs running on standbys need to use
    4699                 :             :  * AccessExclusiveLocks even if you think a lesser lock would do, unless you
    4700                 :             :  * have a solution for that also.
    4701                 :             :  *
    4702                 :             :  * Also note that pg_dump uses only an AccessShareLock, meaning that anything
    4703                 :             :  * that takes a lock less than AccessExclusiveLock can change object definitions
    4704                 :             :  * while pg_dump is running. Be careful to check that the appropriate data is
    4705                 :             :  * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
    4706                 :             :  * otherwise we might end up with an inconsistent dump that can't restore.
    4707                 :             :  */
    4708                 :             : LOCKMODE
    4709                 :       22182 : AlterTableGetLockLevel(List *cmds)
    4710                 :             : {
    4711                 :             :     /*
    4712                 :             :      * This only works if we read catalog tables using MVCC snapshots.
    4713                 :             :      */
    4714                 :             :     ListCell   *lcmd;
    4715                 :       22182 :     LOCKMODE    lockmode = ShareUpdateExclusiveLock;
    4716                 :             : 
    4717   [ +  -  +  +  :       45230 :     foreach(lcmd, cmds)
                   +  + ]
    4718                 :             :     {
    4719                 :       23048 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4720                 :       23048 :         LOCKMODE    cmd_lockmode = AccessExclusiveLock; /* default for compiler */
    4721                 :             : 
    4722   [ +  +  +  +  :       23048 :         switch (cmd->subtype)
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
                   +  - ]
    4723                 :             :         {
    4724                 :             :                 /*
    4725                 :             :                  * These subcommands rewrite the heap, so require full locks.
    4726                 :             :                  */
    4727                 :        2650 :             case AT_AddColumn:  /* may rewrite heap, in some cases and visible
    4728                 :             :                                  * to SELECT */
    4729                 :             :             case AT_SetAccessMethod:    /* must rewrite heap */
    4730                 :             :             case AT_SetTableSpace:  /* must rewrite heap */
    4731                 :             :             case AT_AlterColumnType:    /* must rewrite heap */
    4732                 :        2650 :                 cmd_lockmode = AccessExclusiveLock;
    4733                 :        2650 :                 break;
    4734                 :             : 
    4735                 :             :                 /*
    4736                 :             :                  * These subcommands may require addition of toast tables. If
    4737                 :             :                  * we add a toast table to a table currently being scanned, we
    4738                 :             :                  * might miss data added to the new toast table by concurrent
    4739                 :             :                  * insert transactions.
    4740                 :             :                  */
    4741                 :         168 :             case AT_SetStorage: /* may add toast tables, see
    4742                 :             :                                  * ATRewriteCatalogs() */
    4743                 :         168 :                 cmd_lockmode = AccessExclusiveLock;
    4744                 :         168 :                 break;
    4745                 :             : 
    4746                 :             :                 /*
    4747                 :             :                  * Removing constraints can affect SELECTs that have been
    4748                 :             :                  * optimized assuming the constraint holds true. See also
    4749                 :             :                  * CloneFkReferenced.
    4750                 :             :                  */
    4751                 :         788 :             case AT_DropConstraint: /* as DROP INDEX */
    4752                 :             :             case AT_DropNotNull:    /* may change some SQL plans */
    4753                 :         788 :                 cmd_lockmode = AccessExclusiveLock;
    4754                 :         788 :                 break;
    4755                 :             : 
    4756                 :             :                 /*
    4757                 :             :                  * Subcommands that may be visible to concurrent SELECTs
    4758                 :             :                  */
    4759                 :        1221 :             case AT_DropColumn: /* change visible to SELECT */
    4760                 :             :             case AT_AddColumnToView:    /* CREATE VIEW */
    4761                 :             :             case AT_DropOids:   /* used to equiv to DropColumn */
    4762                 :             :             case AT_EnableAlwaysRule:   /* may change SELECT rules */
    4763                 :             :             case AT_EnableReplicaRule:  /* may change SELECT rules */
    4764                 :             :             case AT_EnableRule: /* may change SELECT rules */
    4765                 :             :             case AT_DisableRule:    /* may change SELECT rules */
    4766                 :        1221 :                 cmd_lockmode = AccessExclusiveLock;
    4767                 :        1221 :                 break;
    4768                 :             : 
    4769                 :             :                 /*
    4770                 :             :                  * Changing owner may remove implicit SELECT privileges
    4771                 :             :                  */
    4772                 :        1169 :             case AT_ChangeOwner:    /* change visible to SELECT */
    4773                 :        1169 :                 cmd_lockmode = AccessExclusiveLock;
    4774                 :        1169 :                 break;
    4775                 :             : 
    4776                 :             :                 /*
    4777                 :             :                  * Changing foreign table options may affect optimization.
    4778                 :             :                  */
    4779                 :         143 :             case AT_GenericOptions:
    4780                 :             :             case AT_AlterColumnGenericOptions:
    4781                 :         143 :                 cmd_lockmode = AccessExclusiveLock;
    4782                 :         143 :                 break;
    4783                 :             : 
    4784                 :             :                 /*
    4785                 :             :                  * These subcommands affect write operations only.
    4786                 :             :                  */
    4787                 :         191 :             case AT_EnableTrig:
    4788                 :             :             case AT_EnableAlwaysTrig:
    4789                 :             :             case AT_EnableReplicaTrig:
    4790                 :             :             case AT_EnableTrigAll:
    4791                 :             :             case AT_EnableTrigUser:
    4792                 :             :             case AT_DisableTrig:
    4793                 :             :             case AT_DisableTrigAll:
    4794                 :             :             case AT_DisableTrigUser:
    4795                 :         191 :                 cmd_lockmode = ShareRowExclusiveLock;
    4796                 :         191 :                 break;
    4797                 :             : 
    4798                 :             :                 /*
    4799                 :             :                  * These subcommands affect write operations only. XXX
    4800                 :             :                  * Theoretically, these could be ShareRowExclusiveLock.
    4801                 :             :                  */
    4802                 :        2164 :             case AT_ColumnDefault:
    4803                 :             :             case AT_CookedColumnDefault:
    4804                 :             :             case AT_AlterConstraint:
    4805                 :             :             case AT_AddIndex:   /* from ADD CONSTRAINT */
    4806                 :             :             case AT_AddIndexConstraint:
    4807                 :             :             case AT_ReplicaIdentity:
    4808                 :             :             case AT_SetNotNull:
    4809                 :             :             case AT_EnableRowSecurity:
    4810                 :             :             case AT_DisableRowSecurity:
    4811                 :             :             case AT_ForceRowSecurity:
    4812                 :             :             case AT_NoForceRowSecurity:
    4813                 :             :             case AT_AddIdentity:
    4814                 :             :             case AT_DropIdentity:
    4815                 :             :             case AT_SetIdentity:
    4816                 :             :             case AT_SetExpression:
    4817                 :             :             case AT_DropExpression:
    4818                 :             :             case AT_SetCompression:
    4819                 :        2164 :                 cmd_lockmode = AccessExclusiveLock;
    4820                 :        2164 :                 break;
    4821                 :             : 
    4822                 :       10141 :             case AT_AddConstraint:
    4823                 :             :             case AT_ReAddConstraint:    /* becomes AT_AddConstraint */
    4824                 :             :             case AT_ReAddDomainConstraint:  /* becomes AT_AddConstraint */
    4825         [ +  - ]:       10141 :                 if (IsA(cmd->def, Constraint))
    4826                 :             :                 {
    4827                 :       10141 :                     Constraint *con = (Constraint *) cmd->def;
    4828                 :             : 
    4829      [ +  +  + ]:       10141 :                     switch (con->contype)
    4830                 :             :                     {
    4831                 :        7482 :                         case CONSTR_EXCLUSION:
    4832                 :             :                         case CONSTR_PRIMARY:
    4833                 :             :                         case CONSTR_UNIQUE:
    4834                 :             : 
    4835                 :             :                             /*
    4836                 :             :                              * Cases essentially the same as CREATE INDEX. We
    4837                 :             :                              * could reduce the lock strength to ShareLock if
    4838                 :             :                              * we can work out how to allow concurrent catalog
    4839                 :             :                              * updates. XXX Might be set down to
    4840                 :             :                              * ShareRowExclusiveLock but requires further
    4841                 :             :                              * analysis.
    4842                 :             :                              */
    4843                 :        7482 :                             cmd_lockmode = AccessExclusiveLock;
    4844                 :        7482 :                             break;
    4845                 :        1820 :                         case CONSTR_FOREIGN:
    4846                 :             : 
    4847                 :             :                             /*
    4848                 :             :                              * We add triggers to both tables when we add a
    4849                 :             :                              * Foreign Key, so the lock level must be at least
    4850                 :             :                              * as strong as CREATE TRIGGER.
    4851                 :             :                              */
    4852                 :        1820 :                             cmd_lockmode = ShareRowExclusiveLock;
    4853                 :        1820 :                             break;
    4854                 :             : 
    4855                 :         839 :                         default:
    4856                 :         839 :                             cmd_lockmode = AccessExclusiveLock;
    4857                 :             :                     }
    4858                 :             :                 }
    4859                 :       10141 :                 break;
    4860                 :             : 
    4861                 :             :                 /*
    4862                 :             :                  * These subcommands affect inheritance behaviour. Queries
    4863                 :             :                  * started before us will continue to see the old inheritance
    4864                 :             :                  * behaviour, while queries started after we commit will see
    4865                 :             :                  * new behaviour. No need to prevent reads or writes to the
    4866                 :             :                  * subtable while we hook it up though. Changing the TupDesc
    4867                 :             :                  * may be a problem, so keep highest lock.
    4868                 :             :                  */
    4869                 :         394 :             case AT_AddInherit:
    4870                 :             :             case AT_DropInherit:
    4871                 :         394 :                 cmd_lockmode = AccessExclusiveLock;
    4872                 :         394 :                 break;
    4873                 :             : 
    4874                 :             :                 /*
    4875                 :             :                  * These subcommands affect implicit row type conversion. They
    4876                 :             :                  * have affects similar to CREATE/DROP CAST on queries. don't
    4877                 :             :                  * provide for invalidating parse trees as a result of such
    4878                 :             :                  * changes, so we keep these at AccessExclusiveLock.
    4879                 :             :                  */
    4880                 :          54 :             case AT_AddOf:
    4881                 :             :             case AT_DropOf:
    4882                 :          54 :                 cmd_lockmode = AccessExclusiveLock;
    4883                 :          54 :                 break;
    4884                 :             : 
    4885                 :             :                 /*
    4886                 :             :                  * Only used by CREATE OR REPLACE VIEW which must conflict
    4887                 :             :                  * with an SELECTs currently using the view.
    4888                 :             :                  */
    4889                 :         137 :             case AT_ReplaceRelOptions:
    4890                 :         137 :                 cmd_lockmode = AccessExclusiveLock;
    4891                 :         137 :                 break;
    4892                 :             : 
    4893                 :             :                 /*
    4894                 :             :                  * These subcommands affect general strategies for performance
    4895                 :             :                  * and maintenance, though don't change the semantic results
    4896                 :             :                  * from normal data reads and writes. Delaying an ALTER TABLE
    4897                 :             :                  * behind currently active writes only delays the point where
    4898                 :             :                  * the new strategy begins to take effect, so there is no
    4899                 :             :                  * benefit in waiting. In this case the minimum restriction
    4900                 :             :                  * applies: we don't currently allow concurrent catalog
    4901                 :             :                  * updates.
    4902                 :             :                  */
    4903                 :         158 :             case AT_SetStatistics:  /* Uses MVCC in getTableAttrs() */
    4904                 :             :             case AT_ClusterOn:  /* Uses MVCC in getIndexes() */
    4905                 :             :             case AT_DropCluster:    /* Uses MVCC in getIndexes() */
    4906                 :             :             case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
    4907                 :             :             case AT_ResetOptions:   /* Uses MVCC in getTableAttrs() */
    4908                 :         158 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4909                 :         158 :                 break;
    4910                 :             : 
    4911                 :          75 :             case AT_SetLogged:
    4912                 :             :             case AT_SetUnLogged:
    4913                 :          75 :                 cmd_lockmode = AccessExclusiveLock;
    4914                 :          75 :                 break;
    4915                 :             : 
    4916                 :         279 :             case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
    4917                 :         279 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4918                 :         279 :                 break;
    4919                 :             : 
    4920                 :             :                 /*
    4921                 :             :                  * Rel options are more complex than first appears. Options
    4922                 :             :                  * are set here for tables, views and indexes; for historical
    4923                 :             :                  * reasons these can all be used with ALTER TABLE, so we can't
    4924                 :             :                  * decide between them using the basic grammar.
    4925                 :             :                  */
    4926                 :         498 :             case AT_SetRelOptions:  /* Uses MVCC in getIndexes() and
    4927                 :             :                                      * getTables() */
    4928                 :             :             case AT_ResetRelOptions:    /* Uses MVCC in getIndexes() and
    4929                 :             :                                          * getTables() */
    4930                 :         498 :                 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
    4931                 :         498 :                 break;
    4932                 :             : 
    4933                 :        1925 :             case AT_AttachPartition:
    4934                 :        1925 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4935                 :        1925 :                 break;
    4936                 :             : 
    4937                 :         385 :             case AT_DetachPartition:
    4938         [ +  + ]:         385 :                 if (((PartitionCmd *) cmd->def)->concurrent)
    4939                 :          87 :                     cmd_lockmode = ShareUpdateExclusiveLock;
    4940                 :             :                 else
    4941                 :         298 :                     cmd_lockmode = AccessExclusiveLock;
    4942                 :         385 :                 break;
    4943                 :             : 
    4944                 :          11 :             case AT_DetachPartitionFinalize:
    4945                 :          11 :                 cmd_lockmode = ShareUpdateExclusiveLock;
    4946                 :          11 :                 break;
    4947                 :             : 
    4948                 :         497 :             case AT_MergePartitions:
    4949                 :             :             case AT_SplitPartition:
    4950                 :         497 :                 cmd_lockmode = AccessExclusiveLock;
    4951                 :         497 :                 break;
    4952                 :             : 
    4953                 :           0 :             default:            /* oops */
    4954         [ #  # ]:           0 :                 elog(ERROR, "unrecognized alter table type: %d",
    4955                 :             :                      (int) cmd->subtype);
    4956                 :             :                 break;
    4957                 :             :         }
    4958                 :             : 
    4959                 :             :         /*
    4960                 :             :          * Take the greatest lockmode from any subcommand
    4961                 :             :          */
    4962         [ +  + ]:       23048 :         if (cmd_lockmode > lockmode)
    4963                 :       19342 :             lockmode = cmd_lockmode;
    4964                 :             :     }
    4965                 :             : 
    4966                 :       22182 :     return lockmode;
    4967                 :             : }
    4968                 :             : 
    4969                 :             : /*
    4970                 :             :  * ATController provides top level control over the phases.
    4971                 :             :  *
    4972                 :             :  * parsetree is passed in to allow it to be passed to event triggers
    4973                 :             :  * when requested.
    4974                 :             :  */
    4975                 :             : static void
    4976                 :       21986 : ATController(AlterTableStmt *parsetree,
    4977                 :             :              Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
    4978                 :             :              AlterTableUtilityContext *context)
    4979                 :             : {
    4980                 :       21986 :     List       *wqueue = NIL;
    4981                 :             :     ListCell   *lcmd;
    4982                 :             : 
    4983                 :             :     /* Phase 1: preliminary examination of commands, create work queue */
    4984   [ +  -  +  +  :       44529 :     foreach(lcmd, cmds)
                   +  + ]
    4985                 :             :     {
    4986                 :       22848 :         AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
    4987                 :             : 
    4988                 :       22848 :         ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
    4989                 :             :     }
    4990                 :             : 
    4991                 :             :     /* Close the relation, but keep lock until commit */
    4992                 :       21681 :     relation_close(rel, NoLock);
    4993                 :             : 
    4994                 :             :     /* Phase 2: update system catalogs */
    4995                 :       21681 :     ATRewriteCatalogs(&wqueue, lockmode, context);
    4996                 :             : 
    4997                 :             :     /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
    4998                 :       19438 :     ATRewriteTables(parsetree, &wqueue, lockmode, context);
    4999                 :       19037 : }
    5000                 :             : 
    5001                 :             : /*
    5002                 :             :  * ATPrepCmd
    5003                 :             :  *
    5004                 :             :  * Traffic cop for ALTER TABLE Phase 1 operations, including simple
    5005                 :             :  * recursion and permission checks.
    5006                 :             :  *
    5007                 :             :  * Caller must have acquired appropriate lock type on relation already.
    5008                 :             :  * This lock should be held until commit.
    5009                 :             :  */
    5010                 :             : static void
    5011                 :       23495 : ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
    5012                 :             :           bool recurse, bool recursing, LOCKMODE lockmode,
    5013                 :             :           AlterTableUtilityContext *context)
    5014                 :             : {
    5015                 :             :     AlteredTableInfo *tab;
    5016                 :       23495 :     AlterTablePass pass = AT_PASS_UNSET;
    5017                 :             : 
    5018                 :             :     /* Find or create work queue entry for this table */
    5019                 :       23495 :     tab = ATGetQueueEntry(wqueue, rel);
    5020                 :             : 
    5021                 :             :     /*
    5022                 :             :      * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
    5023                 :             :      * partitions that are pending detach.
    5024                 :             :      */
    5025         [ +  + ]:       23495 :     if (rel->rd_rel->relispartition &&
    5026   [ +  -  +  + ]:        1884 :         cmd->subtype != AT_DetachPartitionFinalize &&
    5027                 :         942 :         PartitionHasPendingDetach(RelationGetRelid(rel)))
    5028         [ +  - ]:           1 :         ereport(ERROR,
    5029                 :             :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    5030                 :             :                 errmsg("cannot alter partition \"%s\" with an incomplete detach",
    5031                 :             :                        RelationGetRelationName(rel)),
    5032                 :             :                 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
    5033                 :             : 
    5034                 :             :     /*
    5035                 :             :      * Copy the original subcommand for each table, so we can scribble on it.
    5036                 :             :      * This avoids conflicts when different child tables need to make
    5037                 :             :      * different parse transformations (for example, the same column may have
    5038                 :             :      * different column numbers in different children).
    5039                 :             :      */
    5040                 :       23494 :     cmd = copyObject(cmd);
    5041                 :             : 
    5042                 :             :     /*
    5043                 :             :      * Do permissions and relkind checking, recursion to child tables if
    5044                 :             :      * needed, and any additional phase-1 processing needed.  (But beware of
    5045                 :             :      * adding any processing that looks at table details that another
    5046                 :             :      * subcommand could change.  In some cases we reject multiple subcommands
    5047                 :             :      * that could try to change the same state in contrary ways.)
    5048                 :             :      */
    5049   [ +  +  +  +  :       23494 :     switch (cmd->subtype)
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  -  +  -  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
                +  +  - ]
    5050                 :             :     {
    5051                 :        1597 :         case AT_AddColumn:      /* ADD COLUMN */
    5052                 :        1597 :             ATSimplePermissions(cmd->subtype, rel,
    5053                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5054                 :             :                                 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    5055                 :        1597 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
    5056                 :             :                             lockmode, context);
    5057                 :             :             /* Recursion occurs during execution phase */
    5058                 :        1589 :             pass = AT_PASS_ADD_COL;
    5059                 :        1589 :             break;
    5060                 :          21 :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    5061                 :          21 :             ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
    5062                 :          21 :             ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
    5063                 :             :                             lockmode, context);
    5064                 :             :             /* Recursion occurs during execution phase */
    5065                 :          21 :             pass = AT_PASS_ADD_COL;
    5066                 :          21 :             break;
    5067                 :         411 :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    5068                 :             : 
    5069                 :             :             /*
    5070                 :             :              * We allow defaults on views so that INSERT into a view can have
    5071                 :             :              * default-ish behavior.  This works because the rewriter
    5072                 :             :              * substitutes default values into INSERTs before it expands
    5073                 :             :              * rules.
    5074                 :             :              */
    5075                 :         411 :             ATSimplePermissions(cmd->subtype, rel,
    5076                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    5077                 :             :                                 ATT_FOREIGN_TABLE);
    5078                 :         411 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5079                 :             :             /* No command-specific prep needed */
    5080         [ +  + ]:         411 :             pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
    5081                 :         411 :             break;
    5082                 :          57 :         case AT_CookedColumnDefault:    /* add a pre-cooked default */
    5083                 :             :             /* This is currently used only in CREATE TABLE */
    5084                 :             :             /* (so the permission check really isn't necessary) */
    5085                 :          57 :             ATSimplePermissions(cmd->subtype, rel,
    5086                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5087                 :             :             /* This command never recurses */
    5088                 :          57 :             pass = AT_PASS_ADD_OTHERCONSTR;
    5089                 :          57 :             break;
    5090                 :         107 :         case AT_AddIdentity:
    5091                 :         107 :             ATSimplePermissions(cmd->subtype, rel,
    5092                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    5093                 :             :                                 ATT_FOREIGN_TABLE);
    5094                 :             :             /* Set up recursion for phase 2; no other prep needed */
    5095         [ +  + ]:         107 :             if (recurse)
    5096                 :         103 :                 cmd->recurse = true;
    5097                 :         107 :             pass = AT_PASS_ADD_OTHERCONSTR;
    5098                 :         107 :             break;
    5099                 :          41 :         case AT_SetIdentity:
    5100                 :          41 :             ATSimplePermissions(cmd->subtype, rel,
    5101                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    5102                 :             :                                 ATT_FOREIGN_TABLE);
    5103                 :             :             /* Set up recursion for phase 2; no other prep needed */
    5104         [ +  + ]:          41 :             if (recurse)
    5105                 :          37 :                 cmd->recurse = true;
    5106                 :             :             /* This should run after AddIdentity, so do it in MISC pass */
    5107                 :          41 :             pass = AT_PASS_MISC;
    5108                 :          41 :             break;
    5109                 :          37 :         case AT_DropIdentity:
    5110                 :          37 :             ATSimplePermissions(cmd->subtype, rel,
    5111                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    5112                 :             :                                 ATT_FOREIGN_TABLE);
    5113                 :             :             /* Set up recursion for phase 2; no other prep needed */
    5114         [ +  + ]:          37 :             if (recurse)
    5115                 :          33 :                 cmd->recurse = true;
    5116                 :          37 :             pass = AT_PASS_DROP;
    5117                 :          37 :             break;
    5118                 :         181 :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    5119                 :         181 :             ATSimplePermissions(cmd->subtype, rel,
    5120                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5121                 :             :             /* Set up recursion for phase 2; no other prep needed */
    5122         [ +  + ]:         177 :             if (recurse)
    5123                 :         165 :                 cmd->recurse = true;
    5124                 :         177 :             pass = AT_PASS_DROP;
    5125                 :         177 :             break;
    5126                 :         276 :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    5127                 :         276 :             ATSimplePermissions(cmd->subtype, rel,
    5128                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5129                 :             :             /* Set up recursion for phase 2; no other prep needed */
    5130         [ +  + ]:         272 :             if (recurse)
    5131                 :         252 :                 cmd->recurse = true;
    5132                 :         272 :             pass = AT_PASS_COL_ATTRS;
    5133                 :         272 :             break;
    5134                 :         193 :         case AT_SetExpression:  /* ALTER COLUMN SET EXPRESSION */
    5135                 :         193 :             ATSimplePermissions(cmd->subtype, rel,
    5136                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5137                 :         193 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5138                 :         193 :             pass = AT_PASS_SET_EXPRESSION;
    5139                 :         193 :             break;
    5140                 :          97 :         case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
    5141                 :          97 :             ATSimplePermissions(cmd->subtype, rel,
    5142                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5143                 :          97 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5144                 :          97 :             ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
    5145                 :          73 :             pass = AT_PASS_DROP;
    5146                 :          73 :             break;
    5147                 :         111 :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    5148                 :         111 :             ATSimplePermissions(cmd->subtype, rel,
    5149                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
    5150                 :             :                                 ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
    5151                 :         111 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5152                 :             :             /* No command-specific prep needed */
    5153                 :         111 :             pass = AT_PASS_MISC;
    5154                 :         111 :             break;
    5155                 :          29 :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    5156                 :             :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    5157                 :          29 :             ATSimplePermissions(cmd->subtype, rel,
    5158                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5159                 :             :                                 ATT_MATVIEW | ATT_FOREIGN_TABLE);
    5160                 :             :             /* This command never recurses */
    5161                 :          21 :             pass = AT_PASS_MISC;
    5162                 :          21 :             break;
    5163                 :         182 :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    5164                 :         182 :             ATSimplePermissions(cmd->subtype, rel,
    5165                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5166                 :             :                                 ATT_MATVIEW | ATT_FOREIGN_TABLE);
    5167                 :         182 :             ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
    5168                 :             :             /* No command-specific prep needed */
    5169                 :         182 :             pass = AT_PASS_MISC;
    5170                 :         182 :             break;
    5171                 :          48 :         case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    5172                 :          48 :             ATSimplePermissions(cmd->subtype, rel,
    5173                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5174                 :             :             /* This command never recurses */
    5175                 :             :             /* No command-specific prep needed */
    5176                 :          48 :             pass = AT_PASS_MISC;
    5177                 :          48 :             break;
    5178                 :        1147 :         case AT_DropColumn:     /* DROP COLUMN */
    5179                 :        1147 :             ATSimplePermissions(cmd->subtype, rel,
    5180                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5181                 :             :                                 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    5182                 :        1143 :             ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
    5183                 :             :                              lockmode, context);
    5184                 :             :             /* Recursion occurs during execution phase */
    5185                 :        1135 :             pass = AT_PASS_DROP;
    5186                 :        1135 :             break;
    5187                 :           0 :         case AT_AddIndex:       /* ADD INDEX */
    5188                 :           0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
    5189                 :             :             /* This command never recurses */
    5190                 :             :             /* No command-specific prep needed */
    5191                 :           0 :             pass = AT_PASS_ADD_INDEX;
    5192                 :           0 :             break;
    5193                 :       10446 :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    5194                 :       10446 :             ATSimplePermissions(cmd->subtype, rel,
    5195                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5196                 :       10446 :             ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
    5197         [ +  + ]:       10426 :             if (recurse)
    5198                 :             :             {
    5199                 :             :                 /* recurses at exec time; lock descendants and set flag */
    5200                 :       10178 :                 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
    5201                 :       10178 :                 cmd->recurse = true;
    5202                 :             :             }
    5203                 :       10426 :             pass = AT_PASS_ADD_CONSTR;
    5204                 :       10426 :             break;
    5205                 :           0 :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    5206                 :           0 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
    5207                 :             :             /* This command never recurses */
    5208                 :             :             /* No command-specific prep needed */
    5209                 :           0 :             pass = AT_PASS_ADD_INDEXCONSTR;
    5210                 :           0 :             break;
    5211                 :         582 :         case AT_DropConstraint: /* DROP CONSTRAINT */
    5212                 :         582 :             ATSimplePermissions(cmd->subtype, rel,
    5213                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5214                 :         582 :             ATCheckPartitionsNotInUse(rel, lockmode);
    5215                 :             :             /* Other recursion occurs during execution phase */
    5216                 :             :             /* No command-specific prep needed except saving recurse flag */
    5217         [ +  + ]:         578 :             if (recurse)
    5218                 :         554 :                 cmd->recurse = true;
    5219                 :         578 :             pass = AT_PASS_DROP;
    5220                 :         578 :             break;
    5221                 :         963 :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    5222                 :         963 :             ATSimplePermissions(cmd->subtype, rel,
    5223                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE |
    5224                 :             :                                 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
    5225                 :             :             /* See comments for ATPrepAlterColumnType */
    5226                 :         963 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
    5227                 :             :                                       AT_PASS_UNSET, context);
    5228                 :             :             Assert(cmd != NULL);
    5229                 :             :             /* Performs own recursion */
    5230                 :         959 :             ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
    5231                 :             :                                   lockmode, context);
    5232                 :         828 :             pass = AT_PASS_ALTER_TYPE;
    5233                 :         828 :             break;
    5234                 :          94 :         case AT_AlterColumnGenericOptions:
    5235                 :          94 :             ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    5236                 :             :             /* This command never recurses */
    5237                 :             :             /* No command-specific prep needed */
    5238                 :          94 :             pass = AT_PASS_MISC;
    5239                 :          94 :             break;
    5240                 :        1149 :         case AT_ChangeOwner:    /* ALTER OWNER */
    5241                 :             :             /* This command never recurses */
    5242                 :             :             /* No command-specific prep needed */
    5243                 :        1149 :             pass = AT_PASS_MISC;
    5244                 :        1149 :             break;
    5245                 :          43 :         case AT_ClusterOn:      /* CLUSTER ON */
    5246                 :             :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    5247                 :          43 :             ATSimplePermissions(cmd->subtype, rel,
    5248                 :             :                                 ATT_TABLE | ATT_MATVIEW);
    5249                 :             :             /* These commands never recurse */
    5250                 :             :             /* No command-specific prep needed */
    5251                 :          35 :             pass = AT_PASS_MISC;
    5252                 :          35 :             break;
    5253                 :          75 :         case AT_SetLogged:      /* SET LOGGED */
    5254                 :             :         case AT_SetUnLogged:    /* SET UNLOGGED */
    5255                 :          75 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
    5256         [ -  + ]:          67 :             if (tab->chgPersistence)
    5257         [ #  # ]:           0 :                 ereport(ERROR,
    5258                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5259                 :             :                          errmsg("cannot change persistence setting twice")));
    5260                 :          67 :             ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
    5261                 :          59 :             pass = AT_PASS_MISC;
    5262                 :          59 :             break;
    5263                 :           4 :         case AT_DropOids:       /* SET WITHOUT OIDS */
    5264                 :           4 :             ATSimplePermissions(cmd->subtype, rel,
    5265                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5266                 :           4 :             pass = AT_PASS_DROP;
    5267                 :           4 :             break;
    5268                 :          85 :         case AT_SetAccessMethod:    /* SET ACCESS METHOD */
    5269                 :          85 :             ATSimplePermissions(cmd->subtype, rel,
    5270                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5271                 :             : 
    5272                 :             :             /* check if another access method change was already requested */
    5273         [ +  + ]:          85 :             if (tab->chgAccessMethod)
    5274         [ +  - ]:          12 :                 ereport(ERROR,
    5275                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5276                 :             :                          errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
    5277                 :             : 
    5278                 :          73 :             ATPrepSetAccessMethod(tab, rel, cmd->name);
    5279                 :          73 :             pass = AT_PASS_MISC;    /* does not matter; no work in Phase 2 */
    5280                 :          73 :             break;
    5281                 :         105 :         case AT_SetTableSpace:  /* SET TABLESPACE */
    5282                 :         105 :             ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
    5283                 :             :                                 ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
    5284                 :             :             /* This command never recurses */
    5285                 :         105 :             ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
    5286                 :         105 :             pass = AT_PASS_MISC;    /* doesn't actually matter */
    5287                 :         105 :             break;
    5288                 :         633 :         case AT_SetRelOptions:  /* SET (...) */
    5289                 :             :         case AT_ResetRelOptions:    /* RESET (...) */
    5290                 :             :         case AT_ReplaceRelOptions:  /* reset them all, then set just these */
    5291                 :         633 :             ATSimplePermissions(cmd->subtype, rel,
    5292                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
    5293                 :             :                                 ATT_MATVIEW | ATT_INDEX);
    5294                 :             :             /* This command never recurses */
    5295                 :             :             /* No command-specific prep needed */
    5296                 :         632 :             pass = AT_PASS_MISC;
    5297                 :         632 :             break;
    5298                 :         313 :         case AT_AddInherit:     /* INHERIT */
    5299                 :         313 :             ATSimplePermissions(cmd->subtype, rel,
    5300                 :             :                                 ATT_TABLE | ATT_FOREIGN_TABLE);
    5301                 :             :             /* This command never recurses */
    5302                 :         309 :             ATPrepChangeInherit(rel);
    5303                 :         297 :             pass = AT_PASS_MISC;
    5304                 :         297 :             break;
    5305                 :          81 :         case AT_DropInherit:    /* NO INHERIT */
    5306                 :          81 :             ATSimplePermissions(cmd->subtype, rel,
    5307                 :             :                                 ATT_TABLE | ATT_FOREIGN_TABLE);
    5308                 :             :             /* This command never recurses */
    5309                 :          77 :             ATPrepChangeInherit(rel);
    5310                 :          69 :             pass = AT_PASS_MISC;
    5311                 :          69 :             break;
    5312                 :         340 :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    5313                 :         340 :             ATSimplePermissions(cmd->subtype, rel,
    5314                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE);
    5315                 :             :             /* Recursion occurs during execution phase */
    5316         [ +  + ]:         336 :             if (recurse)
    5317                 :         324 :                 cmd->recurse = true;
    5318                 :         336 :             pass = AT_PASS_MISC;
    5319                 :         336 :             break;
    5320                 :         279 :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5321                 :         279 :             ATSimplePermissions(cmd->subtype, rel,
    5322                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5323                 :             :             /* Recursion occurs during execution phase */
    5324                 :             :             /* No command-specific prep needed except saving recurse flag */
    5325         [ +  - ]:         279 :             if (recurse)
    5326                 :         279 :                 cmd->recurse = true;
    5327                 :         279 :             pass = AT_PASS_MISC;
    5328                 :         279 :             break;
    5329                 :         314 :         case AT_ReplicaIdentity:    /* REPLICA IDENTITY ... */
    5330                 :         314 :             ATSimplePermissions(cmd->subtype, rel,
    5331                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
    5332                 :         314 :             pass = AT_PASS_MISC;
    5333                 :             :             /* This command never recurses */
    5334                 :             :             /* No command-specific prep needed */
    5335                 :         314 :             break;
    5336                 :         191 :         case AT_EnableTrig:     /* ENABLE TRIGGER variants */
    5337                 :             :         case AT_EnableAlwaysTrig:
    5338                 :             :         case AT_EnableReplicaTrig:
    5339                 :             :         case AT_EnableTrigAll:
    5340                 :             :         case AT_EnableTrigUser:
    5341                 :             :         case AT_DisableTrig:    /* DISABLE TRIGGER variants */
    5342                 :             :         case AT_DisableTrigAll:
    5343                 :             :         case AT_DisableTrigUser:
    5344                 :         191 :             ATSimplePermissions(cmd->subtype, rel,
    5345                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    5346                 :             :             /* Set up recursion for phase 2; no other prep needed */
    5347         [ +  + ]:         191 :             if (recurse)
    5348                 :         174 :                 cmd->recurse = true;
    5349                 :         191 :             pass = AT_PASS_MISC;
    5350                 :         191 :             break;
    5351                 :         427 :         case AT_EnableRule:     /* ENABLE/DISABLE RULE variants */
    5352                 :             :         case AT_EnableAlwaysRule:
    5353                 :             :         case AT_EnableReplicaRule:
    5354                 :             :         case AT_DisableRule:
    5355                 :             :         case AT_AddOf:          /* OF */
    5356                 :             :         case AT_DropOf:         /* NOT OF */
    5357                 :             :         case AT_EnableRowSecurity:
    5358                 :             :         case AT_DisableRowSecurity:
    5359                 :             :         case AT_ForceRowSecurity:
    5360                 :             :         case AT_NoForceRowSecurity:
    5361                 :         427 :             ATSimplePermissions(cmd->subtype, rel,
    5362                 :             :                                 ATT_TABLE | ATT_PARTITIONED_TABLE);
    5363                 :             :             /* These commands never recurse */
    5364                 :             :             /* No command-specific prep needed */
    5365                 :         427 :             pass = AT_PASS_MISC;
    5366                 :         427 :             break;
    5367                 :          33 :         case AT_GenericOptions:
    5368                 :          33 :             ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
    5369                 :             :             /* No command-specific prep needed */
    5370                 :          33 :             pass = AT_PASS_MISC;
    5371                 :          33 :             break;
    5372                 :        1917 :         case AT_AttachPartition:
    5373                 :        1917 :             ATSimplePermissions(cmd->subtype, rel,
    5374                 :             :                                 ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
    5375                 :             :             /* No command-specific prep needed */
    5376                 :        1913 :             pass = AT_PASS_MISC;
    5377                 :        1913 :             break;
    5378                 :         385 :         case AT_DetachPartition:
    5379                 :         385 :             ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
    5380                 :             :             /* No command-specific prep needed */
    5381                 :         373 :             pass = AT_PASS_MISC;
    5382                 :         373 :             break;
    5383                 :          11 :         case AT_DetachPartitionFinalize:
    5384                 :          11 :             ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
    5385                 :             :             /* No command-specific prep needed */
    5386                 :           7 :             pass = AT_PASS_MISC;
    5387                 :           7 :             break;
    5388                 :         489 :         case AT_MergePartitions:
    5389                 :             :         case AT_SplitPartition:
    5390                 :         489 :             ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
    5391                 :             :             /* No command-specific prep needed */
    5392                 :         485 :             pass = AT_PASS_MISC;
    5393                 :         485 :             break;
    5394                 :           0 :         default:                /* oops */
    5395         [ #  # ]:           0 :             elog(ERROR, "unrecognized alter table type: %d",
    5396                 :             :                  (int) cmd->subtype);
    5397                 :             :             pass = AT_PASS_UNSET;   /* keep compiler quiet */
    5398                 :             :             break;
    5399                 :             :     }
    5400                 :             :     Assert(pass > AT_PASS_UNSET);
    5401                 :             : 
    5402                 :             :     /* Add the subcommand to the appropriate list for phase 2 */
    5403                 :       23182 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
    5404                 :       23182 : }
    5405                 :             : 
    5406                 :             : /*
    5407                 :             :  * ATRewriteCatalogs
    5408                 :             :  *
    5409                 :             :  * Traffic cop for ALTER TABLE Phase 2 operations.  Subcommands are
    5410                 :             :  * dispatched in a "safe" execution order (designed to avoid unnecessary
    5411                 :             :  * conflicts).
    5412                 :             :  */
    5413                 :             : static void
    5414                 :       21681 : ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
    5415                 :             :                   AlterTableUtilityContext *context)
    5416                 :             : {
    5417                 :             :     ListCell   *ltab;
    5418                 :             : 
    5419                 :             :     /*
    5420                 :             :      * We process all the tables "in parallel", one pass at a time.  This is
    5421                 :             :      * needed because we may have to propagate work from one table to another
    5422                 :             :      * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
    5423                 :             :      * re-adding of the foreign key constraint to the other table).  Work can
    5424                 :             :      * only be propagated into later passes, however.
    5425                 :             :      */
    5426         [ +  + ]:      272355 :     for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
    5427                 :             :     {
    5428                 :             :         /* Go through each table that needs to be processed */
    5429   [ +  -  +  +  :      515251 :         foreach(ltab, *wqueue)
                   +  + ]
    5430                 :             :         {
    5431                 :      264577 :             AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5432                 :      264577 :             List       *subcmds = tab->subcmds[pass];
    5433                 :             :             ListCell   *lcmd;
    5434                 :             : 
    5435         [ +  + ]:      264577 :             if (subcmds == NIL)
    5436                 :      227597 :                 continue;
    5437                 :             : 
    5438                 :             :             /*
    5439                 :             :              * Open the relation and store it in tab.  This allows subroutines
    5440                 :             :              * close and reopen, if necessary.  Appropriate lock was obtained
    5441                 :             :              * by phase 1, needn't get it again.
    5442                 :             :              */
    5443                 :       36980 :             tab->rel = relation_open(tab->relid, NoLock);
    5444                 :             : 
    5445   [ +  -  +  +  :       74135 :             foreach(lcmd, subcmds)
                   +  + ]
    5446                 :       39398 :                 ATExecCmd(wqueue, tab,
    5447                 :       39398 :                           lfirst_node(AlterTableCmd, lcmd),
    5448                 :             :                           lockmode, pass, context);
    5449                 :             : 
    5450                 :             :             /*
    5451                 :             :              * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
    5452                 :             :              * (this is not done in ATExecAlterColumnType since it should be
    5453                 :             :              * done only once if multiple columns of a table are altered).
    5454                 :             :              */
    5455   [ +  +  +  + ]:       34737 :             if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
    5456                 :         925 :                 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
    5457                 :             : 
    5458         [ +  - ]:       34737 :             if (tab->rel)
    5459                 :             :             {
    5460                 :       34737 :                 relation_close(tab->rel, NoLock);
    5461                 :       34737 :                 tab->rel = NULL;
    5462                 :             :             }
    5463                 :             :         }
    5464                 :             :     }
    5465                 :             : 
    5466                 :             :     /* Check to see if a toast table must be added. */
    5467   [ +  -  +  +  :       41947 :     foreach(ltab, *wqueue)
                   +  + ]
    5468                 :             :     {
    5469                 :       22509 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5470                 :             : 
    5471                 :             :         /*
    5472                 :             :          * If the table is source table of ATTACH PARTITION command, we did
    5473                 :             :          * not modify anything about it that will change its toasting
    5474                 :             :          * requirement, so no need to check.
    5475                 :             :          */
    5476         [ +  + ]:       22509 :         if (((tab->relkind == RELKIND_RELATION ||
    5477         [ +  + ]:        4412 :               tab->relkind == RELKIND_PARTITIONED_TABLE) &&
    5478         [ +  + ]:       21266 :              tab->partition_constraint == NULL) ||
    5479         [ +  + ]:        2655 :             tab->relkind == RELKIND_MATVIEW)
    5480                 :       19883 :             AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
    5481                 :             :     }
    5482                 :       19438 : }
    5483                 :             : 
    5484                 :             : /*
    5485                 :             :  * ATExecCmd: dispatch a subcommand to appropriate execution routine
    5486                 :             :  */
    5487                 :             : static void
    5488                 :       39398 : ATExecCmd(List **wqueue, AlteredTableInfo *tab,
    5489                 :             :           AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
    5490                 :             :           AlterTableUtilityContext *context)
    5491                 :             : {
    5492                 :       39398 :     ObjectAddress address = InvalidObjectAddress;
    5493                 :       39398 :     Relation    rel = tab->rel;
    5494                 :             : 
    5495   [ +  +  +  +  :       39398 :     switch (cmd->subtype)
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  -  +  -  
          +  +  -  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
                      - ]
    5496                 :             :     {
    5497                 :        1606 :         case AT_AddColumn:      /* ADD COLUMN */
    5498                 :             :         case AT_AddColumnToView:    /* add column via CREATE OR REPLACE VIEW */
    5499                 :        1606 :             address = ATExecAddColumn(wqueue, tab, rel, &cmd,
    5500                 :        1606 :                                       cmd->recurse, false,
    5501                 :             :                                       lockmode, cur_pass, context);
    5502                 :        1438 :             break;
    5503                 :         387 :         case AT_ColumnDefault:  /* ALTER COLUMN DEFAULT */
    5504                 :         387 :             address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
    5505                 :         339 :             break;
    5506                 :          57 :         case AT_CookedColumnDefault:    /* add a pre-cooked default */
    5507                 :          57 :             address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
    5508                 :          53 :             break;
    5509                 :         107 :         case AT_AddIdentity:
    5510                 :         107 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5511                 :             :                                       cur_pass, context);
    5512                 :             :             Assert(cmd != NULL);
    5513                 :          99 :             address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
    5514                 :          63 :             break;
    5515                 :          41 :         case AT_SetIdentity:
    5516                 :          41 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5517                 :             :                                       cur_pass, context);
    5518                 :             :             Assert(cmd != NULL);
    5519                 :          41 :             address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
    5520                 :          25 :             break;
    5521                 :          37 :         case AT_DropIdentity:
    5522                 :          37 :             address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
    5523                 :          25 :             break;
    5524                 :         177 :         case AT_DropNotNull:    /* ALTER COLUMN DROP NOT NULL */
    5525                 :         177 :             address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
    5526                 :         109 :             break;
    5527                 :         272 :         case AT_SetNotNull:     /* ALTER COLUMN SET NOT NULL */
    5528                 :         272 :             address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
    5529                 :         272 :                                        cmd->recurse, false, lockmode);
    5530                 :         252 :             break;
    5531                 :         193 :         case AT_SetExpression:
    5532                 :         193 :             address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
    5533                 :         181 :             break;
    5534                 :          65 :         case AT_DropExpression:
    5535                 :          65 :             address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
    5536                 :          49 :             break;
    5537                 :         111 :         case AT_SetStatistics:  /* ALTER COLUMN SET STATISTICS */
    5538                 :         111 :             address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
    5539                 :          79 :             break;
    5540                 :          17 :         case AT_SetOptions:     /* ALTER COLUMN SET ( options ) */
    5541                 :          17 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
    5542                 :          17 :             break;
    5543                 :           4 :         case AT_ResetOptions:   /* ALTER COLUMN RESET ( options ) */
    5544                 :           4 :             address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
    5545                 :           4 :             break;
    5546                 :         182 :         case AT_SetStorage:     /* ALTER COLUMN SET STORAGE */
    5547                 :         182 :             address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
    5548                 :         174 :             break;
    5549                 :          48 :         case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
    5550                 :          48 :             address = ATExecSetCompression(rel, cmd->name, cmd->def,
    5551                 :             :                                            lockmode);
    5552                 :          44 :             break;
    5553                 :        1135 :         case AT_DropColumn:     /* DROP COLUMN */
    5554                 :        1135 :             address = ATExecDropColumn(wqueue, rel, cmd->name,
    5555                 :        1135 :                                        cmd->behavior, cmd->recurse, false,
    5556                 :        1135 :                                        cmd->missing_ok, lockmode,
    5557                 :             :                                        NULL);
    5558                 :        1007 :             break;
    5559                 :         778 :         case AT_AddIndex:       /* ADD INDEX */
    5560                 :         778 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
    5561                 :             :                                      lockmode);
    5562                 :         665 :             break;
    5563                 :         330 :         case AT_ReAddIndex:     /* ADD INDEX */
    5564                 :         330 :             address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
    5565                 :             :                                      lockmode);
    5566                 :         330 :             break;
    5567                 :          57 :         case AT_ReAddStatistics:    /* ADD STATISTICS */
    5568                 :          57 :             address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
    5569                 :             :                                           true, lockmode);
    5570                 :          57 :             break;
    5571                 :       18538 :         case AT_AddConstraint:  /* ADD CONSTRAINT */
    5572                 :             :             /* Transform the command only during initial examination */
    5573         [ +  + ]:       18538 :             if (cur_pass == AT_PASS_ADD_CONSTR)
    5574                 :       10406 :                 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
    5575                 :       10426 :                                           cmd->recurse, lockmode,
    5576                 :             :                                           cur_pass, context);
    5577                 :             :             /* Depending on constraint type, might be no more work to do now */
    5578         [ +  + ]:       18518 :             if (cmd != NULL)
    5579                 :             :                 address =
    5580                 :        8112 :                     ATExecAddConstraint(wqueue, tab, rel,
    5581                 :        8112 :                                         (Constraint *) cmd->def,
    5582                 :        8112 :                                         cmd->recurse, false, lockmode);
    5583                 :       18057 :             break;
    5584                 :         269 :         case AT_ReAddConstraint:    /* Re-add pre-existing check constraint */
    5585                 :             :             address =
    5586                 :         269 :                 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
    5587                 :             :                                     true, true, lockmode);
    5588                 :         261 :             break;
    5589                 :          13 :         case AT_ReAddDomainConstraint:  /* Re-add pre-existing domain check
    5590                 :             :                                          * constraint */
    5591                 :             :             address =
    5592                 :          13 :                 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
    5593                 :          13 :                                          ((AlterDomainStmt *) cmd->def)->def,
    5594                 :             :                                          NULL, true);
    5595                 :           9 :             break;
    5596                 :          52 :         case AT_ReAddComment:   /* Re-add existing comment */
    5597                 :          52 :             address = CommentObject((CommentStmt *) cmd->def);
    5598                 :          52 :             break;
    5599                 :        6653 :         case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
    5600                 :        6653 :             address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
    5601                 :             :                                                lockmode);
    5602                 :        6645 :             break;
    5603                 :         336 :         case AT_AlterConstraint:    /* ALTER CONSTRAINT */
    5604                 :         336 :             address = ATExecAlterConstraint(wqueue, rel,
    5605                 :         336 :                                             castNode(ATAlterConstraint, cmd->def),
    5606                 :         336 :                                             cmd->recurse, lockmode);
    5607                 :         260 :             break;
    5608                 :         279 :         case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
    5609                 :         279 :             address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
    5610                 :             :                                                false, lockmode);
    5611                 :         275 :             break;
    5612                 :         578 :         case AT_DropConstraint: /* DROP CONSTRAINT */
    5613                 :         578 :             ATExecDropConstraint(rel, cmd->name, cmd->behavior,
    5614                 :         578 :                                  cmd->recurse,
    5615                 :         578 :                                  cmd->missing_ok, lockmode);
    5616                 :         438 :             break;
    5617                 :         804 :         case AT_AlterColumnType:    /* ALTER COLUMN TYPE */
    5618                 :             :             /* parse transformation was done earlier */
    5619                 :         804 :             address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
    5620                 :         776 :             break;
    5621                 :          94 :         case AT_AlterColumnGenericOptions:  /* ALTER COLUMN OPTIONS */
    5622                 :             :             address =
    5623                 :          94 :                 ATExecAlterColumnGenericOptions(rel, cmd->name,
    5624                 :          94 :                                                 (List *) cmd->def, lockmode);
    5625                 :          90 :             break;
    5626                 :        1149 :         case AT_ChangeOwner:    /* ALTER OWNER */
    5627                 :        1146 :             ATExecChangeOwner(RelationGetRelid(rel),
    5628                 :        1149 :                               get_rolespec_oid(cmd->newowner, false),
    5629                 :             :                               false, lockmode);
    5630                 :        1138 :             break;
    5631                 :          39 :         case AT_ClusterOn:      /* CLUSTER ON */
    5632                 :          39 :             address = ATExecClusterOn(rel, cmd->name, lockmode);
    5633                 :          39 :             break;
    5634                 :           8 :         case AT_DropCluster:    /* SET WITHOUT CLUSTER */
    5635                 :           8 :             ATExecDropCluster(rel, lockmode);
    5636                 :           8 :             break;
    5637                 :          59 :         case AT_SetLogged:      /* SET LOGGED */
    5638                 :             :         case AT_SetUnLogged:    /* SET UNLOGGED */
    5639                 :          59 :             break;
    5640                 :           4 :         case AT_DropOids:       /* SET WITHOUT OIDS */
    5641                 :             :             /* nothing to do here, oid columns don't exist anymore */
    5642                 :           4 :             break;
    5643                 :          61 :         case AT_SetAccessMethod:    /* SET ACCESS METHOD */
    5644                 :             : 
    5645                 :             :             /*
    5646                 :             :              * Only do this for partitioned tables, for which this is just a
    5647                 :             :              * catalog change.  Tables with storage are handled by Phase 3.
    5648                 :             :              */
    5649         [ +  + ]:          61 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
    5650         [ +  + ]:          33 :                 tab->chgAccessMethod)
    5651                 :          29 :                 ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
    5652                 :          61 :             break;
    5653                 :         105 :         case AT_SetTableSpace:  /* SET TABLESPACE */
    5654                 :             : 
    5655                 :             :             /*
    5656                 :             :              * Only do this for partitioned tables and indexes, for which this
    5657                 :             :              * is just a catalog change.  Other relation types which have
    5658                 :             :              * storage are handled by Phase 3.
    5659                 :             :              */
    5660         [ +  + ]:         105 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
    5661         [ +  + ]:          97 :                 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    5662                 :          24 :                 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
    5663                 :             : 
    5664                 :         101 :             break;
    5665                 :         632 :         case AT_SetRelOptions:  /* SET (...) */
    5666                 :             :         case AT_ResetRelOptions:    /* RESET (...) */
    5667                 :             :         case AT_ReplaceRelOptions:  /* replace entire option list */
    5668                 :         632 :             ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
    5669                 :         598 :             break;
    5670                 :          65 :         case AT_EnableTrig:     /* ENABLE TRIGGER name */
    5671                 :          65 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5672                 :             :                                        TRIGGER_FIRES_ON_ORIGIN, false,
    5673                 :          65 :                                        cmd->recurse,
    5674                 :             :                                        lockmode);
    5675                 :          65 :             break;
    5676                 :          27 :         case AT_EnableAlwaysTrig:   /* ENABLE ALWAYS TRIGGER name */
    5677                 :          27 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5678                 :             :                                        TRIGGER_FIRES_ALWAYS, false,
    5679                 :          27 :                                        cmd->recurse,
    5680                 :             :                                        lockmode);
    5681                 :          27 :             break;
    5682                 :           8 :         case AT_EnableReplicaTrig:  /* ENABLE REPLICA TRIGGER name */
    5683                 :           8 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5684                 :             :                                        TRIGGER_FIRES_ON_REPLICA, false,
    5685                 :           8 :                                        cmd->recurse,
    5686                 :             :                                        lockmode);
    5687                 :           8 :             break;
    5688                 :          75 :         case AT_DisableTrig:    /* DISABLE TRIGGER name */
    5689                 :          75 :             ATExecEnableDisableTrigger(rel, cmd->name,
    5690                 :             :                                        TRIGGER_DISABLED, false,
    5691                 :          75 :                                        cmd->recurse,
    5692                 :             :                                        lockmode);
    5693                 :          75 :             break;
    5694                 :           0 :         case AT_EnableTrigAll:  /* ENABLE TRIGGER ALL */
    5695                 :           0 :             ATExecEnableDisableTrigger(rel, NULL,
    5696                 :             :                                        TRIGGER_FIRES_ON_ORIGIN, false,
    5697                 :           0 :                                        cmd->recurse,
    5698                 :             :                                        lockmode);
    5699                 :           0 :             break;
    5700                 :           8 :         case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
    5701                 :           8 :             ATExecEnableDisableTrigger(rel, NULL,
    5702                 :             :                                        TRIGGER_DISABLED, false,
    5703                 :           8 :                                        cmd->recurse,
    5704                 :             :                                        lockmode);
    5705                 :           8 :             break;
    5706                 :           0 :         case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
    5707                 :           0 :             ATExecEnableDisableTrigger(rel, NULL,
    5708                 :             :                                        TRIGGER_FIRES_ON_ORIGIN, true,
    5709                 :           0 :                                        cmd->recurse,
    5710                 :             :                                        lockmode);
    5711                 :           0 :             break;
    5712                 :           8 :         case AT_DisableTrigUser:    /* DISABLE TRIGGER USER */
    5713                 :           8 :             ATExecEnableDisableTrigger(rel, NULL,
    5714                 :             :                                        TRIGGER_DISABLED, true,
    5715                 :           8 :                                        cmd->recurse,
    5716                 :             :                                        lockmode);
    5717                 :           8 :             break;
    5718                 :             : 
    5719                 :           5 :         case AT_EnableRule:     /* ENABLE RULE name */
    5720                 :           5 :             ATExecEnableDisableRule(rel, cmd->name,
    5721                 :             :                                     RULE_FIRES_ON_ORIGIN, lockmode);
    5722                 :           5 :             break;
    5723                 :           0 :         case AT_EnableAlwaysRule:   /* ENABLE ALWAYS RULE name */
    5724                 :           0 :             ATExecEnableDisableRule(rel, cmd->name,
    5725                 :             :                                     RULE_FIRES_ALWAYS, lockmode);
    5726                 :           0 :             break;
    5727                 :           4 :         case AT_EnableReplicaRule:  /* ENABLE REPLICA RULE name */
    5728                 :           4 :             ATExecEnableDisableRule(rel, cmd->name,
    5729                 :             :                                     RULE_FIRES_ON_REPLICA, lockmode);
    5730                 :           4 :             break;
    5731                 :          20 :         case AT_DisableRule:    /* DISABLE RULE name */
    5732                 :          20 :             ATExecEnableDisableRule(rel, cmd->name,
    5733                 :             :                                     RULE_DISABLED, lockmode);
    5734                 :          20 :             break;
    5735                 :             : 
    5736                 :         297 :         case AT_AddInherit:
    5737                 :         297 :             address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
    5738                 :         217 :             break;
    5739                 :          69 :         case AT_DropInherit:
    5740                 :          69 :             address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
    5741                 :          65 :             break;
    5742                 :          50 :         case AT_AddOf:
    5743                 :          50 :             address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
    5744                 :          22 :             break;
    5745                 :           4 :         case AT_DropOf:
    5746                 :           4 :             ATExecDropOf(rel, lockmode);
    5747                 :           4 :             break;
    5748                 :         326 :         case AT_ReplicaIdentity:
    5749                 :         326 :             ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
    5750                 :         290 :             break;
    5751                 :         248 :         case AT_EnableRowSecurity:
    5752                 :         248 :             ATExecSetRowSecurity(rel, true);
    5753                 :         248 :             break;
    5754                 :           6 :         case AT_DisableRowSecurity:
    5755                 :           6 :             ATExecSetRowSecurity(rel, false);
    5756                 :           6 :             break;
    5757                 :          70 :         case AT_ForceRowSecurity:
    5758                 :          70 :             ATExecForceNoForceRowSecurity(rel, true);
    5759                 :          70 :             break;
    5760                 :          20 :         case AT_NoForceRowSecurity:
    5761                 :          20 :             ATExecForceNoForceRowSecurity(rel, false);
    5762                 :          20 :             break;
    5763                 :          33 :         case AT_GenericOptions:
    5764                 :          33 :             ATExecGenericOptions(rel, (List *) cmd->def);
    5765                 :          32 :             break;
    5766                 :        1913 :         case AT_AttachPartition:
    5767                 :        1913 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5768                 :             :                                       cur_pass, context);
    5769                 :             :             Assert(cmd != NULL);
    5770         [ +  + ]:        1897 :             if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    5771                 :        1613 :                 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
    5772                 :             :                                                 context);
    5773                 :             :             else
    5774                 :         284 :                 address = ATExecAttachPartitionIdx(wqueue, rel,
    5775                 :         284 :                                                    ((PartitionCmd *) cmd->def)->name);
    5776                 :        1629 :             break;
    5777                 :         373 :         case AT_DetachPartition:
    5778                 :         373 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5779                 :             :                                       cur_pass, context);
    5780                 :             :             Assert(cmd != NULL);
    5781                 :             :             /* ATPrepCmd ensures it must be a table */
    5782                 :             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5783                 :         373 :             address = ATExecDetachPartition(wqueue, tab, rel,
    5784                 :         373 :                                             ((PartitionCmd *) cmd->def)->name,
    5785                 :         373 :                                             ((PartitionCmd *) cmd->def)->concurrent);
    5786                 :         296 :             break;
    5787                 :           7 :         case AT_DetachPartitionFinalize:
    5788                 :           7 :             address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
    5789                 :           7 :             break;
    5790                 :         200 :         case AT_MergePartitions:
    5791                 :         200 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5792                 :             :                                       cur_pass, context);
    5793                 :             :             Assert(cmd != NULL);
    5794                 :             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5795                 :         144 :             ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
    5796                 :             :                                   context);
    5797                 :         106 :             break;
    5798                 :         285 :         case AT_SplitPartition:
    5799                 :         285 :             cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
    5800                 :             :                                       cur_pass, context);
    5801                 :             :             Assert(cmd != NULL);
    5802                 :             :             Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    5803                 :         153 :             ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
    5804                 :             :                                  context);
    5805                 :         141 :             break;
    5806                 :           0 :         default:                /* oops */
    5807         [ #  # ]:           0 :             elog(ERROR, "unrecognized alter table type: %d",
    5808                 :             :                  (int) cmd->subtype);
    5809                 :             :             break;
    5810                 :             :     }
    5811                 :             : 
    5812                 :             :     /*
    5813                 :             :      * Report the subcommand to interested event triggers.
    5814                 :             :      */
    5815         [ +  + ]:       37155 :     if (cmd)
    5816                 :       26749 :         EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
    5817                 :             : 
    5818                 :             :     /*
    5819                 :             :      * Bump the command counter to ensure the next subcommand in the sequence
    5820                 :             :      * can see the changes so far
    5821                 :             :      */
    5822                 :       37155 :     CommandCounterIncrement();
    5823                 :       37155 : }
    5824                 :             : 
    5825                 :             : /*
    5826                 :             :  * ATParseTransformCmd: perform parse transformation for one subcommand
    5827                 :             :  *
    5828                 :             :  * Returns the transformed subcommand tree, if there is one, else NULL.
    5829                 :             :  *
    5830                 :             :  * The parser may hand back additional AlterTableCmd(s) and/or other
    5831                 :             :  * utility statements, either before or after the original subcommand.
    5832                 :             :  * Other AlterTableCmds are scheduled into the appropriate slot of the
    5833                 :             :  * AlteredTableInfo (they had better be for later passes than the current one).
    5834                 :             :  * Utility statements that are supposed to happen before the AlterTableCmd
    5835                 :             :  * are executed immediately.  Those that are supposed to happen afterwards
    5836                 :             :  * are added to the tab->afterStmts list to be done at the very end.
    5837                 :             :  */
    5838                 :             : static AlterTableCmd *
    5839                 :       15821 : ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
    5840                 :             :                     AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    5841                 :             :                     AlterTablePass cur_pass, AlterTableUtilityContext *context)
    5842                 :             : {
    5843                 :       15821 :     AlterTableCmd *newcmd = NULL;
    5844                 :       15821 :     AlterTableStmt *atstmt = makeNode(AlterTableStmt);
    5845                 :             :     List       *beforeStmts;
    5846                 :             :     List       *afterStmts;
    5847                 :             :     ListCell   *lc;
    5848                 :             : 
    5849                 :             :     /* Gin up an AlterTableStmt with just this subcommand and this table */
    5850                 :       15821 :     atstmt->relation =
    5851                 :       15821 :         makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
    5852                 :       15821 :                      pstrdup(RelationGetRelationName(rel)),
    5853                 :             :                      -1);
    5854                 :       15821 :     atstmt->relation->inh = recurse;
    5855                 :       15821 :     atstmt->cmds = list_make1(cmd);
    5856                 :       15821 :     atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
    5857                 :       15821 :     atstmt->missing_ok = false;
    5858                 :             : 
    5859                 :             :     /* Transform the AlterTableStmt */
    5860                 :       15821 :     atstmt = transformAlterTableStmt(RelationGetRelid(rel),
    5861                 :             :                                      atstmt,
    5862                 :             :                                      context->queryString,
    5863                 :             :                                      &beforeStmts,
    5864                 :             :                                      &afterStmts);
    5865                 :             : 
    5866                 :             :     /* Execute any statements that should happen before these subcommand(s) */
    5867   [ +  +  +  +  :       15896 :     foreach(lc, beforeStmts)
                   +  + ]
    5868                 :             :     {
    5869                 :         315 :         Node       *stmt = (Node *) lfirst(lc);
    5870                 :             : 
    5871                 :         315 :         ProcessUtilityForAlterTable(stmt, context);
    5872                 :         307 :         CommandCounterIncrement();
    5873                 :             :     }
    5874                 :             : 
    5875                 :             :     /* Examine the transformed subcommands and schedule them appropriately */
    5876   [ +  -  +  +  :       36323 :     foreach(lc, atstmt->cmds)
                   +  + ]
    5877                 :             :     {
    5878                 :       20742 :         AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
    5879                 :             :         AlterTablePass pass;
    5880                 :             : 
    5881                 :             :         /*
    5882                 :             :          * This switch need only cover the subcommand types that can be added
    5883                 :             :          * by parse_utilcmd.c; otherwise, we'll use the default strategy of
    5884                 :             :          * executing the subcommand immediately, as a substitute for the
    5885                 :             :          * original subcommand.  (Note, however, that this does cause
    5886                 :             :          * AT_AddConstraint subcommands to be rescheduled into later passes,
    5887                 :             :          * which is important for index and foreign key constraints.)
    5888                 :             :          *
    5889                 :             :          * We assume we needn't do any phase-1 checks for added subcommands.
    5890                 :             :          */
    5891   [ +  +  +  -  :       20742 :         switch (cmd2->subtype)
                      + ]
    5892                 :             :         {
    5893                 :         794 :             case AT_AddIndex:
    5894                 :         794 :                 pass = AT_PASS_ADD_INDEX;
    5895                 :         794 :                 break;
    5896                 :        6653 :             case AT_AddIndexConstraint:
    5897                 :        6653 :                 pass = AT_PASS_ADD_INDEXCONSTR;
    5898                 :        6653 :                 break;
    5899                 :        8120 :             case AT_AddConstraint:
    5900                 :             :                 /* Recursion occurs during execution phase */
    5901         [ +  + ]:        8120 :                 if (recurse)
    5902                 :        8083 :                     cmd2->recurse = true;
    5903      [ +  -  + ]:        8120 :                 switch (castNode(Constraint, cmd2->def)->contype)
    5904                 :             :                 {
    5905                 :        5596 :                     case CONSTR_NOTNULL:
    5906                 :        5596 :                         pass = AT_PASS_COL_ATTRS;
    5907                 :        5596 :                         break;
    5908                 :           0 :                     case CONSTR_PRIMARY:
    5909                 :             :                     case CONSTR_UNIQUE:
    5910                 :             :                     case CONSTR_EXCLUSION:
    5911                 :           0 :                         pass = AT_PASS_ADD_INDEXCONSTR;
    5912                 :           0 :                         break;
    5913                 :        2524 :                     default:
    5914                 :        2524 :                         pass = AT_PASS_ADD_OTHERCONSTR;
    5915                 :        2524 :                         break;
    5916                 :             :                 }
    5917                 :        8120 :                 break;
    5918                 :           0 :             case AT_AlterColumnGenericOptions:
    5919                 :             :                 /* This command never recurses */
    5920                 :             :                 /* No command-specific prep needed */
    5921                 :           0 :                 pass = AT_PASS_MISC;
    5922                 :           0 :                 break;
    5923                 :        5175 :             default:
    5924                 :        5175 :                 pass = cur_pass;
    5925                 :        5175 :                 break;
    5926                 :             :         }
    5927                 :             : 
    5928         [ -  + ]:       20742 :         if (pass < cur_pass)
    5929                 :             :         {
    5930                 :             :             /* Cannot schedule into a pass we already finished */
    5931         [ #  # ]:           0 :             elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
    5932                 :             :                  pass);
    5933                 :             :         }
    5934         [ +  + ]:       20742 :         else if (pass > cur_pass)
    5935                 :             :         {
    5936                 :             :             /* OK, queue it up for later */
    5937                 :       15567 :             tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
    5938                 :             :         }
    5939                 :             :         else
    5940                 :             :         {
    5941                 :             :             /*
    5942                 :             :              * We should see at most one subcommand for the current pass,
    5943                 :             :              * which is the transformed version of the original subcommand.
    5944                 :             :              */
    5945   [ +  -  +  - ]:        5175 :             if (newcmd == NULL && cmd->subtype == cmd2->subtype)
    5946                 :             :             {
    5947                 :             :                 /* Found the transformed version of our subcommand */
    5948                 :        5175 :                 newcmd = cmd2;
    5949                 :             :             }
    5950                 :             :             else
    5951         [ #  # ]:           0 :                 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
    5952                 :             :                      pass);
    5953                 :             :         }
    5954                 :             :     }
    5955                 :             : 
    5956                 :             :     /* Queue up any after-statements to happen at the end */
    5957                 :       15581 :     tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
    5958                 :             : 
    5959                 :       15581 :     return newcmd;
    5960                 :             : }
    5961                 :             : 
    5962                 :             : /*
    5963                 :             :  * ATRewriteTables: ALTER TABLE phase 3
    5964                 :             :  */
    5965                 :             : static void
    5966                 :       19438 : ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
    5967                 :             :                 AlterTableUtilityContext *context)
    5968                 :             : {
    5969                 :             :     ListCell   *ltab;
    5970                 :             : 
    5971                 :             :     /* Go through each table that needs to be checked or rewritten */
    5972   [ +  -  +  +  :       41533 :     foreach(ltab, *wqueue)
                   +  + ]
    5973                 :             :     {
    5974                 :       22425 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    5975                 :             : 
    5976                 :             :         /* Relations without storage may be ignored here */
    5977   [ +  +  +  +  :       22425 :         if (!RELKIND_HAS_STORAGE(tab->relkind))
          +  +  +  -  +  
                      + ]
    5978                 :        4234 :             continue;
    5979                 :             : 
    5980                 :             :         /*
    5981                 :             :          * If we change column data types, the operation has to be propagated
    5982                 :             :          * to tables that use this table's rowtype as a column type.
    5983                 :             :          * tab->newvals will also be non-NULL in the case where we're adding a
    5984                 :             :          * column with a default.  We choose to forbid that case as well,
    5985                 :             :          * since composite types might eventually support defaults.
    5986                 :             :          *
    5987                 :             :          * (Eventually we'll probably need to check for composite type
    5988                 :             :          * dependencies even when we're just scanning the table without a
    5989                 :             :          * rewrite, but at the moment a composite type does not enforce any
    5990                 :             :          * constraints, so it's not necessary/appropriate to enforce them just
    5991                 :             :          * during ALTER.)
    5992                 :             :          */
    5993   [ +  +  +  + ]:       18191 :         if (tab->newvals != NIL || tab->rewrite > 0)
    5994                 :             :         {
    5995                 :             :             Relation    rel;
    5996                 :             : 
    5997                 :        1329 :             rel = table_open(tab->relid, NoLock);
    5998                 :        1329 :             find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
    5999                 :        1293 :             table_close(rel, NoLock);
    6000                 :             :         }
    6001                 :             : 
    6002                 :             :         /*
    6003                 :             :          * We only need to rewrite the table if at least one column needs to
    6004                 :             :          * be recomputed, or we are changing its persistence or access method.
    6005                 :             :          *
    6006                 :             :          * There are two reasons for requiring a rewrite when changing
    6007                 :             :          * persistence: on one hand, we need to ensure that the buffers
    6008                 :             :          * belonging to each of the two relations are marked with or without
    6009                 :             :          * BM_PERMANENT properly.  On the other hand, since rewriting creates
    6010                 :             :          * and assigns a new relfilenumber, we automatically create or drop an
    6011                 :             :          * init fork for the relation as appropriate.
    6012                 :             :          */
    6013   [ +  +  +  + ]:       18155 :         if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
    6014                 :         732 :         {
    6015                 :             :             /* Build a temporary relation and copy data */
    6016                 :             :             Relation    OldHeap;
    6017                 :             :             Oid         OIDNewHeap;
    6018                 :             :             Oid         NewAccessMethod;
    6019                 :             :             Oid         NewTableSpace;
    6020                 :             :             char        persistence;
    6021                 :             : 
    6022                 :         793 :             OldHeap = table_open(tab->relid, NoLock);
    6023                 :             : 
    6024                 :             :             /*
    6025                 :             :              * We don't support rewriting of system catalogs; there are too
    6026                 :             :              * many corner cases and too little benefit.  In particular this
    6027                 :             :              * is certainly not going to work for mapped catalogs.
    6028                 :             :              */
    6029         [ -  + ]:         793 :             if (IsSystemRelation(OldHeap))
    6030         [ #  # ]:           0 :                 ereport(ERROR,
    6031                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6032                 :             :                          errmsg("cannot rewrite system relation \"%s\"",
    6033                 :             :                                 RelationGetRelationName(OldHeap))));
    6034                 :             : 
    6035   [ +  +  -  +  :         793 :             if (RelationIsUsedAsCatalogTable(OldHeap))
             -  -  +  + ]
    6036         [ +  - ]:           1 :                 ereport(ERROR,
    6037                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6038                 :             :                          errmsg("cannot rewrite table \"%s\" used as a catalog table",
    6039                 :             :                                 RelationGetRelationName(OldHeap))));
    6040                 :             : 
    6041                 :             :             /*
    6042                 :             :              * Don't allow rewrite on temp tables of other backends ... their
    6043                 :             :              * local buffer manager is not going to cope.  (This is redundant
    6044                 :             :              * with the check in CheckAlterTableIsSafe, but for safety we'll
    6045                 :             :              * check here too.)
    6046                 :             :              */
    6047   [ +  +  -  + ]:         792 :             if (RELATION_IS_OTHER_TEMP(OldHeap))
    6048         [ #  # ]:           0 :                 ereport(ERROR,
    6049                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    6050                 :             :                          errmsg("cannot rewrite temporary tables of other sessions")));
    6051                 :             : 
    6052                 :             :             /*
    6053                 :             :              * Select destination tablespace (same as original unless user
    6054                 :             :              * requested a change)
    6055                 :             :              */
    6056         [ -  + ]:         792 :             if (tab->newTableSpace)
    6057                 :           0 :                 NewTableSpace = tab->newTableSpace;
    6058                 :             :             else
    6059                 :         792 :                 NewTableSpace = OldHeap->rd_rel->reltablespace;
    6060                 :             : 
    6061                 :             :             /*
    6062                 :             :              * Select destination access method (same as original unless user
    6063                 :             :              * requested a change)
    6064                 :             :              */
    6065         [ +  + ]:         792 :             if (tab->chgAccessMethod)
    6066                 :          24 :                 NewAccessMethod = tab->newAccessMethod;
    6067                 :             :             else
    6068                 :         768 :                 NewAccessMethod = OldHeap->rd_rel->relam;
    6069                 :             : 
    6070                 :             :             /*
    6071                 :             :              * Select persistence of transient table (same as original unless
    6072                 :             :              * user requested a change)
    6073                 :             :              */
    6074         [ +  + ]:         792 :             persistence = tab->chgPersistence ?
    6075                 :         757 :                 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
    6076                 :             : 
    6077                 :         792 :             table_close(OldHeap, NoLock);
    6078                 :             : 
    6079                 :             :             /*
    6080                 :             :              * Fire off an Event Trigger now, before actually rewriting the
    6081                 :             :              * table.
    6082                 :             :              *
    6083                 :             :              * We don't support Event Trigger for nested commands anywhere,
    6084                 :             :              * here included, and parsetree is given NULL when coming from
    6085                 :             :              * AlterTableInternal.
    6086                 :             :              *
    6087                 :             :              * And fire it only once.
    6088                 :             :              */
    6089         [ +  - ]:         792 :             if (parsetree)
    6090                 :         792 :                 EventTriggerTableRewrite((Node *) parsetree,
    6091                 :             :                                          tab->relid,
    6092                 :             :                                          tab->rewrite);
    6093                 :             : 
    6094                 :             :             /*
    6095                 :             :              * Create transient table that will receive the modified data.
    6096                 :             :              *
    6097                 :             :              * Ensure it is marked correctly as logged or unlogged.  We have
    6098                 :             :              * to do this here so that buffers for the new relfilenumber will
    6099                 :             :              * have the right persistence set, and at the same time ensure
    6100                 :             :              * that the original filenumbers's buffers will get read in with
    6101                 :             :              * the correct setting (i.e. the original one).  Otherwise a
    6102                 :             :              * rollback after the rewrite would possibly result with buffers
    6103                 :             :              * for the original filenumbers having the wrong persistence
    6104                 :             :              * setting.
    6105                 :             :              *
    6106                 :             :              * NB: This relies on swap_relation_files() also swapping the
    6107                 :             :              * persistence. That wouldn't work for pg_class, but that can't be
    6108                 :             :              * unlogged anyway.
    6109                 :             :              */
    6110                 :         788 :             OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
    6111                 :             :                                        persistence, lockmode);
    6112                 :             : 
    6113                 :             :             /*
    6114                 :             :              * Copy the heap data into the new table with the desired
    6115                 :             :              * modifications, and test the current data within the table
    6116                 :             :              * against new constraints generated by ALTER TABLE commands.
    6117                 :             :              */
    6118                 :         788 :             ATRewriteTable(tab, OIDNewHeap);
    6119                 :             : 
    6120                 :             :             /*
    6121                 :             :              * Swap the physical files of the old and new heaps, then rebuild
    6122                 :             :              * indexes and discard the old heap.  We can use RecentXmin for
    6123                 :             :              * the table's new relfrozenxid because we rewrote all the tuples
    6124                 :             :              * in ATRewriteTable, so no older Xid remains in the table.  Also,
    6125                 :             :              * we never try to swap toast tables by content, since we have no
    6126                 :             :              * interest in letting this code work on system catalogs.
    6127                 :             :              */
    6128                 :         736 :             finish_heap_swap(tab->relid, OIDNewHeap,
    6129                 :             :                              false, false, true,
    6130                 :         736 :                              !OidIsValid(tab->newTableSpace),
    6131                 :             :                              true,  /* reindex */
    6132                 :             :                              RecentXmin,
    6133                 :             :                              ReadNextMultiXactId(),
    6134                 :             :                              persistence);
    6135                 :             : 
    6136         [ -  + ]:         732 :             InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
    6137                 :             :         }
    6138   [ +  +  +  - ]:       17362 :         else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
    6139                 :             :         {
    6140         [ +  - ]:          16 :             if (tab->chgPersistence)
    6141                 :          16 :                 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
    6142                 :             :         }
    6143                 :             :         else
    6144                 :             :         {
    6145                 :             :             /*
    6146                 :             :              * If required, test the current data within the table against new
    6147                 :             :              * constraints generated by ALTER TABLE commands, but don't
    6148                 :             :              * rebuild data.
    6149                 :             :              */
    6150   [ +  +  +  + ]:       17346 :             if (tab->constraints != NIL || tab->verify_new_notnull ||
    6151         [ +  + ]:       15140 :                 tab->partition_constraint != NULL)
    6152                 :        3520 :                 ATRewriteTable(tab, InvalidOid);
    6153                 :             : 
    6154                 :             :             /*
    6155                 :             :              * If we had SET TABLESPACE but no reason to reconstruct tuples,
    6156                 :             :              * just do a block-by-block copy.
    6157                 :             :              */
    6158         [ +  + ]:       17113 :             if (tab->newTableSpace)
    6159                 :          81 :                 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
    6160                 :             :         }
    6161                 :             : 
    6162                 :             :         /*
    6163                 :             :          * Also change persistence of owned sequences, so that it matches the
    6164                 :             :          * table persistence.
    6165                 :             :          */
    6166         [ +  + ]:       17861 :         if (tab->chgPersistence)
    6167                 :             :         {
    6168                 :          51 :             List       *seqlist = getOwnedSequences(tab->relid);
    6169                 :             :             ListCell   *lc;
    6170                 :             : 
    6171   [ +  +  +  +  :          83 :             foreach(lc, seqlist)
                   +  + ]
    6172                 :             :             {
    6173                 :          32 :                 Oid         seq_relid = lfirst_oid(lc);
    6174                 :             : 
    6175                 :          32 :                 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
    6176                 :             :             }
    6177                 :             :         }
    6178                 :             :     }
    6179                 :             : 
    6180                 :             :     /*
    6181                 :             :      * Foreign key constraints are checked in a final pass, since (a) it's
    6182                 :             :      * generally best to examine each one separately, and (b) it's at least
    6183                 :             :      * theoretically possible that we have changed both relations of the
    6184                 :             :      * foreign key, and we'd better have finished both rewrites before we try
    6185                 :             :      * to read the tables.
    6186                 :             :      */
    6187   [ +  -  +  +  :       40954 :     foreach(ltab, *wqueue)
                   +  + ]
    6188                 :             :     {
    6189                 :       21917 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    6190                 :       21917 :         Relation    rel = NULL;
    6191                 :             :         ListCell   *lcon;
    6192                 :             : 
    6193                 :             :         /* Relations without storage may be ignored here too */
    6194   [ +  +  +  +  :       21917 :         if (!RELKIND_HAS_STORAGE(tab->relkind))
          +  +  +  -  +  
                      + ]
    6195                 :        4129 :             continue;
    6196                 :             : 
    6197   [ +  +  +  +  :       19175 :         foreach(lcon, tab->constraints)
                   +  + ]
    6198                 :             :         {
    6199                 :        1458 :             NewConstraint *con = lfirst(lcon);
    6200                 :             : 
    6201         [ +  + ]:        1458 :             if (con->contype == CONSTR_FOREIGN)
    6202                 :             :             {
    6203                 :         832 :                 Constraint *fkconstraint = (Constraint *) con->qual;
    6204                 :             :                 Relation    refrel;
    6205                 :             : 
    6206         [ +  + ]:         832 :                 if (rel == NULL)
    6207                 :             :                 {
    6208                 :             :                     /* Long since locked, no need for another */
    6209                 :         824 :                     rel = table_open(tab->relid, NoLock);
    6210                 :             :                 }
    6211                 :             : 
    6212                 :         832 :                 refrel = table_open(con->refrelid, RowShareLock);
    6213                 :             : 
    6214                 :         832 :                 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
    6215                 :             :                                              con->refindid,
    6216                 :             :                                              con->conid,
    6217                 :         832 :                                              con->conwithperiod);
    6218                 :             : 
    6219                 :             :                 /*
    6220                 :             :                  * No need to mark the constraint row as validated, we did
    6221                 :             :                  * that when we inserted the row earlier.
    6222                 :             :                  */
    6223                 :             : 
    6224                 :         761 :                 table_close(refrel, NoLock);
    6225                 :             :             }
    6226                 :             :         }
    6227                 :             : 
    6228         [ +  + ]:       17717 :         if (rel)
    6229                 :         753 :             table_close(rel, NoLock);
    6230                 :             :     }
    6231                 :             : 
    6232                 :             :     /* Finally, run any afterStmts that were queued up */
    6233   [ +  -  +  +  :       40856 :     foreach(ltab, *wqueue)
                   +  + ]
    6234                 :             :     {
    6235                 :       21819 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
    6236                 :             :         ListCell   *lc;
    6237                 :             : 
    6238   [ +  +  +  +  :       21875 :         foreach(lc, tab->afterStmts)
                   +  + ]
    6239                 :             :         {
    6240                 :          56 :             Node       *stmt = (Node *) lfirst(lc);
    6241                 :             : 
    6242                 :          56 :             ProcessUtilityForAlterTable(stmt, context);
    6243                 :          56 :             CommandCounterIncrement();
    6244                 :             :         }
    6245                 :             :     }
    6246                 :       19037 : }
    6247                 :             : 
    6248                 :             : /*
    6249                 :             :  * ATRewriteTable: scan or rewrite one table
    6250                 :             :  *
    6251                 :             :  * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
    6252                 :             :  * must already hold AccessExclusiveLock on it.
    6253                 :             :  */
    6254                 :             : static void
    6255                 :        4308 : ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
    6256                 :             : {
    6257                 :             :     Relation    oldrel;
    6258                 :             :     Relation    newrel;
    6259                 :             :     TupleDesc   oldTupDesc;
    6260                 :             :     TupleDesc   newTupDesc;
    6261                 :        4308 :     bool        needscan = false;
    6262                 :             :     List       *notnull_attrs;
    6263                 :             :     List       *notnull_virtual_attrs;
    6264                 :             :     int         i;
    6265                 :             :     ListCell   *l;
    6266                 :             :     EState     *estate;
    6267                 :             :     CommandId   mycid;
    6268                 :             :     BulkInsertState bistate;
    6269                 :             :     uint32      ti_options;
    6270                 :        4308 :     ExprState  *partqualstate = NULL;
    6271                 :             : 
    6272                 :             :     /*
    6273                 :             :      * Open the relation(s).  We have surely already locked the existing
    6274                 :             :      * table.
    6275                 :             :      */
    6276                 :        4308 :     oldrel = table_open(tab->relid, NoLock);
    6277                 :        4308 :     oldTupDesc = tab->oldDesc;
    6278                 :        4308 :     newTupDesc = RelationGetDescr(oldrel);  /* includes all mods */
    6279                 :             : 
    6280         [ +  + ]:        4308 :     if (OidIsValid(OIDNewHeap))
    6281                 :             :     {
    6282                 :             :         Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
    6283                 :             :                                           false));
    6284                 :         788 :         newrel = table_open(OIDNewHeap, NoLock);
    6285                 :             :     }
    6286                 :             :     else
    6287                 :        3520 :         newrel = NULL;
    6288                 :             : 
    6289                 :             :     /*
    6290                 :             :      * Prepare a BulkInsertState and options for table_tuple_insert.  The FSM
    6291                 :             :      * is empty, so don't bother using it.
    6292                 :             :      */
    6293         [ +  + ]:        4308 :     if (newrel)
    6294                 :             :     {
    6295                 :         788 :         mycid = GetCurrentCommandId(true);
    6296                 :         788 :         bistate = GetBulkInsertState();
    6297                 :         788 :         ti_options = TABLE_INSERT_SKIP_FSM;
    6298                 :             :     }
    6299                 :             :     else
    6300                 :             :     {
    6301                 :             :         /* keep compiler quiet about using these uninitialized */
    6302                 :        3520 :         mycid = 0;
    6303                 :        3520 :         bistate = NULL;
    6304                 :        3520 :         ti_options = 0;
    6305                 :             :     }
    6306                 :             : 
    6307                 :             :     /*
    6308                 :             :      * Generate the constraint and default execution states
    6309                 :             :      */
    6310                 :             : 
    6311                 :        4308 :     estate = CreateExecutorState();
    6312                 :             : 
    6313                 :             :     /* Build the needed expression execution states */
    6314   [ +  +  +  +  :        5930 :     foreach(l, tab->constraints)
                   +  + ]
    6315                 :             :     {
    6316                 :        1622 :         NewConstraint *con = lfirst(l);
    6317                 :             : 
    6318      [ +  +  - ]:        1622 :         switch (con->contype)
    6319                 :             :         {
    6320                 :         786 :             case CONSTR_CHECK:
    6321                 :         786 :                 needscan = true;
    6322                 :         786 :                 con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
    6323                 :         786 :                 break;
    6324                 :         836 :             case CONSTR_FOREIGN:
    6325                 :             :                 /* Nothing to do here */
    6326                 :         836 :                 break;
    6327                 :           0 :             default:
    6328         [ #  # ]:           0 :                 elog(ERROR, "unrecognized constraint type: %d",
    6329                 :             :                      (int) con->contype);
    6330                 :             :         }
    6331                 :             :     }
    6332                 :             : 
    6333                 :             :     /* Build expression execution states for partition check quals */
    6334         [ +  + ]:        4308 :     if (tab->partition_constraint)
    6335                 :             :     {
    6336                 :        1408 :         needscan = true;
    6337                 :        1408 :         partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
    6338                 :             :     }
    6339                 :             : 
    6340   [ +  +  +  +  :        5157 :     foreach(l, tab->newvals)
                   +  + ]
    6341                 :             :     {
    6342                 :         849 :         NewColumnValue *ex = lfirst(l);
    6343                 :             : 
    6344                 :             :         /* expr already planned */
    6345                 :         849 :         ex->exprstate = ExecInitExpr(ex->expr, NULL);
    6346                 :             :     }
    6347                 :             : 
    6348                 :        4308 :     notnull_attrs = notnull_virtual_attrs = NIL;
    6349   [ +  +  +  + ]:        4308 :     if (newrel || tab->verify_new_notnull)
    6350                 :             :     {
    6351                 :             :         /*
    6352                 :             :          * If we are rebuilding the tuples OR if we added any new but not
    6353                 :             :          * verified not-null constraints, check all *valid* not-null
    6354                 :             :          * constraints. This is a bit of overkill but it minimizes risk of
    6355                 :             :          * bugs.
    6356                 :             :          *
    6357                 :             :          * notnull_attrs does *not* collect attribute numbers for valid
    6358                 :             :          * not-null constraints over virtual generated columns; instead, they
    6359                 :             :          * are collected in notnull_virtual_attrs for verification elsewhere.
    6360                 :             :          */
    6361         [ +  + ]:        5518 :         for (i = 0; i < newTupDesc->natts; i++)
    6362                 :             :         {
    6363                 :        4025 :             CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
    6364                 :             : 
    6365         [ +  + ]:        4025 :             if (attr->attnullability == ATTNULLABLE_VALID &&
    6366         [ +  - ]:        1512 :                 !attr->attisdropped)
    6367                 :             :             {
    6368                 :        1512 :                 Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
    6369                 :             : 
    6370         [ +  + ]:        1512 :                 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
    6371                 :        1448 :                     notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
    6372                 :             :                 else
    6373                 :          64 :                     notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
    6374                 :          64 :                                                         wholeatt->attnum);
    6375                 :             :             }
    6376                 :             :         }
    6377   [ +  +  +  + ]:        1493 :         if (notnull_attrs || notnull_virtual_attrs)
    6378                 :        1095 :             needscan = true;
    6379                 :             :     }
    6380                 :             : 
    6381   [ +  +  +  + ]:        4308 :     if (newrel || needscan)
    6382                 :             :     {
    6383                 :             :         ExprContext *econtext;
    6384                 :             :         TupleTableSlot *oldslot;
    6385                 :             :         TupleTableSlot *newslot;
    6386                 :             :         TableScanDesc scan;
    6387                 :             :         MemoryContext oldCxt;
    6388                 :        3602 :         List       *dropped_attrs = NIL;
    6389                 :             :         ListCell   *lc;
    6390                 :             :         Snapshot    snapshot;
    6391                 :        3602 :         ResultRelInfo *rInfo = NULL;
    6392                 :             : 
    6393                 :             :         /*
    6394                 :             :          * When adding or changing a virtual generated column with a not-null
    6395                 :             :          * constraint, we need to evaluate whether the generation expression
    6396                 :             :          * is null.  For that, we borrow ExecRelGenVirtualNotNull().  Here, we
    6397                 :             :          * prepare a dummy ResultRelInfo.
    6398                 :             :          */
    6399         [ +  + ]:        3602 :         if (notnull_virtual_attrs != NIL)
    6400                 :             :         {
    6401                 :             :             MemoryContext oldcontext;
    6402                 :             : 
    6403                 :             :             Assert(newTupDesc->constr->has_generated_virtual);
    6404                 :             :             Assert(newTupDesc->constr->has_not_null);
    6405                 :          44 :             oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
    6406                 :          44 :             rInfo = makeNode(ResultRelInfo);
    6407                 :          44 :             InitResultRelInfo(rInfo,
    6408                 :             :                               oldrel,
    6409                 :             :                               0,    /* dummy rangetable index */
    6410                 :             :                               NULL,
    6411                 :             :                               estate->es_instrument);
    6412                 :          44 :             MemoryContextSwitchTo(oldcontext);
    6413                 :             :         }
    6414                 :             : 
    6415         [ +  + ]:        3602 :         if (newrel)
    6416         [ -  + ]:         788 :             ereport(DEBUG1,
    6417                 :             :                     (errmsg_internal("rewriting table \"%s\"",
    6418                 :             :                                      RelationGetRelationName(oldrel))));
    6419                 :             :         else
    6420         [ +  + ]:        2814 :             ereport(DEBUG1,
    6421                 :             :                     (errmsg_internal("verifying table \"%s\"",
    6422                 :             :                                      RelationGetRelationName(oldrel))));
    6423                 :             : 
    6424         [ +  + ]:        3602 :         if (newrel)
    6425                 :             :         {
    6426                 :             :             /*
    6427                 :             :              * All predicate locks on the tuples or pages are about to be made
    6428                 :             :              * invalid, because we move tuples around.  Promote them to
    6429                 :             :              * relation locks.
    6430                 :             :              */
    6431                 :         788 :             TransferPredicateLocksToHeapRelation(oldrel);
    6432                 :             :         }
    6433                 :             : 
    6434         [ -  + ]:        3602 :         econtext = GetPerTupleExprContext(estate);
    6435                 :             : 
    6436                 :             :         /*
    6437                 :             :          * Create necessary tuple slots. When rewriting, two slots are needed,
    6438                 :             :          * otherwise one suffices. In the case where one slot suffices, we
    6439                 :             :          * need to use the new tuple descriptor, otherwise some constraints
    6440                 :             :          * can't be evaluated.  Note that even when the tuple layout is the
    6441                 :             :          * same and no rewrite is required, the tupDescs might not be
    6442                 :             :          * (consider ADD COLUMN without a default).
    6443                 :             :          */
    6444         [ +  + ]:        3602 :         if (tab->rewrite)
    6445                 :             :         {
    6446                 :             :             Assert(newrel != NULL);
    6447                 :         788 :             oldslot = MakeSingleTupleTableSlot(oldTupDesc,
    6448                 :             :                                                table_slot_callbacks(oldrel));
    6449                 :         788 :             newslot = MakeSingleTupleTableSlot(newTupDesc,
    6450                 :             :                                                table_slot_callbacks(newrel));
    6451                 :             : 
    6452                 :             :             /*
    6453                 :             :              * Set all columns in the new slot to NULL initially, to ensure
    6454                 :             :              * columns added as part of the rewrite are initialized to NULL.
    6455                 :             :              * That is necessary as tab->newvals will not contain an
    6456                 :             :              * expression for columns with a NULL default, e.g. when adding a
    6457                 :             :              * column without a default together with a column with a default
    6458                 :             :              * requiring an actual rewrite.
    6459                 :             :              */
    6460                 :         788 :             ExecStoreAllNullTuple(newslot);
    6461                 :             :         }
    6462                 :             :         else
    6463                 :             :         {
    6464                 :        2814 :             oldslot = MakeSingleTupleTableSlot(newTupDesc,
    6465                 :             :                                                table_slot_callbacks(oldrel));
    6466                 :        2814 :             newslot = NULL;
    6467                 :             :         }
    6468                 :             : 
    6469                 :             :         /*
    6470                 :             :          * Any attributes that are dropped according to the new tuple
    6471                 :             :          * descriptor can be set to NULL. We precompute the list of dropped
    6472                 :             :          * attributes to avoid needing to do so in the per-tuple loop.
    6473                 :             :          */
    6474         [ +  + ]:       12745 :         for (i = 0; i < newTupDesc->natts; i++)
    6475                 :             :         {
    6476         [ +  + ]:        9143 :             if (TupleDescAttr(newTupDesc, i)->attisdropped)
    6477                 :         616 :                 dropped_attrs = lappend_int(dropped_attrs, i);
    6478                 :             :         }
    6479                 :             : 
    6480                 :             :         /*
    6481                 :             :          * Scan through the rows, generating a new row if needed and then
    6482                 :             :          * checking all the constraints.
    6483                 :             :          */
    6484                 :        3602 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
    6485                 :        3602 :         scan = table_beginscan(oldrel, snapshot, 0, NULL,
    6486                 :             :                                SO_NONE);
    6487                 :             : 
    6488                 :             :         /*
    6489                 :             :          * Switch to per-tuple memory context and reset it for each tuple
    6490                 :             :          * produced, so we don't leak memory.
    6491                 :             :          */
    6492         [ +  - ]:        3602 :         oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
    6493                 :             : 
    6494         [ +  + ]:      491295 :         while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
    6495                 :             :         {
    6496                 :             :             TupleTableSlot *insertslot;
    6497                 :             : 
    6498         [ +  + ]:      484376 :             if (tab->rewrite > 0)
    6499                 :             :             {
    6500                 :             :                 /* Extract data from old tuple */
    6501                 :       67479 :                 slot_getallattrs(oldslot);
    6502                 :       67479 :                 ExecClearTuple(newslot);
    6503                 :             : 
    6504                 :             :                 /* copy attributes */
    6505                 :       67479 :                 memcpy(newslot->tts_values, oldslot->tts_values,
    6506                 :       67479 :                        sizeof(Datum) * oldslot->tts_nvalid);
    6507                 :       67479 :                 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
    6508                 :       67479 :                        sizeof(bool) * oldslot->tts_nvalid);
    6509                 :             : 
    6510                 :             :                 /* Set dropped attributes to null in new tuple */
    6511   [ +  +  +  +  :       67564 :                 foreach(lc, dropped_attrs)
                   +  + ]
    6512                 :          85 :                     newslot->tts_isnull[lfirst_int(lc)] = true;
    6513                 :             : 
    6514                 :             :                 /*
    6515                 :             :                  * Constraints and GENERATED expressions might reference the
    6516                 :             :                  * tableoid column, so fill tts_tableOid with the desired
    6517                 :             :                  * value.  (We must do this each time, because it gets
    6518                 :             :                  * overwritten with newrel's OID during storing.)
    6519                 :             :                  */
    6520                 :       67479 :                 newslot->tts_tableOid = RelationGetRelid(oldrel);
    6521                 :             : 
    6522                 :             :                 /*
    6523                 :             :                  * Process supplied expressions to replace selected columns.
    6524                 :             :                  *
    6525                 :             :                  * First, evaluate expressions whose inputs come from the old
    6526                 :             :                  * tuple.
    6527                 :             :                  */
    6528                 :       67479 :                 econtext->ecxt_scantuple = oldslot;
    6529                 :             : 
    6530   [ +  +  +  +  :      137950 :                 foreach(l, tab->newvals)
                   +  + ]
    6531                 :             :                 {
    6532                 :       70479 :                     NewColumnValue *ex = lfirst(l);
    6533                 :             : 
    6534         [ +  + ]:       70479 :                     if (ex->is_generated)
    6535                 :         284 :                         continue;
    6536                 :             : 
    6537                 :       70195 :                     newslot->tts_values[ex->attnum - 1]
    6538                 :       70187 :                         = ExecEvalExpr(ex->exprstate,
    6539                 :             :                                        econtext,
    6540                 :       70195 :                                        &newslot->tts_isnull[ex->attnum - 1]);
    6541                 :             :                 }
    6542                 :             : 
    6543                 :       67471 :                 ExecStoreVirtualTuple(newslot);
    6544                 :             : 
    6545                 :             :                 /*
    6546                 :             :                  * Now, evaluate any expressions whose inputs come from the
    6547                 :             :                  * new tuple.  We assume these columns won't reference each
    6548                 :             :                  * other, so that there's no ordering dependency.
    6549                 :             :                  */
    6550                 :       67471 :                 econtext->ecxt_scantuple = newslot;
    6551                 :             : 
    6552   [ +  +  +  +  :      137942 :                 foreach(l, tab->newvals)
                   +  + ]
    6553                 :             :                 {
    6554                 :       70471 :                     NewColumnValue *ex = lfirst(l);
    6555                 :             : 
    6556         [ +  + ]:       70471 :                     if (!ex->is_generated)
    6557                 :       70187 :                         continue;
    6558                 :             : 
    6559                 :         284 :                     newslot->tts_values[ex->attnum - 1]
    6560                 :         284 :                         = ExecEvalExpr(ex->exprstate,
    6561                 :             :                                        econtext,
    6562                 :         284 :                                        &newslot->tts_isnull[ex->attnum - 1]);
    6563                 :             :                 }
    6564                 :             : 
    6565                 :       67471 :                 insertslot = newslot;
    6566                 :             :             }
    6567                 :             :             else
    6568                 :             :             {
    6569                 :             :                 /*
    6570                 :             :                  * If there's no rewrite, old and new table are guaranteed to
    6571                 :             :                  * have the same AM, so we can just use the old slot to verify
    6572                 :             :                  * new constraints etc.
    6573                 :             :                  */
    6574                 :      416897 :                 insertslot = oldslot;
    6575                 :             :             }
    6576                 :             : 
    6577                 :             :             /* Now check any constraints on the possibly-changed tuple */
    6578                 :      484368 :             econtext->ecxt_scantuple = insertslot;
    6579                 :             : 
    6580   [ +  +  +  +  :     2579602 :             foreach_int(attn, notnull_attrs)
                   +  + ]
    6581                 :             :             {
    6582         [ +  + ]:     1611042 :                 if (slot_attisnull(insertslot, attn))
    6583                 :             :                 {
    6584                 :          88 :                     Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
    6585                 :             : 
    6586         [ +  - ]:          88 :                     ereport(ERROR,
    6587                 :             :                             (errcode(ERRCODE_NOT_NULL_VIOLATION),
    6588                 :             :                              errmsg("column \"%s\" of relation \"%s\" contains null values",
    6589                 :             :                                     NameStr(attr->attname),
    6590                 :             :                                     RelationGetRelationName(oldrel)),
    6591                 :             :                              errtablecol(oldrel, attn)));
    6592                 :             :                 }
    6593                 :             :             }
    6594                 :             : 
    6595         [ +  + ]:      484280 :             if (notnull_virtual_attrs != NIL)
    6596                 :             :             {
    6597                 :             :                 AttrNumber  attnum;
    6598                 :             : 
    6599                 :          60 :                 attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
    6600                 :             :                                                   estate,
    6601                 :             :                                                   notnull_virtual_attrs);
    6602         [ +  + ]:          60 :                 if (attnum != InvalidAttrNumber)
    6603                 :             :                 {
    6604                 :          20 :                     Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
    6605                 :             : 
    6606         [ +  - ]:          20 :                     ereport(ERROR,
    6607                 :             :                             errcode(ERRCODE_NOT_NULL_VIOLATION),
    6608                 :             :                             errmsg("column \"%s\" of relation \"%s\" contains null values",
    6609                 :             :                                    NameStr(attr->attname),
    6610                 :             :                                    RelationGetRelationName(oldrel)),
    6611                 :             :                             errtablecol(oldrel, attnum));
    6612                 :             :                 }
    6613                 :             :             }
    6614                 :             : 
    6615   [ +  +  +  +  :      489601 :             foreach(l, tab->constraints)
                   +  + ]
    6616                 :             :             {
    6617                 :        5461 :                 NewConstraint *con = lfirst(l);
    6618                 :             : 
    6619      [ +  +  - ]:        5461 :                 switch (con->contype)
    6620                 :             :                 {
    6621                 :        5395 :                     case CONSTR_CHECK:
    6622         [ +  + ]:        5395 :                         if (!ExecCheck(con->qualstate, econtext))
    6623         [ +  - ]:         120 :                             ereport(ERROR,
    6624                 :             :                                     (errcode(ERRCODE_CHECK_VIOLATION),
    6625                 :             :                                      errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
    6626                 :             :                                             con->name,
    6627                 :             :                                             RelationGetRelationName(oldrel)),
    6628                 :             :                                      errtableconstraint(oldrel, con->name)));
    6629                 :        5275 :                         break;
    6630                 :          66 :                     case CONSTR_NOTNULL:
    6631                 :             :                     case CONSTR_FOREIGN:
    6632                 :             :                         /* Nothing to do here */
    6633                 :          66 :                         break;
    6634                 :           0 :                     default:
    6635         [ #  # ]:           0 :                         elog(ERROR, "unrecognized constraint type: %d",
    6636                 :             :                              (int) con->contype);
    6637                 :             :                 }
    6638                 :             :             }
    6639                 :             : 
    6640   [ +  +  +  + ]:      484140 :             if (partqualstate && !ExecCheck(partqualstate, econtext))
    6641                 :             :             {
    6642         [ +  + ]:          49 :                 if (tab->validate_default)
    6643         [ +  - ]:          17 :                     ereport(ERROR,
    6644                 :             :                             (errcode(ERRCODE_CHECK_VIOLATION),
    6645                 :             :                              errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
    6646                 :             :                                     RelationGetRelationName(oldrel)),
    6647                 :             :                              errtable(oldrel)));
    6648                 :             :                 else
    6649         [ +  - ]:          32 :                     ereport(ERROR,
    6650                 :             :                             (errcode(ERRCODE_CHECK_VIOLATION),
    6651                 :             :                              errmsg("partition constraint of relation \"%s\" is violated by some row",
    6652                 :             :                                     RelationGetRelationName(oldrel)),
    6653                 :             :                              errtable(oldrel)));
    6654                 :             :             }
    6655                 :             : 
    6656                 :             :             /* Write the tuple out to the new relation */
    6657         [ +  + ]:      484091 :             if (newrel)
    6658                 :       67427 :                 table_tuple_insert(newrel, insertslot, mycid,
    6659                 :             :                                    ti_options, bistate);
    6660                 :             : 
    6661                 :      484091 :             ResetExprContext(econtext);
    6662                 :             : 
    6663         [ -  + ]:      484091 :             CHECK_FOR_INTERRUPTS();
    6664                 :             :         }
    6665                 :             : 
    6666                 :        3317 :         MemoryContextSwitchTo(oldCxt);
    6667                 :        3317 :         table_endscan(scan);
    6668                 :        3317 :         UnregisterSnapshot(snapshot);
    6669                 :             : 
    6670                 :        3317 :         ExecDropSingleTupleTableSlot(oldslot);
    6671         [ +  + ]:        3317 :         if (newslot)
    6672                 :         736 :             ExecDropSingleTupleTableSlot(newslot);
    6673                 :             :     }
    6674                 :             : 
    6675                 :        4023 :     FreeExecutorState(estate);
    6676                 :             : 
    6677                 :        4023 :     table_close(oldrel, NoLock);
    6678         [ +  + ]:        4023 :     if (newrel)
    6679                 :             :     {
    6680                 :         736 :         FreeBulkInsertState(bistate);
    6681                 :             : 
    6682                 :         736 :         table_finish_bulk_insert(newrel, ti_options);
    6683                 :             : 
    6684                 :         736 :         table_close(newrel, NoLock);
    6685                 :             :     }
    6686                 :        4023 : }
    6687                 :             : 
    6688                 :             : /*
    6689                 :             :  * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
    6690                 :             :  */
    6691                 :             : static AlteredTableInfo *
    6692                 :       29639 : ATGetQueueEntry(List **wqueue, Relation rel)
    6693                 :             : {
    6694                 :       29639 :     Oid         relid = RelationGetRelid(rel);
    6695                 :             :     AlteredTableInfo *tab;
    6696                 :             :     ListCell   *ltab;
    6697                 :             : 
    6698   [ +  +  +  +  :       38264 :     foreach(ltab, *wqueue)
                   +  + ]
    6699                 :             :     {
    6700                 :       12627 :         tab = (AlteredTableInfo *) lfirst(ltab);
    6701         [ +  + ]:       12627 :         if (tab->relid == relid)
    6702                 :        4002 :             return tab;
    6703                 :             :     }
    6704                 :             : 
    6705                 :             :     /*
    6706                 :             :      * Not there, so add it.  Note that we make a copy of the relation's
    6707                 :             :      * existing descriptor before anything interesting can happen to it.
    6708                 :             :      */
    6709                 :       25637 :     tab = palloc0_object(AlteredTableInfo);
    6710                 :       25637 :     tab->relid = relid;
    6711                 :       25637 :     tab->rel = NULL;         /* set later */
    6712                 :       25637 :     tab->relkind = rel->rd_rel->relkind;
    6713                 :       25637 :     tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
    6714                 :       25637 :     tab->newAccessMethod = InvalidOid;
    6715                 :       25637 :     tab->chgAccessMethod = false;
    6716                 :       25637 :     tab->newTableSpace = InvalidOid;
    6717                 :       25637 :     tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
    6718                 :       25637 :     tab->chgPersistence = false;
    6719                 :             : 
    6720                 :       25637 :     *wqueue = lappend(*wqueue, tab);
    6721                 :             : 
    6722                 :       25637 :     return tab;
    6723                 :             : }
    6724                 :             : 
    6725                 :             : static const char *
    6726                 :          73 : alter_table_type_to_string(AlterTableType cmdtype)
    6727                 :             : {
    6728   [ -  -  +  +  :          73 :     switch (cmdtype)
          -  -  -  +  -  
          -  -  +  -  -  
          +  -  -  -  -  
          -  -  +  +  -  
          +  +  -  -  +  
          -  -  -  -  -  
          -  -  -  -  -  
          -  -  -  -  +  
          +  -  -  -  -  
          -  -  -  -  +  
          +  +  -  +  -  
             -  -  -  - ]
    6729                 :             :     {
    6730                 :           0 :         case AT_AddColumn:
    6731                 :             :         case AT_AddColumnToView:
    6732                 :           0 :             return "ADD COLUMN";
    6733                 :           0 :         case AT_ColumnDefault:
    6734                 :             :         case AT_CookedColumnDefault:
    6735                 :           0 :             return "ALTER COLUMN ... SET DEFAULT";
    6736                 :           4 :         case AT_DropNotNull:
    6737                 :           4 :             return "ALTER COLUMN ... DROP NOT NULL";
    6738                 :           4 :         case AT_SetNotNull:
    6739                 :           4 :             return "ALTER COLUMN ... SET NOT NULL";
    6740                 :           0 :         case AT_SetExpression:
    6741                 :           0 :             return "ALTER COLUMN ... SET EXPRESSION";
    6742                 :           0 :         case AT_DropExpression:
    6743                 :           0 :             return "ALTER COLUMN ... DROP EXPRESSION";
    6744                 :           0 :         case AT_SetStatistics:
    6745                 :           0 :             return "ALTER COLUMN ... SET STATISTICS";
    6746                 :           8 :         case AT_SetOptions:
    6747                 :           8 :             return "ALTER COLUMN ... SET";
    6748                 :           0 :         case AT_ResetOptions:
    6749                 :           0 :             return "ALTER COLUMN ... RESET";
    6750                 :           0 :         case AT_SetStorage:
    6751                 :           0 :             return "ALTER COLUMN ... SET STORAGE";
    6752                 :           0 :         case AT_SetCompression:
    6753                 :           0 :             return "ALTER COLUMN ... SET COMPRESSION";
    6754                 :           4 :         case AT_DropColumn:
    6755                 :           4 :             return "DROP COLUMN";
    6756                 :           0 :         case AT_AddIndex:
    6757                 :             :         case AT_ReAddIndex:
    6758                 :           0 :             return NULL;        /* not real grammar */
    6759                 :           0 :         case AT_AddConstraint:
    6760                 :             :         case AT_ReAddConstraint:
    6761                 :             :         case AT_ReAddDomainConstraint:
    6762                 :             :         case AT_AddIndexConstraint:
    6763                 :           0 :             return "ADD CONSTRAINT";
    6764                 :           4 :         case AT_AlterConstraint:
    6765                 :           4 :             return "ALTER CONSTRAINT";
    6766                 :           0 :         case AT_ValidateConstraint:
    6767                 :           0 :             return "VALIDATE CONSTRAINT";
    6768                 :           0 :         case AT_DropConstraint:
    6769                 :           0 :             return "DROP CONSTRAINT";
    6770                 :           0 :         case AT_ReAddComment:
    6771                 :           0 :             return NULL;        /* not real grammar */
    6772                 :           0 :         case AT_AlterColumnType:
    6773                 :           0 :             return "ALTER COLUMN ... SET DATA TYPE";
    6774                 :           0 :         case AT_AlterColumnGenericOptions:
    6775                 :           0 :             return "ALTER COLUMN ... OPTIONS";
    6776                 :           0 :         case AT_ChangeOwner:
    6777                 :           0 :             return "OWNER TO";
    6778                 :           4 :         case AT_ClusterOn:
    6779                 :           4 :             return "CLUSTER ON";
    6780                 :           4 :         case AT_DropCluster:
    6781                 :           4 :             return "SET WITHOUT CLUSTER";
    6782                 :           0 :         case AT_SetAccessMethod:
    6783                 :           0 :             return "SET ACCESS METHOD";
    6784                 :           4 :         case AT_SetLogged:
    6785                 :           4 :             return "SET LOGGED";
    6786                 :           4 :         case AT_SetUnLogged:
    6787                 :           4 :             return "SET UNLOGGED";
    6788                 :           0 :         case AT_DropOids:
    6789                 :           0 :             return "SET WITHOUT OIDS";
    6790                 :           0 :         case AT_SetTableSpace:
    6791                 :           0 :             return "SET TABLESPACE";
    6792                 :           1 :         case AT_SetRelOptions:
    6793                 :           1 :             return "SET";
    6794                 :           0 :         case AT_ResetRelOptions:
    6795                 :           0 :             return "RESET";
    6796                 :           0 :         case AT_ReplaceRelOptions:
    6797                 :           0 :             return NULL;        /* not real grammar */
    6798                 :           0 :         case AT_EnableTrig:
    6799                 :           0 :             return "ENABLE TRIGGER";
    6800                 :           0 :         case AT_EnableAlwaysTrig:
    6801                 :           0 :             return "ENABLE ALWAYS TRIGGER";
    6802                 :           0 :         case AT_EnableReplicaTrig:
    6803                 :           0 :             return "ENABLE REPLICA TRIGGER";
    6804                 :           0 :         case AT_DisableTrig:
    6805                 :           0 :             return "DISABLE TRIGGER";
    6806                 :           0 :         case AT_EnableTrigAll:
    6807                 :           0 :             return "ENABLE TRIGGER ALL";
    6808                 :           0 :         case AT_DisableTrigAll:
    6809                 :           0 :             return "DISABLE TRIGGER ALL";
    6810                 :           0 :         case AT_EnableTrigUser:
    6811                 :           0 :             return "ENABLE TRIGGER USER";
    6812                 :           0 :         case AT_DisableTrigUser:
    6813                 :           0 :             return "DISABLE TRIGGER USER";
    6814                 :           0 :         case AT_EnableRule:
    6815                 :           0 :             return "ENABLE RULE";
    6816                 :           0 :         case AT_EnableAlwaysRule:
    6817                 :           0 :             return "ENABLE ALWAYS RULE";
    6818                 :           0 :         case AT_EnableReplicaRule:
    6819                 :           0 :             return "ENABLE REPLICA RULE";
    6820                 :           0 :         case AT_DisableRule:
    6821                 :           0 :             return "DISABLE RULE";
    6822                 :           4 :         case AT_AddInherit:
    6823                 :           4 :             return "INHERIT";
    6824                 :           4 :         case AT_DropInherit:
    6825                 :           4 :             return "NO INHERIT";
    6826                 :           0 :         case AT_AddOf:
    6827                 :           0 :             return "OF";
    6828                 :           0 :         case AT_DropOf:
    6829                 :           0 :             return "NOT OF";
    6830                 :           0 :         case AT_ReplicaIdentity:
    6831                 :           0 :             return "REPLICA IDENTITY";
    6832                 :           0 :         case AT_EnableRowSecurity:
    6833                 :           0 :             return "ENABLE ROW SECURITY";
    6834                 :           0 :         case AT_DisableRowSecurity:
    6835                 :           0 :             return "DISABLE ROW SECURITY";
    6836                 :           0 :         case AT_ForceRowSecurity:
    6837                 :           0 :             return "FORCE ROW SECURITY";
    6838                 :           0 :         case AT_NoForceRowSecurity:
    6839                 :           0 :             return "NO FORCE ROW SECURITY";
    6840                 :           0 :         case AT_GenericOptions:
    6841                 :           0 :             return "OPTIONS";
    6842                 :           4 :         case AT_AttachPartition:
    6843                 :           4 :             return "ATTACH PARTITION";
    6844                 :          12 :         case AT_DetachPartition:
    6845                 :          12 :             return "DETACH PARTITION";
    6846                 :           4 :         case AT_DetachPartitionFinalize:
    6847                 :           4 :             return "DETACH PARTITION ... FINALIZE";
    6848                 :           0 :         case AT_MergePartitions:
    6849                 :           0 :             return "MERGE PARTITIONS";
    6850                 :           4 :         case AT_SplitPartition:
    6851                 :           4 :             return "SPLIT PARTITION";
    6852                 :           0 :         case AT_AddIdentity:
    6853                 :           0 :             return "ALTER COLUMN ... ADD IDENTITY";
    6854                 :           0 :         case AT_SetIdentity:
    6855                 :           0 :             return "ALTER COLUMN ... SET";
    6856                 :           0 :         case AT_DropIdentity:
    6857                 :           0 :             return "ALTER COLUMN ... DROP IDENTITY";
    6858                 :           0 :         case AT_ReAddStatistics:
    6859                 :           0 :             return NULL;        /* not real grammar */
    6860                 :             :     }
    6861                 :             : 
    6862                 :           0 :     return NULL;
    6863                 :             : }
    6864                 :             : 
    6865                 :             : /*
    6866                 :             :  * ATSimplePermissions
    6867                 :             :  *
    6868                 :             :  * - Ensure that it is a relation (or possibly a view)
    6869                 :             :  * - Ensure this user is the owner
    6870                 :             :  * - Ensure that it is not a system table
    6871                 :             :  */
    6872                 :             : static void
    6873                 :       26177 : ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
    6874                 :             : {
    6875                 :             :     int         actual_target;
    6876                 :             : 
    6877   [ +  +  +  +  :       26177 :     switch (rel->rd_rel->relkind)
          +  +  +  +  +  
                      + ]
    6878                 :             :     {
    6879                 :       20261 :         case RELKIND_RELATION:
    6880                 :       20261 :             actual_target = ATT_TABLE;
    6881                 :       20261 :             break;
    6882                 :        4394 :         case RELKIND_PARTITIONED_TABLE:
    6883                 :        4394 :             actual_target = ATT_PARTITIONED_TABLE;
    6884                 :        4394 :             break;
    6885                 :         280 :         case RELKIND_VIEW:
    6886                 :         280 :             actual_target = ATT_VIEW;
    6887                 :         280 :             break;
    6888                 :          29 :         case RELKIND_MATVIEW:
    6889                 :          29 :             actual_target = ATT_MATVIEW;
    6890                 :          29 :             break;
    6891                 :         140 :         case RELKIND_INDEX:
    6892                 :         140 :             actual_target = ATT_INDEX;
    6893                 :         140 :             break;
    6894                 :         312 :         case RELKIND_PARTITIONED_INDEX:
    6895                 :         312 :             actual_target = ATT_PARTITIONED_INDEX;
    6896                 :         312 :             break;
    6897                 :         149 :         case RELKIND_COMPOSITE_TYPE:
    6898                 :         149 :             actual_target = ATT_COMPOSITE_TYPE;
    6899                 :         149 :             break;
    6900                 :         595 :         case RELKIND_FOREIGN_TABLE:
    6901                 :         595 :             actual_target = ATT_FOREIGN_TABLE;
    6902                 :         595 :             break;
    6903                 :          16 :         case RELKIND_SEQUENCE:
    6904                 :          16 :             actual_target = ATT_SEQUENCE;
    6905                 :          16 :             break;
    6906                 :           1 :         default:
    6907                 :           1 :             actual_target = 0;
    6908                 :           1 :             break;
    6909                 :             :     }
    6910                 :             : 
    6911                 :             :     /* Wrong target type? */
    6912         [ +  + ]:       26177 :     if ((actual_target & allowed_targets) == 0)
    6913                 :             :     {
    6914                 :          73 :         const char *action_str = alter_table_type_to_string(cmdtype);
    6915                 :             : 
    6916         [ +  - ]:          73 :         if (action_str)
    6917         [ +  - ]:          73 :             ereport(ERROR,
    6918                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6919                 :             :             /* translator: %s is a group of some SQL keywords */
    6920                 :             :                      errmsg("ALTER action %s cannot be performed on relation \"%s\"",
    6921                 :             :                             action_str, RelationGetRelationName(rel)),
    6922                 :             :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
    6923                 :             :         else
    6924                 :             :             /* internal error? */
    6925         [ #  # ]:           0 :             elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
    6926                 :             :                  RelationGetRelationName(rel));
    6927                 :             :     }
    6928                 :             : 
    6929                 :             :     /* Permissions checks */
    6930         [ +  + ]:       26104 :     if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
    6931                 :           8 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
    6932                 :           8 :                        RelationGetRelationName(rel));
    6933                 :             : 
    6934                 :             :     /*
    6935                 :             :      * Conflict log tables are used internally for logical replication
    6936                 :             :      * conflict logging and should not be altered directly, as it could
    6937                 :             :      * disrupt conflict logging. Direct ALTER commands are already rejected
    6938                 :             :      * during relation lookup in RangeVarCallbackForAlterRelation(), and
    6939                 :             :      * AlterTableMoveAll() skips these tables, so a conflict log table does
    6940                 :             :      * not normally reach here; this check guards any internal caller that
    6941                 :             :      * arrives via AlterTableInternal().
    6942                 :             :      */
    6943         [ -  + ]:       26096 :     if (IsConflictLogTableClass(rel->rd_rel))
    6944         [ #  # ]:           0 :         ereport(ERROR,
    6945                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    6946                 :             :                  errmsg("cannot alter conflict log table \"%s\"",
    6947                 :             :                         RelationGetRelationName(rel)),
    6948                 :             :                  errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
    6949                 :             : 
    6950   [ +  +  -  + ]:       26096 :     if (!allowSystemTableMods && IsSystemRelation(rel))
    6951         [ #  # ]:           0 :         ereport(ERROR,
    6952                 :             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
    6953                 :             :                  errmsg("permission denied: \"%s\" is a system catalog",
    6954                 :             :                         RelationGetRelationName(rel))));
    6955                 :       26096 : }
    6956                 :             : 
    6957                 :             : /*
    6958                 :             :  * ATSimpleRecursion
    6959                 :             :  *
    6960                 :             :  * Simple table recursion sufficient for most ALTER TABLE operations.
    6961                 :             :  * All direct and indirect children are processed in an unspecified order.
    6962                 :             :  * Note that if a child inherits from the original table via multiple
    6963                 :             :  * inheritance paths, it will be visited just once.
    6964                 :             :  */
    6965                 :             : static void
    6966                 :         994 : ATSimpleRecursion(List **wqueue, Relation rel,
    6967                 :             :                   AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
    6968                 :             :                   AlterTableUtilityContext *context)
    6969                 :             : {
    6970                 :             :     /*
    6971                 :             :      * Propagate to children, if desired and if there are (or might be) any
    6972                 :             :      * children.
    6973                 :             :      */
    6974   [ +  +  +  + ]:         994 :     if (recurse && rel->rd_rel->relhassubclass)
    6975                 :             :     {
    6976                 :          71 :         Oid         relid = RelationGetRelid(rel);
    6977                 :             :         ListCell   *child;
    6978                 :             :         List       *children;
    6979                 :             : 
    6980                 :          71 :         children = find_all_inheritors(relid, lockmode, NULL);
    6981                 :             : 
    6982                 :             :         /*
    6983                 :             :          * find_all_inheritors does the recursive search of the inheritance
    6984                 :             :          * hierarchy, so all we have to do is process all of the relids in the
    6985                 :             :          * list that it returns.
    6986                 :             :          */
    6987   [ +  -  +  +  :         300 :         foreach(child, children)
                   +  + ]
    6988                 :             :         {
    6989                 :         229 :             Oid         childrelid = lfirst_oid(child);
    6990                 :             :             Relation    childrel;
    6991                 :             : 
    6992         [ +  + ]:         229 :             if (childrelid == relid)
    6993                 :          71 :                 continue;
    6994                 :             :             /* find_all_inheritors already got lock */
    6995                 :         158 :             childrel = relation_open(childrelid, NoLock);
    6996                 :         158 :             CheckAlterTableIsSafe(childrel);
    6997                 :         158 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
    6998                 :         158 :             relation_close(childrel, NoLock);
    6999                 :             :         }
    7000                 :             :     }
    7001                 :         994 : }
    7002                 :             : 
    7003                 :             : /*
    7004                 :             :  * Obtain list of partitions of the given table, locking them all at the given
    7005                 :             :  * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
    7006                 :             :  *
    7007                 :             :  * This function is a no-op if the given relation is not a partitioned table;
    7008                 :             :  * in particular, nothing is done if it's a legacy inheritance parent.
    7009                 :             :  */
    7010                 :             : static void
    7011                 :         582 : ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
    7012                 :             : {
    7013         [ +  + ]:         582 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    7014                 :             :     {
    7015                 :             :         List       *inh;
    7016                 :             :         ListCell   *cell;
    7017                 :             : 
    7018                 :         121 :         inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
    7019                 :             :         /* first element is the parent rel; must ignore it */
    7020   [ +  -  +  +  :         398 :         for_each_from(cell, inh, 1)
                   +  + ]
    7021                 :             :         {
    7022                 :             :             Relation    childrel;
    7023                 :             : 
    7024                 :             :             /* find_all_inheritors already got lock */
    7025                 :         281 :             childrel = table_open(lfirst_oid(cell), NoLock);
    7026                 :         281 :             CheckAlterTableIsSafe(childrel);
    7027                 :         277 :             table_close(childrel, NoLock);
    7028                 :             :         }
    7029                 :         117 :         list_free(inh);
    7030                 :             :     }
    7031                 :         578 : }
    7032                 :             : 
    7033                 :             : /*
    7034                 :             :  * ATTypedTableRecursion
    7035                 :             :  *
    7036                 :             :  * Propagate ALTER TYPE operations to the typed tables of that type.
    7037                 :             :  * Also check the RESTRICT/CASCADE behavior.  Given CASCADE, also permit
    7038                 :             :  * recursion to inheritance children of the typed tables.
    7039                 :             :  */
    7040                 :             : static void
    7041                 :         133 : ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
    7042                 :             :                       LOCKMODE lockmode, AlterTableUtilityContext *context)
    7043                 :             : {
    7044                 :             :     ListCell   *child;
    7045                 :             :     List       *children;
    7046                 :             : 
    7047                 :             :     Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    7048                 :             : 
    7049                 :         133 :     children = find_typed_table_dependencies(rel->rd_rel->reltype,
    7050                 :         133 :                                              RelationGetRelationName(rel),
    7051                 :             :                                              cmd->behavior);
    7052                 :             : 
    7053   [ +  +  +  +  :         141 :     foreach(child, children)
                   +  + ]
    7054                 :             :     {
    7055                 :          20 :         Oid         childrelid = lfirst_oid(child);
    7056                 :             :         Relation    childrel;
    7057                 :             : 
    7058                 :          20 :         childrel = relation_open(childrelid, lockmode);
    7059                 :          20 :         CheckAlterTableIsSafe(childrel);
    7060                 :          20 :         ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
    7061                 :          20 :         relation_close(childrel, NoLock);
    7062                 :             :     }
    7063                 :         121 : }
    7064                 :             : 
    7065                 :             : 
    7066                 :             : /*
    7067                 :             :  * find_composite_type_dependencies
    7068                 :             :  *
    7069                 :             :  * Check to see if the type "typeOid" is being used as a column in some table
    7070                 :             :  * (possibly nested several levels deep in composite types, arrays, etc!).
    7071                 :             :  * Eventually, we'd like to propagate the check or rewrite operation
    7072                 :             :  * into such tables, but for now, just error out if we find any.
    7073                 :             :  *
    7074                 :             :  * Caller should provide either the associated relation of a rowtype,
    7075                 :             :  * or a type name (not both) for use in the error message, if any.
    7076                 :             :  *
    7077                 :             :  * Note that "typeOid" is not necessarily a composite type; it could also be
    7078                 :             :  * another container type such as an array or range, or a domain over one of
    7079                 :             :  * these things.  The name of this function is therefore somewhat historical,
    7080                 :             :  * but it's not worth changing.
    7081                 :             :  *
    7082                 :             :  * We assume that functions and views depending on the type are not reasons
    7083                 :             :  * to reject the ALTER.  (How safe is this really?)
    7084                 :             :  */
    7085                 :             : void
    7086                 :        3380 : find_composite_type_dependencies(Oid typeOid, Relation origRelation,
    7087                 :             :                                  const char *origTypeName)
    7088                 :             : {
    7089                 :             :     Relation    depRel;
    7090                 :             :     ScanKeyData key[2];
    7091                 :             :     SysScanDesc depScan;
    7092                 :             :     HeapTuple   depTup;
    7093                 :             : 
    7094                 :             :     /* since this function recurses, it could be driven to stack overflow */
    7095                 :        3380 :     check_stack_depth();
    7096                 :             : 
    7097                 :             :     /*
    7098                 :             :      * We scan pg_depend to find those things that depend on the given type.
    7099                 :             :      * (We assume we can ignore refobjsubid for a type.)
    7100                 :             :      */
    7101                 :        3380 :     depRel = table_open(DependRelationId, AccessShareLock);
    7102                 :             : 
    7103                 :        3380 :     ScanKeyInit(&key[0],
    7104                 :             :                 Anum_pg_depend_refclassid,
    7105                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
    7106                 :             :                 ObjectIdGetDatum(TypeRelationId));
    7107                 :        3380 :     ScanKeyInit(&key[1],
    7108                 :             :                 Anum_pg_depend_refobjid,
    7109                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
    7110                 :             :                 ObjectIdGetDatum(typeOid));
    7111                 :             : 
    7112                 :        3380 :     depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
    7113                 :             :                                  NULL, 2, key);
    7114                 :             : 
    7115         [ +  + ]:        5191 :     while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
    7116                 :             :     {
    7117                 :        1915 :         Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
    7118                 :             :         Relation    rel;
    7119                 :             :         TupleDesc   tupleDesc;
    7120                 :             :         Form_pg_attribute att;
    7121                 :             : 
    7122                 :             :         /* Check for directly dependent types */
    7123         [ +  + ]:        1915 :         if (pg_depend->classid == TypeRelationId)
    7124                 :             :         {
    7125                 :             :             /*
    7126                 :             :              * This must be an array, domain, or range containing the given
    7127                 :             :              * type, so recursively check for uses of this type.  Note that
    7128                 :             :              * any error message will mention the original type not the
    7129                 :             :              * container; this is intentional.
    7130                 :             :              */
    7131                 :        1637 :             find_composite_type_dependencies(pg_depend->objid,
    7132                 :             :                                              origRelation, origTypeName);
    7133                 :        1621 :             continue;
    7134                 :             :         }
    7135                 :             : 
    7136                 :             :         /* Else, ignore dependees that aren't relations */
    7137         [ +  + ]:         278 :         if (pg_depend->classid != RelationRelationId)
    7138                 :          85 :             continue;
    7139                 :             : 
    7140                 :         193 :         rel = relation_open(pg_depend->objid, AccessShareLock);
    7141                 :         193 :         tupleDesc = RelationGetDescr(rel);
    7142                 :             : 
    7143                 :             :         /*
    7144                 :             :          * If objsubid identifies a specific column, refer to that in error
    7145                 :             :          * messages.  Otherwise, search to see if there's a user column of the
    7146                 :             :          * type.  (We assume system columns are never of interesting types.)
    7147                 :             :          * The search is needed because an index containing an expression
    7148                 :             :          * column of the target type will just be recorded as a whole-relation
    7149                 :             :          * dependency.  If we do not find a column of the type, the dependency
    7150                 :             :          * must indicate that the type is transiently referenced in an index
    7151                 :             :          * expression but not stored on disk, which we assume is OK, just as
    7152                 :             :          * we do for references in views.  (It could also be that the target
    7153                 :             :          * type is embedded in some container type that is stored in an index
    7154                 :             :          * column, but the previous recursion should catch such cases.)
    7155                 :             :          */
    7156   [ +  +  +  - ]:         193 :         if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
    7157                 :          84 :             att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
    7158                 :             :         else
    7159                 :             :         {
    7160                 :         109 :             att = NULL;
    7161         [ +  + ]:         286 :             for (int attno = 1; attno <= tupleDesc->natts; attno++)
    7162                 :             :             {
    7163                 :         181 :                 att = TupleDescAttr(tupleDesc, attno - 1);
    7164   [ +  +  +  - ]:         181 :                 if (att->atttypid == typeOid && !att->attisdropped)
    7165                 :           4 :                     break;
    7166                 :         177 :                 att = NULL;
    7167                 :             :             }
    7168         [ +  + ]:         109 :             if (att == NULL)
    7169                 :             :             {
    7170                 :             :                 /* No such column, so assume OK */
    7171                 :         105 :                 relation_close(rel, AccessShareLock);
    7172                 :         105 :                 continue;
    7173                 :             :             }
    7174                 :             :         }
    7175                 :             : 
    7176                 :             :         /*
    7177                 :             :          * We definitely should reject if the relation has storage.  If it's
    7178                 :             :          * partitioned, then perhaps we don't have to reject: if there are
    7179                 :             :          * partitions then we'll fail when we find one, else there is no
    7180                 :             :          * stored data to worry about.  However, it's possible that the type
    7181                 :             :          * change would affect conclusions about whether the type is sortable
    7182                 :             :          * or hashable and thus (if it's a partitioning column) break the
    7183                 :             :          * partitioning rule.  For now, reject for partitioned rels too.
    7184                 :             :          */
    7185   [ +  +  -  +  :          88 :         if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
          -  -  -  -  -  
                      - ]
    7186   [ #  #  #  # ]:           0 :             RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
    7187                 :             :         {
    7188         [ +  + ]:          88 :             if (origTypeName)
    7189         [ +  - ]:          20 :                 ereport(ERROR,
    7190                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7191                 :             :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    7192                 :             :                                 origTypeName,
    7193                 :             :                                 RelationGetRelationName(rel),
    7194                 :             :                                 NameStr(att->attname))));
    7195         [ +  + ]:          68 :             else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    7196         [ +  - ]:          12 :                 ereport(ERROR,
    7197                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7198                 :             :                          errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
    7199                 :             :                                 RelationGetRelationName(origRelation),
    7200                 :             :                                 RelationGetRelationName(rel),
    7201                 :             :                                 NameStr(att->attname))));
    7202         [ +  + ]:          56 :             else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
    7203         [ +  - ]:           4 :                 ereport(ERROR,
    7204                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7205                 :             :                          errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
    7206                 :             :                                 RelationGetRelationName(origRelation),
    7207                 :             :                                 RelationGetRelationName(rel),
    7208                 :             :                                 NameStr(att->attname))));
    7209                 :             :             else
    7210         [ +  - ]:          52 :                 ereport(ERROR,
    7211                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7212                 :             :                          errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
    7213                 :             :                                 RelationGetRelationName(origRelation),
    7214                 :             :                                 RelationGetRelationName(rel),
    7215                 :             :                                 NameStr(att->attname))));
    7216                 :             :         }
    7217         [ #  # ]:           0 :         else if (OidIsValid(rel->rd_rel->reltype))
    7218                 :             :         {
    7219                 :             :             /*
    7220                 :             :              * A view or composite type itself isn't a problem, but we must
    7221                 :             :              * recursively check for indirect dependencies via its rowtype.
    7222                 :             :              */
    7223                 :           0 :             find_composite_type_dependencies(rel->rd_rel->reltype,
    7224                 :             :                                              origRelation, origTypeName);
    7225                 :             :         }
    7226                 :             : 
    7227                 :           0 :         relation_close(rel, AccessShareLock);
    7228                 :             :     }
    7229                 :             : 
    7230                 :        3276 :     systable_endscan(depScan);
    7231                 :             : 
    7232                 :        3276 :     relation_close(depRel, AccessShareLock);
    7233                 :        3276 : }
    7234                 :             : 
    7235                 :             : 
    7236                 :             : /*
    7237                 :             :  * find_typed_table_dependencies
    7238                 :             :  *
    7239                 :             :  * Check to see if a composite type is being used as the type of a
    7240                 :             :  * typed table.  Abort if any are found and behavior is RESTRICT.
    7241                 :             :  * Else return the list of tables.
    7242                 :             :  */
    7243                 :             : static List *
    7244                 :         149 : find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
    7245                 :             : {
    7246                 :             :     Relation    classRel;
    7247                 :             :     ScanKeyData key[1];
    7248                 :             :     TableScanDesc scan;
    7249                 :             :     HeapTuple   tuple;
    7250                 :         149 :     List       *result = NIL;
    7251                 :             : 
    7252                 :         149 :     classRel = table_open(RelationRelationId, AccessShareLock);
    7253                 :             : 
    7254                 :         149 :     ScanKeyInit(&key[0],
    7255                 :             :                 Anum_pg_class_reloftype,
    7256                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
    7257                 :             :                 ObjectIdGetDatum(typeOid));
    7258                 :             : 
    7259                 :         149 :     scan = table_beginscan_catalog(classRel, 1, key);
    7260                 :             : 
    7261         [ +  + ]:         173 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
    7262                 :             :     {
    7263                 :          40 :         Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
    7264                 :             : 
    7265         [ +  + ]:          40 :         if (behavior == DROP_RESTRICT)
    7266         [ +  - ]:          16 :             ereport(ERROR,
    7267                 :             :                     (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
    7268                 :             :                      errmsg("cannot alter type \"%s\" because it is the type of a typed table",
    7269                 :             :                             typeName),
    7270                 :             :                      errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
    7271                 :             :         else
    7272                 :          24 :             result = lappend_oid(result, classform->oid);
    7273                 :             :     }
    7274                 :             : 
    7275                 :         133 :     table_endscan(scan);
    7276                 :         133 :     table_close(classRel, AccessShareLock);
    7277                 :             : 
    7278                 :         133 :     return result;
    7279                 :             : }
    7280                 :             : 
    7281                 :             : 
    7282                 :             : /*
    7283                 :             :  * check_of_type
    7284                 :             :  *
    7285                 :             :  * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF.  If it
    7286                 :             :  * isn't suitable, throw an error.  Currently, we require that the type
    7287                 :             :  * originated with CREATE TYPE AS.  We could support any row type, but doing so
    7288                 :             :  * would require handling a number of extra corner cases in the DDL commands.
    7289                 :             :  * (Also, allowing domain-over-composite would open up a can of worms about
    7290                 :             :  * whether and how the domain's constraints should apply to derived tables.)
    7291                 :             :  */
    7292                 :             : void
    7293                 :         127 : check_of_type(HeapTuple typetuple)
    7294                 :             : {
    7295                 :         127 :     Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
    7296                 :         127 :     bool        typeOk = false;
    7297                 :             : 
    7298         [ +  + ]:         127 :     if (typ->typtype == TYPTYPE_COMPOSITE)
    7299                 :             :     {
    7300                 :             :         Relation    typeRelation;
    7301                 :             : 
    7302                 :             :         Assert(OidIsValid(typ->typrelid));
    7303                 :         123 :         typeRelation = relation_open(typ->typrelid, AccessShareLock);
    7304                 :         123 :         typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
    7305                 :             : 
    7306                 :             :         /*
    7307                 :             :          * Close the parent rel, but keep our AccessShareLock on it until xact
    7308                 :             :          * commit.  That will prevent someone else from deleting or ALTERing
    7309                 :             :          * the type before the typed table creation/conversion commits.
    7310                 :             :          */
    7311                 :         123 :         relation_close(typeRelation, NoLock);
    7312                 :             : 
    7313         [ +  + ]:         123 :         if (!typeOk)
    7314         [ +  - ]:           4 :             ereport(ERROR,
    7315                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7316                 :             :                      errmsg("type %s is the row type of another table",
    7317                 :             :                             format_type_be(typ->oid)),
    7318                 :             :                      errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
    7319                 :             :     }
    7320                 :             :     else
    7321         [ +  - ]:           4 :         ereport(ERROR,
    7322                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7323                 :             :                  errmsg("type %s is not a composite type",
    7324                 :             :                         format_type_be(typ->oid))));
    7325                 :         119 : }
    7326                 :             : 
    7327                 :             : 
    7328                 :             : /*
    7329                 :             :  * ALTER TABLE ADD COLUMN
    7330                 :             :  *
    7331                 :             :  * Adds an additional attribute to a relation making the assumption that
    7332                 :             :  * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
    7333                 :             :  * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
    7334                 :             :  * AlterTableCmd's.
    7335                 :             :  *
    7336                 :             :  * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
    7337                 :             :  * have to decide at runtime whether to recurse or not depending on whether we
    7338                 :             :  * actually add a column or merely merge with an existing column.  (We can't
    7339                 :             :  * check this in a static pre-pass because it won't handle multiple inheritance
    7340                 :             :  * situations correctly.)
    7341                 :             :  */
    7342                 :             : static void
    7343                 :        1618 : ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    7344                 :             :                 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
    7345                 :             :                 AlterTableUtilityContext *context)
    7346                 :             : {
    7347   [ +  +  +  + ]:        1618 :     if (rel->rd_rel->reloftype && !recursing)
    7348         [ +  - ]:           4 :         ereport(ERROR,
    7349                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7350                 :             :                  errmsg("cannot add column to typed table")));
    7351                 :             : 
    7352         [ +  + ]:        1614 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    7353                 :          42 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    7354                 :             : 
    7355   [ +  +  +  + ]:        1610 :     if (recurse && !is_view)
    7356                 :        1535 :         cmd->recurse = true;
    7357                 :        1610 : }
    7358                 :             : 
    7359                 :             : /*
    7360                 :             :  * Add a column to a table.  The return value is the address of the
    7361                 :             :  * new column in the parent relation.
    7362                 :             :  *
    7363                 :             :  * cmd is pass-by-ref so that we can replace it with the parse-transformed
    7364                 :             :  * copy (but that happens only after we check for IF NOT EXISTS).
    7365                 :             :  */
    7366                 :             : static ObjectAddress
    7367                 :        2120 : ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
    7368                 :             :                 AlterTableCmd **cmd, bool recurse, bool recursing,
    7369                 :             :                 LOCKMODE lockmode, AlterTablePass cur_pass,
    7370                 :             :                 AlterTableUtilityContext *context)
    7371                 :             : {
    7372                 :        2120 :     Oid         myrelid = RelationGetRelid(rel);
    7373                 :        2120 :     ColumnDef  *colDef = castNode(ColumnDef, (*cmd)->def);
    7374                 :        2120 :     bool        if_not_exists = (*cmd)->missing_ok;
    7375                 :             :     Relation    pgclass,
    7376                 :             :                 attrdesc;
    7377                 :             :     HeapTuple   reltup;
    7378                 :             :     Form_pg_class relform;
    7379                 :             :     Form_pg_attribute attribute;
    7380                 :             :     int         newattnum;
    7381                 :             :     char        relkind;
    7382                 :             :     Expr       *defval;
    7383                 :             :     List       *children;
    7384                 :             :     ListCell   *child;
    7385                 :             :     AlterTableCmd *childcmd;
    7386                 :             :     ObjectAddress address;
    7387                 :             :     TupleDesc   tupdesc;
    7388                 :             : 
    7389                 :             :     /* since this function recurses, it could be driven to stack overflow */
    7390                 :        2120 :     check_stack_depth();
    7391                 :             : 
    7392                 :             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    7393         [ +  + ]:        2120 :     if (recursing)
    7394                 :         514 :         ATSimplePermissions((*cmd)->subtype, rel,
    7395                 :             :                             ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    7396                 :             : 
    7397   [ +  +  +  + ]:        2120 :     if (rel->rd_rel->relispartition && !recursing)
    7398         [ +  - ]:           8 :         ereport(ERROR,
    7399                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    7400                 :             :                  errmsg("cannot add column to a partition")));
    7401                 :             : 
    7402                 :        2112 :     attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
    7403                 :             : 
    7404                 :             :     /*
    7405                 :             :      * Are we adding the column to a recursion child?  If so, check whether to
    7406                 :             :      * merge with an existing definition for the column.  If we do merge, we
    7407                 :             :      * must not recurse.  Children will already have the column, and recursing
    7408                 :             :      * into them would mess up attinhcount.
    7409                 :             :      */
    7410         [ +  + ]:        2112 :     if (colDef->inhcount > 0)
    7411                 :             :     {
    7412                 :             :         HeapTuple   tuple;
    7413                 :             : 
    7414                 :             :         /* Does child already have a column by this name? */
    7415                 :         514 :         tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
    7416         [ +  + ]:         514 :         if (HeapTupleIsValid(tuple))
    7417                 :             :         {
    7418                 :          40 :             Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    7419                 :             :             Oid         ctypeId;
    7420                 :             :             int32       ctypmod;
    7421                 :             :             Oid         ccollid;
    7422                 :             : 
    7423                 :             :             /* Child column must match on type, typmod, and collation */
    7424                 :          40 :             typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
    7425         [ +  - ]:          40 :             if (ctypeId != childatt->atttypid ||
    7426         [ -  + ]:          40 :                 ctypmod != childatt->atttypmod)
    7427         [ #  # ]:           0 :                 ereport(ERROR,
    7428                 :             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
    7429                 :             :                          errmsg("child table \"%s\" has different type for column \"%s\"",
    7430                 :             :                                 RelationGetRelationName(rel), colDef->colname)));
    7431                 :          40 :             ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
    7432         [ -  + ]:          40 :             if (ccollid != childatt->attcollation)
    7433         [ #  # ]:           0 :                 ereport(ERROR,
    7434                 :             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
    7435                 :             :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
    7436                 :             :                                 RelationGetRelationName(rel), colDef->colname),
    7437                 :             :                          errdetail("\"%s\" versus \"%s\"",
    7438                 :             :                                    get_collation_name(ccollid),
    7439                 :             :                                    get_collation_name(childatt->attcollation))));
    7440                 :             : 
    7441                 :             :             /* Bump the existing child att's inhcount */
    7442         [ -  + ]:          40 :             if (pg_add_s16_overflow(childatt->attinhcount, 1,
    7443                 :             :                                     &childatt->attinhcount))
    7444         [ #  # ]:           0 :                 ereport(ERROR,
    7445                 :             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    7446                 :             :                         errmsg("too many inheritance parents"));
    7447                 :          40 :             CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
    7448                 :             : 
    7449                 :          40 :             heap_freetuple(tuple);
    7450                 :             : 
    7451                 :             :             /* Inform the user about the merge */
    7452         [ +  - ]:          40 :             ereport(NOTICE,
    7453                 :             :                     (errmsg("merging definition of column \"%s\" for child \"%s\"",
    7454                 :             :                             colDef->colname, RelationGetRelationName(rel))));
    7455                 :             : 
    7456                 :          40 :             table_close(attrdesc, RowExclusiveLock);
    7457                 :             : 
    7458                 :             :             /* Make the child column change visible */
    7459                 :          40 :             CommandCounterIncrement();
    7460                 :             : 
    7461                 :          40 :             return InvalidObjectAddress;
    7462                 :             :         }
    7463                 :             :     }
    7464                 :             : 
    7465                 :             :     /* skip if the name already exists and if_not_exists is true */
    7466         [ +  + ]:        2072 :     if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
    7467                 :             :     {
    7468                 :          44 :         table_close(attrdesc, RowExclusiveLock);
    7469                 :          44 :         return InvalidObjectAddress;
    7470                 :             :     }
    7471                 :             : 
    7472                 :             :     /*
    7473                 :             :      * Okay, we need to add the column, so go ahead and do parse
    7474                 :             :      * transformation.  This can result in queueing up, or even immediately
    7475                 :             :      * executing, subsidiary operations (such as creation of unique indexes);
    7476                 :             :      * so we mustn't do it until we have made the if_not_exists check.
    7477                 :             :      *
    7478                 :             :      * When recursing, the command was already transformed and we needn't do
    7479                 :             :      * so again.  Also, if context isn't given we can't transform.  (That
    7480                 :             :      * currently happens only for AT_AddColumnToView; we expect that view.c
    7481                 :             :      * passed us a ColumnDef that doesn't need work.)
    7482                 :             :      */
    7483   [ +  +  +  + ]:        2008 :     if (context != NULL && !recursing)
    7484                 :             :     {
    7485                 :        1513 :         *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
    7486                 :             :                                    cur_pass, context);
    7487                 :             :         Assert(*cmd != NULL);
    7488                 :        1509 :         colDef = castNode(ColumnDef, (*cmd)->def);
    7489                 :             :     }
    7490                 :             : 
    7491                 :             :     /*
    7492                 :             :      * Regular inheritance children are independent enough not to inherit the
    7493                 :             :      * identity column from parent hence cannot recursively add identity
    7494                 :             :      * column if the table has inheritance children.
    7495                 :             :      *
    7496                 :             :      * Partitions, on the other hand, are integral part of a partitioned table
    7497                 :             :      * and inherit identity column.  Hence propagate identity column down the
    7498                 :             :      * partition hierarchy.
    7499                 :             :      */
    7500   [ +  +  +  - ]:        2004 :     if (colDef->identity &&
    7501                 :          36 :         recurse &&
    7502   [ +  +  +  + ]:          68 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
    7503                 :          32 :         find_inheritance_children(myrelid, NoLock) != NIL)
    7504         [ +  - ]:           4 :         ereport(ERROR,
    7505                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7506                 :             :                  errmsg("cannot recursively add identity column to table that has child tables")));
    7507                 :             : 
    7508                 :        2000 :     pgclass = table_open(RelationRelationId, RowExclusiveLock);
    7509                 :             : 
    7510                 :        2000 :     reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
    7511         [ -  + ]:        2000 :     if (!HeapTupleIsValid(reltup))
    7512         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", myrelid);
    7513                 :        2000 :     relform = (Form_pg_class) GETSTRUCT(reltup);
    7514                 :        2000 :     relkind = relform->relkind;
    7515                 :             : 
    7516                 :             :     /* Determine the new attribute's number */
    7517                 :        2000 :     newattnum = relform->relnatts + 1;
    7518         [ -  + ]:        2000 :     if (newattnum > MaxHeapAttributeNumber)
    7519         [ #  # ]:           0 :         ereport(ERROR,
    7520                 :             :                 (errcode(ERRCODE_TOO_MANY_COLUMNS),
    7521                 :             :                  errmsg("tables can have at most %d columns",
    7522                 :             :                         MaxHeapAttributeNumber)));
    7523                 :             : 
    7524                 :             :     /*
    7525                 :             :      * Construct new attribute's pg_attribute entry.
    7526                 :             :      */
    7527                 :        2000 :     tupdesc = BuildDescForRelation(list_make1(colDef));
    7528                 :             : 
    7529                 :        1992 :     attribute = TupleDescAttr(tupdesc, 0);
    7530                 :             : 
    7531                 :             :     /* Fix up attribute number */
    7532                 :        1992 :     attribute->attnum = newattnum;
    7533                 :             : 
    7534                 :             :     /* make sure datatype is legal for a column */
    7535                 :        3984 :     CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
    7536                 :        1992 :                        list_make1_oid(rel->rd_rel->reltype),
    7537         [ +  + ]:        1992 :                        (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
    7538                 :             : 
    7539                 :        1964 :     InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
    7540                 :             : 
    7541                 :        1964 :     table_close(attrdesc, RowExclusiveLock);
    7542                 :             : 
    7543                 :             :     /*
    7544                 :             :      * Update pg_class tuple as appropriate
    7545                 :             :      */
    7546                 :        1964 :     relform->relnatts = newattnum;
    7547                 :             : 
    7548                 :        1964 :     CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
    7549                 :             : 
    7550                 :        1964 :     heap_freetuple(reltup);
    7551                 :             : 
    7552                 :             :     /* Post creation hook for new attribute */
    7553         [ -  + ]:        1964 :     InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
    7554                 :             : 
    7555                 :        1964 :     table_close(pgclass, RowExclusiveLock);
    7556                 :             : 
    7557                 :             :     /* Make the attribute's catalog entry visible */
    7558                 :        1964 :     CommandCounterIncrement();
    7559                 :             : 
    7560                 :             :     /*
    7561                 :             :      * Store the DEFAULT, if any, in the catalogs
    7562                 :             :      */
    7563         [ +  + ]:        1964 :     if (colDef->raw_default)
    7564                 :             :     {
    7565                 :             :         RawColumnDefault *rawEnt;
    7566                 :             : 
    7567                 :         766 :         rawEnt = palloc_object(RawColumnDefault);
    7568                 :         766 :         rawEnt->attnum = attribute->attnum;
    7569                 :         766 :         rawEnt->raw_default = copyObject(colDef->raw_default);
    7570                 :         766 :         rawEnt->generated = colDef->generated;
    7571                 :             : 
    7572                 :             :         /*
    7573                 :             :          * This function is intended for CREATE TABLE, so it processes a
    7574                 :             :          * _list_ of defaults, but we just do one.
    7575                 :             :          */
    7576                 :         766 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    7577                 :             :                                   false, true, false, NULL);
    7578                 :             : 
    7579                 :             :         /* Make the additional catalog changes visible */
    7580                 :         678 :         CommandCounterIncrement();
    7581                 :             :     }
    7582                 :             : 
    7583                 :             :     /*
    7584                 :             :      * Tell Phase 3 to fill in the default expression, if there is one.
    7585                 :             :      *
    7586                 :             :      * If there is no default, Phase 3 doesn't have to do anything, because
    7587                 :             :      * that effectively means that the default is NULL.  The heap tuple access
    7588                 :             :      * routines always check for attnum > # of attributes in tuple, and return
    7589                 :             :      * NULL if so, so without any modification of the tuple data we will get
    7590                 :             :      * the effect of NULL values in the new column.
    7591                 :             :      *
    7592                 :             :      * An exception occurs when the new column is of a domain type: the domain
    7593                 :             :      * might have a not-null constraint, or a check constraint that indirectly
    7594                 :             :      * rejects nulls.  If there are any domain constraints then we construct
    7595                 :             :      * an explicit NULL default value that will be passed through
    7596                 :             :      * CoerceToDomain processing.  (This is a tad inefficient, since it causes
    7597                 :             :      * rewriting the table which we really wouldn't have to do; but we do it
    7598                 :             :      * to preserve the historical behavior that such a failure will be raised
    7599                 :             :      * only if the table currently contains some rows.)
    7600                 :             :      *
    7601                 :             :      * Note: we use build_column_default, and not just the cooked default
    7602                 :             :      * returned by AddRelationNewConstraints, so that the right thing happens
    7603                 :             :      * when a datatype's default applies.
    7604                 :             :      *
    7605                 :             :      * Note: it might seem that this should happen at the end of Phase 2, so
    7606                 :             :      * that the effects of subsequent subcommands can be taken into account.
    7607                 :             :      * It's intentional that we do it now, though.  The new column should be
    7608                 :             :      * filled according to what is said in the ADD COLUMN subcommand, so that
    7609                 :             :      * the effects are the same as if this subcommand had been run by itself
    7610                 :             :      * and the later subcommands had been issued in new ALTER TABLE commands.
    7611                 :             :      *
    7612                 :             :      * We can skip this entirely for relations without storage, since Phase 3
    7613                 :             :      * is certainly not going to touch them.
    7614                 :             :      */
    7615   [ +  +  +  -  :        1876 :     if (RELKIND_HAS_STORAGE(relkind))
          +  -  +  -  -  
                      + ]
    7616                 :             :     {
    7617                 :             :         bool        has_domain_constraints;
    7618                 :        1606 :         bool        has_missing = false;
    7619                 :             : 
    7620                 :             :         /*
    7621                 :             :          * For an identity column, we can't use build_column_default(),
    7622                 :             :          * because the sequence ownership isn't set yet.  So do it manually.
    7623                 :             :          */
    7624         [ +  + ]:        1606 :         if (colDef->identity)
    7625                 :             :         {
    7626                 :          28 :             NextValueExpr *nve = makeNode(NextValueExpr);
    7627                 :             : 
    7628                 :          28 :             nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
    7629                 :          28 :             nve->typeId = attribute->atttypid;
    7630                 :             : 
    7631                 :          28 :             defval = (Expr *) nve;
    7632                 :             :         }
    7633                 :             :         else
    7634                 :        1578 :             defval = (Expr *) build_column_default(rel, attribute->attnum);
    7635                 :             : 
    7636                 :             :         /* Build CoerceToDomain(NULL) expression if needed */
    7637                 :        1606 :         has_domain_constraints = DomainHasConstraints(attribute->atttypid, NULL);
    7638   [ +  +  +  + ]:        1606 :         if (!defval && has_domain_constraints)
    7639                 :             :         {
    7640                 :             :             Oid         baseTypeId;
    7641                 :             :             int32       baseTypeMod;
    7642                 :             :             Oid         baseTypeColl;
    7643                 :             : 
    7644                 :           4 :             baseTypeMod = attribute->atttypmod;
    7645                 :           4 :             baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
    7646                 :           4 :             baseTypeColl = get_typcollation(baseTypeId);
    7647                 :           4 :             defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
    7648                 :           4 :             defval = (Expr *) coerce_to_target_type(NULL,
    7649                 :             :                                                     (Node *) defval,
    7650                 :             :                                                     baseTypeId,
    7651                 :             :                                                     attribute->atttypid,
    7652                 :             :                                                     attribute->atttypmod,
    7653                 :             :                                                     COERCION_ASSIGNMENT,
    7654                 :             :                                                     COERCE_IMPLICIT_CAST,
    7655                 :             :                                                     -1);
    7656         [ -  + ]:           4 :             if (defval == NULL) /* should not happen */
    7657         [ #  # ]:           0 :                 elog(ERROR, "failed to coerce base type to domain");
    7658                 :             :         }
    7659                 :             : 
    7660         [ +  + ]:        1606 :         if (defval)
    7661                 :             :         {
    7662                 :             :             NewColumnValue *newval;
    7663                 :             : 
    7664                 :             :             /* Prepare defval for execution, either here or in Phase 3 */
    7665                 :         615 :             defval = expression_planner(defval);
    7666                 :             : 
    7667                 :             :             /* Add the new default to the newvals list */
    7668                 :         615 :             newval = palloc0_object(NewColumnValue);
    7669                 :         615 :             newval->attnum = attribute->attnum;
    7670                 :         615 :             newval->expr = defval;
    7671                 :         615 :             newval->is_generated = (colDef->generated != '\0');
    7672                 :             : 
    7673                 :         615 :             tab->newvals = lappend(tab->newvals, newval);
    7674                 :             : 
    7675                 :             :             /*
    7676                 :             :              * Attempt to skip a complete table rewrite by storing the
    7677                 :             :              * specified DEFAULT value outside of the heap.  This is only
    7678                 :             :              * allowed for plain relations and non-generated columns, and the
    7679                 :             :              * default expression can't be volatile (stable is OK).  Note that
    7680                 :             :              * contain_volatile_functions deems CoerceToDomain immutable, but
    7681                 :             :              * here we consider that coercion to a domain with constraints is
    7682                 :             :              * volatile; else it might fail even when the table is empty.
    7683                 :             :              */
    7684         [ +  - ]:         615 :             if (rel->rd_rel->relkind == RELKIND_RELATION &&
    7685         [ +  + ]:         615 :                 !colDef->generated &&
    7686         [ +  + ]:         478 :                 !has_domain_constraints &&
    7687         [ +  + ]:         470 :                 !contain_volatile_functions((Node *) defval))
    7688                 :         360 :             {
    7689                 :             :                 EState     *estate;
    7690                 :             :                 ExprState  *exprState;
    7691                 :             :                 Datum       missingval;
    7692                 :             :                 bool        missingIsNull;
    7693                 :             : 
    7694                 :             :                 /* Evaluate the default expression */
    7695                 :         360 :                 estate = CreateExecutorState();
    7696                 :         360 :                 exprState = ExecPrepareExpr(defval, estate);
    7697                 :         360 :                 missingval = ExecEvalExpr(exprState,
    7698         [ -  + ]:         360 :                                           GetPerTupleExprContext(estate),
    7699                 :             :                                           &missingIsNull);
    7700                 :             :                 /* If it turns out NULL, nothing to do; else store it */
    7701         [ +  - ]:         360 :                 if (!missingIsNull)
    7702                 :             :                 {
    7703                 :         360 :                     StoreAttrMissingVal(rel, attribute->attnum, missingval);
    7704                 :             :                     /* Make the additional catalog change visible */
    7705                 :         360 :                     CommandCounterIncrement();
    7706                 :         360 :                     has_missing = true;
    7707                 :             :                 }
    7708                 :         360 :                 FreeExecutorState(estate);
    7709                 :             :             }
    7710                 :             :             else
    7711                 :             :             {
    7712                 :             :                 /*
    7713                 :             :                  * Failed to use missing mode.  We have to do a table rewrite
    7714                 :             :                  * to install the value --- unless it's a virtual generated
    7715                 :             :                  * column.
    7716                 :             :                  */
    7717         [ +  + ]:         255 :                 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
    7718                 :         182 :                     tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    7719                 :             :             }
    7720                 :             :         }
    7721                 :             : 
    7722         [ +  + ]:        1606 :         if (!has_missing)
    7723                 :             :         {
    7724                 :             :             /*
    7725                 :             :              * If the new column is NOT NULL, and there is no missing value,
    7726                 :             :              * tell Phase 3 it needs to check for NULLs.
    7727                 :             :              */
    7728                 :        1246 :             tab->verify_new_notnull |= colDef->is_not_null;
    7729                 :             :         }
    7730                 :             :     }
    7731                 :             : 
    7732                 :             :     /*
    7733                 :             :      * Add needed dependency entries for the new column.
    7734                 :             :      */
    7735                 :        1876 :     add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
    7736                 :        1876 :     add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
    7737                 :             : 
    7738                 :             :     /*
    7739                 :             :      * Propagate to children as appropriate.  Unlike most other ALTER
    7740                 :             :      * routines, we have to do this one level of recursion at a time; we can't
    7741                 :             :      * use find_all_inheritors to do it in one pass.
    7742                 :             :      */
    7743                 :             :     children =
    7744                 :        1876 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    7745                 :             : 
    7746                 :             :     /*
    7747                 :             :      * If we are told not to recurse, there had better not be any child
    7748                 :             :      * tables; else the addition would put them out of step.
    7749                 :             :      */
    7750   [ +  +  +  + ]:        1876 :     if (children && !recurse)
    7751         [ +  - ]:           8 :         ereport(ERROR,
    7752                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7753                 :             :                  errmsg("column must be added to child tables too")));
    7754                 :             : 
    7755                 :             :     /* Children should see column as singly inherited */
    7756         [ +  + ]:        1868 :     if (!recursing)
    7757                 :             :     {
    7758                 :        1394 :         childcmd = copyObject(*cmd);
    7759                 :        1394 :         colDef = castNode(ColumnDef, childcmd->def);
    7760                 :        1394 :         colDef->inhcount = 1;
    7761                 :        1394 :         colDef->is_local = false;
    7762                 :             :     }
    7763                 :             :     else
    7764                 :         474 :         childcmd = *cmd;        /* no need to copy again */
    7765                 :             : 
    7766   [ +  +  +  +  :        2382 :     foreach(child, children)
                   +  + ]
    7767                 :             :     {
    7768                 :         514 :         Oid         childrelid = lfirst_oid(child);
    7769                 :             :         Relation    childrel;
    7770                 :             :         AlteredTableInfo *childtab;
    7771                 :             : 
    7772                 :             :         /* find_inheritance_children already got lock */
    7773                 :         514 :         childrel = table_open(childrelid, NoLock);
    7774                 :         514 :         CheckAlterTableIsSafe(childrel);
    7775                 :             : 
    7776                 :             :         /* Find or create work queue entry for this table */
    7777                 :         514 :         childtab = ATGetQueueEntry(wqueue, childrel);
    7778                 :             : 
    7779                 :             :         /* Recurse to child; return value is ignored */
    7780                 :         514 :         ATExecAddColumn(wqueue, childtab, childrel,
    7781                 :             :                         &childcmd, recurse, true,
    7782                 :             :                         lockmode, cur_pass, context);
    7783                 :             : 
    7784                 :         514 :         table_close(childrel, NoLock);
    7785                 :             :     }
    7786                 :             : 
    7787                 :        1868 :     ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
    7788                 :        1868 :     return address;
    7789                 :             : }
    7790                 :             : 
    7791                 :             : /*
    7792                 :             :  * If a new or renamed column will collide with the name of an existing
    7793                 :             :  * column and if_not_exists is false then error out, else do nothing.
    7794                 :             :  */
    7795                 :             : static bool
    7796                 :        2379 : check_for_column_name_collision(Relation rel, const char *colname,
    7797                 :             :                                 bool if_not_exists)
    7798                 :             : {
    7799                 :             :     HeapTuple   attTuple;
    7800                 :             :     int         attnum;
    7801                 :             : 
    7802                 :             :     /*
    7803                 :             :      * this test is deliberately not attisdropped-aware, since if one tries to
    7804                 :             :      * add a column matching a dropped column name, it's gonna fail anyway.
    7805                 :             :      */
    7806                 :        2379 :     attTuple = SearchSysCache2(ATTNAME,
    7807                 :             :                                ObjectIdGetDatum(RelationGetRelid(rel)),
    7808                 :             :                                PointerGetDatum(colname));
    7809         [ +  + ]:        2379 :     if (!HeapTupleIsValid(attTuple))
    7810                 :        2307 :         return true;
    7811                 :             : 
    7812                 :          72 :     attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
    7813                 :          72 :     ReleaseSysCache(attTuple);
    7814                 :             : 
    7815                 :             :     /*
    7816                 :             :      * We throw a different error message for conflicts with system column
    7817                 :             :      * names, since they are normally not shown and the user might otherwise
    7818                 :             :      * be confused about the reason for the conflict.
    7819                 :             :      */
    7820         [ +  + ]:          72 :     if (attnum <= 0)
    7821         [ +  - ]:           8 :         ereport(ERROR,
    7822                 :             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7823                 :             :                  errmsg("column name \"%s\" conflicts with a system column name",
    7824                 :             :                         colname)));
    7825                 :             :     else
    7826                 :             :     {
    7827         [ +  + ]:          64 :         if (if_not_exists)
    7828                 :             :         {
    7829         [ +  - ]:          44 :             ereport(NOTICE,
    7830                 :             :                     (errcode(ERRCODE_DUPLICATE_COLUMN),
    7831                 :             :                      errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
    7832                 :             :                             colname, RelationGetRelationName(rel))));
    7833                 :          44 :             return false;
    7834                 :             :         }
    7835                 :             : 
    7836         [ +  - ]:          20 :         ereport(ERROR,
    7837                 :             :                 (errcode(ERRCODE_DUPLICATE_COLUMN),
    7838                 :             :                  errmsg("column \"%s\" of relation \"%s\" already exists",
    7839                 :             :                         colname, RelationGetRelationName(rel))));
    7840                 :             :     }
    7841                 :             : 
    7842                 :             :     return true;
    7843                 :             : }
    7844                 :             : 
    7845                 :             : /*
    7846                 :             :  * Install a column's dependency on its datatype.
    7847                 :             :  */
    7848                 :             : static void
    7849                 :        2652 : add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
    7850                 :             : {
    7851                 :             :     ObjectAddress myself,
    7852                 :             :                 referenced;
    7853                 :             : 
    7854                 :        2652 :     myself.classId = RelationRelationId;
    7855                 :        2652 :     myself.objectId = relid;
    7856                 :        2652 :     myself.objectSubId = attnum;
    7857                 :        2652 :     referenced.classId = TypeRelationId;
    7858                 :        2652 :     referenced.objectId = typid;
    7859                 :        2652 :     referenced.objectSubId = 0;
    7860                 :        2652 :     recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7861                 :        2652 : }
    7862                 :             : 
    7863                 :             : /*
    7864                 :             :  * Install a column's dependency on its collation.
    7865                 :             :  */
    7866                 :             : static void
    7867                 :        2652 : add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
    7868                 :             : {
    7869                 :             :     ObjectAddress myself,
    7870                 :             :                 referenced;
    7871                 :             : 
    7872                 :             :     /* We know the default collation is pinned, so don't bother recording it */
    7873   [ +  +  +  + ]:        2652 :     if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
    7874                 :             :     {
    7875                 :          12 :         myself.classId = RelationRelationId;
    7876                 :          12 :         myself.objectId = relid;
    7877                 :          12 :         myself.objectSubId = attnum;
    7878                 :          12 :         referenced.classId = CollationRelationId;
    7879                 :          12 :         referenced.objectId = collid;
    7880                 :          12 :         referenced.objectSubId = 0;
    7881                 :          12 :         recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
    7882                 :             :     }
    7883                 :        2652 : }
    7884                 :             : 
    7885                 :             : /*
    7886                 :             :  * ALTER TABLE ALTER COLUMN DROP NOT NULL
    7887                 :             :  *
    7888                 :             :  * Return the address of the modified column.  If the column was already
    7889                 :             :  * nullable, InvalidObjectAddress is returned.
    7890                 :             :  */
    7891                 :             : static ObjectAddress
    7892                 :         177 : ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
    7893                 :             :                   LOCKMODE lockmode)
    7894                 :             : {
    7895                 :             :     HeapTuple   tuple;
    7896                 :             :     HeapTuple   conTup;
    7897                 :             :     Form_pg_attribute attTup;
    7898                 :             :     AttrNumber  attnum;
    7899                 :             :     Relation    attr_rel;
    7900                 :             :     ObjectAddress address;
    7901                 :             : 
    7902                 :             :     /*
    7903                 :             :      * lookup the attribute
    7904                 :             :      */
    7905                 :         177 :     attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    7906                 :             : 
    7907                 :         177 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    7908         [ +  + ]:         177 :     if (!HeapTupleIsValid(tuple))
    7909         [ +  - ]:          12 :         ereport(ERROR,
    7910                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    7911                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    7912                 :             :                         colName, RelationGetRelationName(rel))));
    7913                 :         165 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    7914                 :         165 :     attnum = attTup->attnum;
    7915                 :         165 :     ObjectAddressSubSet(address, RelationRelationId,
    7916                 :             :                         RelationGetRelid(rel), attnum);
    7917                 :             : 
    7918                 :             :     /* If the column is already nullable there's nothing to do. */
    7919         [ -  + ]:         165 :     if (!attTup->attnotnull)
    7920                 :             :     {
    7921                 :           0 :         table_close(attr_rel, RowExclusiveLock);
    7922                 :           0 :         return InvalidObjectAddress;
    7923                 :             :     }
    7924                 :             : 
    7925                 :             :     /* Prevent them from altering a system attribute */
    7926         [ -  + ]:         165 :     if (attnum <= 0)
    7927         [ #  # ]:           0 :         ereport(ERROR,
    7928                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    7929                 :             :                  errmsg("cannot alter system column \"%s\"",
    7930                 :             :                         colName)));
    7931                 :             : 
    7932         [ +  + ]:         165 :     if (attTup->attidentity)
    7933         [ +  - ]:          12 :         ereport(ERROR,
    7934                 :             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    7935                 :             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    7936                 :             :                         colName, RelationGetRelationName(rel))));
    7937                 :             : 
    7938                 :             :     /*
    7939                 :             :      * If rel is partition, shouldn't drop NOT NULL if parent has the same.
    7940                 :             :      */
    7941         [ +  + ]:         153 :     if (rel->rd_rel->relispartition)
    7942                 :             :     {
    7943                 :           8 :         Oid         parentId = get_partition_parent(RelationGetRelid(rel), false);
    7944                 :           8 :         Relation    parent = table_open(parentId, AccessShareLock);
    7945                 :           8 :         TupleDesc   tupDesc = RelationGetDescr(parent);
    7946                 :             :         AttrNumber  parent_attnum;
    7947                 :             : 
    7948                 :           8 :         parent_attnum = get_attnum(parentId, colName);
    7949         [ +  - ]:           8 :         if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
    7950         [ +  - ]:           8 :             ereport(ERROR,
    7951                 :             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    7952                 :             :                      errmsg("column \"%s\" is marked NOT NULL in parent table",
    7953                 :             :                             colName)));
    7954                 :           0 :         table_close(parent, AccessShareLock);
    7955                 :             :     }
    7956                 :             : 
    7957                 :             :     /*
    7958                 :             :      * Find the constraint that makes this column NOT NULL, and drop it.
    7959                 :             :      * dropconstraint_internal() resets attnotnull.
    7960                 :             :      */
    7961                 :         145 :     conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
    7962         [ -  + ]:         145 :     if (conTup == NULL)
    7963         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
    7964                 :             :              colName, RelationGetRelationName(rel));
    7965                 :             : 
    7966                 :             :     /* The normal case: we have a pg_constraint row, remove it */
    7967                 :         145 :     dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
    7968                 :             :                             false, lockmode);
    7969                 :         109 :     heap_freetuple(conTup);
    7970                 :             : 
    7971         [ -  + ]:         109 :     InvokeObjectPostAlterHook(RelationRelationId,
    7972                 :             :                               RelationGetRelid(rel), attnum);
    7973                 :             : 
    7974                 :         109 :     table_close(attr_rel, RowExclusiveLock);
    7975                 :             : 
    7976                 :         109 :     return address;
    7977                 :             : }
    7978                 :             : 
    7979                 :             : /*
    7980                 :             :  * set_attnotnull
    7981                 :             :  *      Helper to update/validate the pg_attribute status of a not-null
    7982                 :             :  *      constraint
    7983                 :             :  *
    7984                 :             :  * pg_attribute.attnotnull is set true, if it isn't already.
    7985                 :             :  * If queue_validation is true, also set up wqueue to validate the constraint.
    7986                 :             :  * wqueue may be given as NULL when validation is not needed (e.g., on table
    7987                 :             :  * creation).
    7988                 :             :  */
    7989                 :             : static void
    7990                 :       16621 : set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
    7991                 :             :                bool queue_validation)
    7992                 :             : {
    7993                 :             :     Form_pg_attribute attr;
    7994                 :             : 
    7995                 :             :     Assert(!queue_validation || wqueue);
    7996                 :             : 
    7997                 :       16621 :     CheckAlterTableIsSafe(rel);
    7998                 :             : 
    7999                 :             :     /*
    8000                 :             :      * Exit quickly by testing attnotnull from the tupledesc's copy of the
    8001                 :             :      * attribute.
    8002                 :             :      */
    8003                 :       16621 :     attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
    8004         [ -  + ]:       16621 :     if (attr->attisdropped)
    8005                 :           0 :         return;
    8006                 :             : 
    8007         [ +  + ]:       16621 :     if (!attr->attnotnull)
    8008                 :             :     {
    8009                 :             :         Relation    attr_rel;
    8010                 :             :         HeapTuple   tuple;
    8011                 :             : 
    8012                 :         995 :         attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    8013                 :             : 
    8014                 :         995 :         tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
    8015         [ -  + ]:         995 :         if (!HeapTupleIsValid(tuple))
    8016         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for attribute %d of relation %u",
    8017                 :             :                  attnum, RelationGetRelid(rel));
    8018                 :             : 
    8019                 :         995 :         attr = (Form_pg_attribute) GETSTRUCT(tuple);
    8020                 :             : 
    8021                 :         995 :         attr->attnotnull = true;
    8022                 :         995 :         CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    8023                 :             : 
    8024                 :             :         /*
    8025                 :             :          * If the nullness isn't already proven by validated constraints, have
    8026                 :             :          * ALTER TABLE phase 3 test for it.
    8027                 :             :          */
    8028   [ +  +  +  - ]:         995 :         if (queue_validation && wqueue &&
    8029         [ +  + ]:         824 :             !NotNullImpliedByRelConstraints(rel, attr))
    8030                 :             :         {
    8031                 :             :             AlteredTableInfo *tab;
    8032                 :             : 
    8033                 :         792 :             tab = ATGetQueueEntry(wqueue, rel);
    8034                 :         792 :             tab->verify_new_notnull = true;
    8035                 :             :         }
    8036                 :             : 
    8037                 :         995 :         CommandCounterIncrement();
    8038                 :             : 
    8039                 :         995 :         table_close(attr_rel, RowExclusiveLock);
    8040                 :         995 :         heap_freetuple(tuple);
    8041                 :             :     }
    8042                 :             :     else
    8043                 :             :     {
    8044                 :       15626 :         CacheInvalidateRelcache(rel);
    8045                 :             :     }
    8046                 :             : }
    8047                 :             : 
    8048                 :             : /*
    8049                 :             :  * ALTER TABLE ALTER COLUMN SET NOT NULL
    8050                 :             :  *
    8051                 :             :  * Add a not-null constraint to a single table and its children.  Returns
    8052                 :             :  * the address of the constraint added to the parent relation, if one gets
    8053                 :             :  * added, or InvalidObjectAddress otherwise.
    8054                 :             :  *
    8055                 :             :  * We must recurse to child tables during execution, rather than using
    8056                 :             :  * ALTER TABLE's normal prep-time recursion.
    8057                 :             :  */
    8058                 :             : static ObjectAddress
    8059                 :         469 : ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
    8060                 :             :                  bool recurse, bool recursing, LOCKMODE lockmode)
    8061                 :             : {
    8062                 :             :     HeapTuple   tuple;
    8063                 :             :     AttrNumber  attnum;
    8064                 :             :     ObjectAddress address;
    8065                 :             :     Constraint *constraint;
    8066                 :             :     CookedConstraint *ccon;
    8067                 :             :     List       *cooked;
    8068                 :         469 :     bool        is_no_inherit = false;
    8069                 :             : 
    8070                 :             :     /* Guard against stack overflow due to overly deep inheritance tree. */
    8071                 :         469 :     check_stack_depth();
    8072                 :             : 
    8073                 :             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    8074         [ +  + ]:         469 :     if (recursing)
    8075                 :             :     {
    8076                 :         197 :         ATSimplePermissions(AT_AddConstraint, rel,
    8077                 :             :                             ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
    8078                 :             :         Assert(conName != NULL);
    8079                 :             :     }
    8080                 :             : 
    8081                 :         469 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    8082         [ +  + ]:         469 :     if (attnum == InvalidAttrNumber)
    8083         [ +  - ]:          12 :         ereport(ERROR,
    8084                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8085                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8086                 :             :                         colName, RelationGetRelationName(rel))));
    8087                 :             : 
    8088                 :             :     /* Prevent them from altering a system attribute */
    8089         [ -  + ]:         457 :     if (attnum <= 0)
    8090         [ #  # ]:           0 :         ereport(ERROR,
    8091                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8092                 :             :                  errmsg("cannot alter system column \"%s\"",
    8093                 :             :                         colName)));
    8094                 :             : 
    8095                 :             :     /* See if there's already a constraint */
    8096                 :         457 :     tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
    8097         [ +  + ]:         457 :     if (HeapTupleIsValid(tuple))
    8098                 :             :     {
    8099                 :         105 :         Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
    8100                 :         105 :         bool        changed = false;
    8101                 :             : 
    8102                 :             :         /*
    8103                 :             :          * Don't let a NO INHERIT constraint be changed into inherit.
    8104                 :             :          */
    8105   [ +  +  +  - ]:         105 :         if (conForm->connoinherit && recurse)
    8106         [ +  - ]:           8 :             ereport(ERROR,
    8107                 :             :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8108                 :             :                     errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
    8109                 :             :                            NameStr(conForm->conname),
    8110                 :             :                            RelationGetRelationName(rel)));
    8111                 :             : 
    8112                 :             :         /*
    8113                 :             :          * If we find an appropriate constraint, we're almost done, but just
    8114                 :             :          * need to change some properties on it: if we're recursing, increment
    8115                 :             :          * coninhcount; if not, set conislocal if not already set.
    8116                 :             :          */
    8117         [ +  + ]:          97 :         if (recursing)
    8118                 :             :         {
    8119         [ -  + ]:          68 :             if (pg_add_s16_overflow(conForm->coninhcount, 1,
    8120                 :             :                                     &conForm->coninhcount))
    8121         [ #  # ]:           0 :                 ereport(ERROR,
    8122                 :             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    8123                 :             :                         errmsg("too many inheritance parents"));
    8124                 :          68 :             changed = true;
    8125                 :             :         }
    8126         [ -  + ]:          29 :         else if (!conForm->conislocal)
    8127                 :             :         {
    8128                 :           0 :             conForm->conislocal = true;
    8129                 :           0 :             changed = true;
    8130                 :             :         }
    8131         [ +  + ]:          29 :         else if (!conForm->convalidated)
    8132                 :             :         {
    8133                 :             :             /*
    8134                 :             :              * Flip attnotnull and convalidated, and also validate the
    8135                 :             :              * constraint.
    8136                 :             :              */
    8137                 :          16 :             return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
    8138                 :             :                                             recurse, recursing, lockmode);
    8139                 :             :         }
    8140                 :             : 
    8141         [ +  + ]:          81 :         if (changed)
    8142                 :             :         {
    8143                 :             :             Relation    constr_rel;
    8144                 :             : 
    8145                 :          68 :             constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
    8146                 :             : 
    8147                 :          68 :             CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
    8148                 :          68 :             ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
    8149                 :          68 :             table_close(constr_rel, RowExclusiveLock);
    8150                 :             :         }
    8151                 :             : 
    8152         [ +  + ]:          81 :         if (changed)
    8153                 :          68 :             return address;
    8154                 :             :         else
    8155                 :          13 :             return InvalidObjectAddress;
    8156                 :             :     }
    8157                 :             : 
    8158                 :             :     /*
    8159                 :             :      * If we're asked not to recurse, and children exist, raise an error for
    8160                 :             :      * partitioned tables.  For inheritance, we act as if NO INHERIT had been
    8161                 :             :      * specified.
    8162                 :             :      */
    8163   [ +  +  +  + ]:         372 :     if (!recurse &&
    8164                 :          20 :         find_inheritance_children(RelationGetRelid(rel),
    8165                 :             :                                   NoLock) != NIL)
    8166                 :             :     {
    8167         [ +  + ]:          12 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    8168         [ +  - ]:           4 :             ereport(ERROR,
    8169                 :             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8170                 :             :                     errmsg("constraint must be added to child tables too"),
    8171                 :             :                     errhint("Do not specify the ONLY keyword."));
    8172                 :             :         else
    8173                 :           8 :             is_no_inherit = true;
    8174                 :             :     }
    8175                 :             : 
    8176                 :             :     /*
    8177                 :             :      * No constraint exists; we must add one.  First determine a name to use,
    8178                 :             :      * if we haven't already.
    8179                 :             :      */
    8180         [ +  + ]:         348 :     if (!recursing)
    8181                 :             :     {
    8182                 :             :         Assert(conName == NULL);
    8183                 :         223 :         conName = ChooseConstraintName(RelationGetRelationName(rel),
    8184                 :             :                                        colName, "not_null",
    8185                 :         223 :                                        RelationGetNamespace(rel),
    8186                 :             :                                        NIL);
    8187                 :             :     }
    8188                 :             : 
    8189                 :         348 :     constraint = makeNotNullConstraint(makeString(colName));
    8190                 :         348 :     constraint->is_no_inherit = is_no_inherit;
    8191                 :         348 :     constraint->conname = conName;
    8192                 :             : 
    8193                 :             :     /* and do it */
    8194                 :         348 :     cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
    8195                 :         348 :                                        false, !recursing, false, NULL);
    8196                 :         348 :     ccon = linitial(cooked);
    8197                 :         348 :     ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
    8198                 :             : 
    8199                 :             :     /* Mark pg_attribute.attnotnull for the column and queue validation */
    8200                 :         348 :     set_attnotnull(wqueue, rel, attnum, true);
    8201                 :             : 
    8202         [ -  + ]:         348 :     InvokeObjectPostAlterHook(RelationRelationId,
    8203                 :             :                               RelationGetRelid(rel), attnum);
    8204                 :             : 
    8205                 :             :     /*
    8206                 :             :      * Recurse to propagate the constraint to children that don't have one.
    8207                 :             :      */
    8208         [ +  + ]:         348 :     if (recurse)
    8209                 :             :     {
    8210                 :             :         List       *children;
    8211                 :             : 
    8212                 :         332 :         children = find_inheritance_children(RelationGetRelid(rel),
    8213                 :             :                                              lockmode);
    8214                 :             : 
    8215   [ +  +  +  +  :         817 :         foreach_oid(childoid, children)
                   +  + ]
    8216                 :             :         {
    8217                 :         161 :             Relation    childrel = table_open(childoid, NoLock);
    8218                 :             : 
    8219                 :         161 :             CommandCounterIncrement();
    8220                 :             : 
    8221                 :         161 :             ATExecSetNotNull(wqueue, childrel, conName, colName,
    8222                 :             :                              recurse, true, lockmode);
    8223                 :         157 :             table_close(childrel, NoLock);
    8224                 :             :         }
    8225                 :             :     }
    8226                 :             : 
    8227                 :         344 :     return address;
    8228                 :             : }
    8229                 :             : 
    8230                 :             : /*
    8231                 :             :  * NotNullImpliedByRelConstraints
    8232                 :             :  *      Does rel's existing constraints imply NOT NULL for the given attribute?
    8233                 :             :  */
    8234                 :             : static bool
    8235                 :         824 : NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
    8236                 :             : {
    8237                 :         824 :     NullTest   *nnulltest = makeNode(NullTest);
    8238                 :             : 
    8239                 :        1648 :     nnulltest->arg = (Expr *) makeVar(1,
    8240                 :         824 :                                       attr->attnum,
    8241                 :             :                                       attr->atttypid,
    8242                 :             :                                       attr->atttypmod,
    8243                 :             :                                       attr->attcollation,
    8244                 :             :                                       0);
    8245                 :         824 :     nnulltest->nulltesttype = IS_NOT_NULL;
    8246                 :             : 
    8247                 :             :     /*
    8248                 :             :      * argisrow = false is correct even for a composite column, because
    8249                 :             :      * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
    8250                 :             :      * case, just IS DISTINCT FROM NULL.
    8251                 :             :      */
    8252                 :         824 :     nnulltest->argisrow = false;
    8253                 :         824 :     nnulltest->location = -1;
    8254                 :             : 
    8255         [ +  + ]:         824 :     if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
    8256                 :             :     {
    8257         [ +  + ]:          32 :         ereport(DEBUG1,
    8258                 :             :                 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
    8259                 :             :                                  RelationGetRelationName(rel), NameStr(attr->attname))));
    8260                 :          32 :         return true;
    8261                 :             :     }
    8262                 :             : 
    8263                 :         792 :     return false;
    8264                 :             : }
    8265                 :             : 
    8266                 :             : /*
    8267                 :             :  * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
    8268                 :             :  *
    8269                 :             :  * Return the address of the affected column.
    8270                 :             :  */
    8271                 :             : static ObjectAddress
    8272                 :         387 : ATExecColumnDefault(Relation rel, const char *colName,
    8273                 :             :                     Node *newDefault, LOCKMODE lockmode)
    8274                 :             : {
    8275                 :         387 :     TupleDesc   tupdesc = RelationGetDescr(rel);
    8276                 :             :     AttrNumber  attnum;
    8277                 :             :     ObjectAddress address;
    8278                 :             : 
    8279                 :             :     /*
    8280                 :             :      * get the number of the attribute
    8281                 :             :      */
    8282                 :         387 :     attnum = get_attnum(RelationGetRelid(rel), colName);
    8283         [ +  + ]:         387 :     if (attnum == InvalidAttrNumber)
    8284         [ +  - ]:          20 :         ereport(ERROR,
    8285                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8286                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8287                 :             :                         colName, RelationGetRelationName(rel))));
    8288                 :             : 
    8289                 :             :     /* Prevent them from altering a system attribute */
    8290         [ -  + ]:         367 :     if (attnum <= 0)
    8291         [ #  # ]:           0 :         ereport(ERROR,
    8292                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8293                 :             :                  errmsg("cannot alter system column \"%s\"",
    8294                 :             :                         colName)));
    8295                 :             : 
    8296         [ +  + ]:         367 :     if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
    8297   [ +  -  -  + ]:          12 :         ereport(ERROR,
    8298                 :             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    8299                 :             :                  errmsg("column \"%s\" of relation \"%s\" is an identity column",
    8300                 :             :                         colName, RelationGetRelationName(rel)),
    8301                 :             :         /* translator: %s is an SQL ALTER command */
    8302                 :             :                  newDefault ? 0 : errhint("Use %s instead.",
    8303                 :             :                                           "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
    8304                 :             : 
    8305         [ +  + ]:         355 :     if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
    8306   [ +  -  -  +  :           8 :         ereport(ERROR,
                   +  + ]
    8307                 :             :                 (errcode(ERRCODE_SYNTAX_ERROR),
    8308                 :             :                  errmsg("column \"%s\" of relation \"%s\" is a generated column",
    8309                 :             :                         colName, RelationGetRelationName(rel)),
    8310                 :             :                  newDefault ?
    8311                 :             :         /* translator: %s is an SQL ALTER command */
    8312                 :             :                  errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
    8313                 :             :                  (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
    8314                 :             :                   errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
    8315                 :             : 
    8316                 :             :     /*
    8317                 :             :      * Remove any old default for the column.  We use RESTRICT here for
    8318                 :             :      * safety, but at present we do not expect anything to depend on the
    8319                 :             :      * default.
    8320                 :             :      *
    8321                 :             :      * We treat removing the existing default as an internal operation when it
    8322                 :             :      * is preparatory to adding a new default, but as a user-initiated
    8323                 :             :      * operation when the user asked for a drop.
    8324                 :             :      */
    8325                 :         347 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    8326                 :             :                       newDefault != NULL);
    8327                 :             : 
    8328         [ +  + ]:         347 :     if (newDefault)
    8329                 :             :     {
    8330                 :             :         /* SET DEFAULT */
    8331                 :             :         RawColumnDefault *rawEnt;
    8332                 :             : 
    8333                 :         231 :         rawEnt = palloc_object(RawColumnDefault);
    8334                 :         231 :         rawEnt->attnum = attnum;
    8335                 :         231 :         rawEnt->raw_default = newDefault;
    8336                 :         231 :         rawEnt->generated = '\0';
    8337                 :             : 
    8338                 :             :         /*
    8339                 :             :          * This function is intended for CREATE TABLE, so it processes a
    8340                 :             :          * _list_ of defaults, but we just do one.
    8341                 :             :          */
    8342                 :         231 :         AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    8343                 :             :                                   false, true, false, NULL);
    8344                 :             :     }
    8345                 :             : 
    8346                 :         339 :     ObjectAddressSubSet(address, RelationRelationId,
    8347                 :             :                         RelationGetRelid(rel), attnum);
    8348                 :         339 :     return address;
    8349                 :             : }
    8350                 :             : 
    8351                 :             : /*
    8352                 :             :  * Add a pre-cooked default expression.
    8353                 :             :  *
    8354                 :             :  * Return the address of the affected column.
    8355                 :             :  */
    8356                 :             : static ObjectAddress
    8357                 :          57 : ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
    8358                 :             :                           Node *newDefault)
    8359                 :             : {
    8360                 :             :     ObjectAddress address;
    8361                 :             : 
    8362                 :             :     /*
    8363                 :             :      * This is used for a cooked default copied by CREATE TABLE ... LIKE,
    8364                 :             :      * which adds new type dependencies.  Such a default doesn't go through
    8365                 :             :      * AddRelationNewConstraints(), and StoreAttrDefault() leaves the
    8366                 :             :      * privilege checks to its caller, so we must check for USAGE on the types
    8367                 :             :      * here.
    8368                 :             :      */
    8369                 :          57 :     CheckUsageOnTypesInSingleRelExpr(newDefault, RelationGetRelid(rel), GetUserId());
    8370                 :             : 
    8371                 :             :     /*
    8372                 :             :      * Remove any old default for the column.  We use RESTRICT here for
    8373                 :             :      * safety, but at present we do not expect anything to depend on the
    8374                 :             :      * default.  (In ordinary cases, there could not be a default in place
    8375                 :             :      * anyway, but it's possible when combining LIKE with inheritance.)
    8376                 :             :      */
    8377                 :          53 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
    8378                 :             :                       true);
    8379                 :             : 
    8380                 :          53 :     (void) StoreAttrDefault(rel, attnum, newDefault, true);
    8381                 :             : 
    8382                 :          53 :     ObjectAddressSubSet(address, RelationRelationId,
    8383                 :             :                         RelationGetRelid(rel), attnum);
    8384                 :          53 :     return address;
    8385                 :             : }
    8386                 :             : 
    8387                 :             : /*
    8388                 :             :  * ALTER TABLE ALTER COLUMN ADD IDENTITY
    8389                 :             :  *
    8390                 :             :  * Return the address of the affected column.
    8391                 :             :  */
    8392                 :             : static ObjectAddress
    8393                 :         103 : ATExecAddIdentity(Relation rel, const char *colName,
    8394                 :             :                   Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
    8395                 :             : {
    8396                 :             :     Relation    attrelation;
    8397                 :             :     HeapTuple   tuple;
    8398                 :             :     Form_pg_attribute attTup;
    8399                 :             :     AttrNumber  attnum;
    8400                 :             :     ObjectAddress address;
    8401                 :         103 :     ColumnDef  *cdef = castNode(ColumnDef, def);
    8402                 :             :     bool        ispartitioned;
    8403                 :             : 
    8404                 :         103 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8405   [ +  +  +  + ]:         103 :     if (ispartitioned && !recurse)
    8406         [ +  - ]:           4 :         ereport(ERROR,
    8407                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8408                 :             :                  errmsg("cannot add identity to a column of only the partitioned table"),
    8409                 :             :                  errhint("Do not specify the ONLY keyword.")));
    8410                 :             : 
    8411   [ +  +  +  + ]:          99 :     if (rel->rd_rel->relispartition && !recursing)
    8412         [ +  - ]:           8 :         ereport(ERROR,
    8413                 :             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8414                 :             :                 errmsg("cannot add identity to a column of a partition"));
    8415                 :             : 
    8416                 :          91 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8417                 :             : 
    8418                 :          91 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8419         [ -  + ]:          91 :     if (!HeapTupleIsValid(tuple))
    8420         [ #  # ]:           0 :         ereport(ERROR,
    8421                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8422                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8423                 :             :                         colName, RelationGetRelationName(rel))));
    8424                 :          91 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8425                 :          91 :     attnum = attTup->attnum;
    8426                 :             : 
    8427                 :             :     /* Can't alter a system attribute */
    8428         [ -  + ]:          91 :     if (attnum <= 0)
    8429         [ #  # ]:           0 :         ereport(ERROR,
    8430                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8431                 :             :                  errmsg("cannot alter system column \"%s\"",
    8432                 :             :                         colName)));
    8433                 :             : 
    8434                 :             :     /*
    8435                 :             :      * Creating a column as identity implies NOT NULL, so adding the identity
    8436                 :             :      * to an existing column that is not NOT NULL would create a state that
    8437                 :             :      * cannot be reproduced without contortions.
    8438                 :             :      */
    8439         [ +  + ]:          91 :     if (!attTup->attnotnull)
    8440         [ +  - ]:           4 :         ereport(ERROR,
    8441                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8442                 :             :                  errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
    8443                 :             :                         colName, RelationGetRelationName(rel))));
    8444                 :             : 
    8445                 :             :     /*
    8446                 :             :      * On the other hand, if a not-null constraint exists, then verify that
    8447                 :             :      * it's compatible.
    8448                 :             :      */
    8449         [ +  - ]:          87 :     if (attTup->attnotnull)
    8450                 :             :     {
    8451                 :             :         HeapTuple   contup;
    8452                 :             :         Form_pg_constraint conForm;
    8453                 :             : 
    8454                 :          87 :         contup = findNotNullConstraintAttnum(RelationGetRelid(rel),
    8455                 :             :                                              attnum);
    8456         [ -  + ]:          87 :         if (!HeapTupleIsValid(contup))
    8457         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
    8458                 :             :                  colName, RelationGetRelationName(rel));
    8459                 :             : 
    8460                 :          87 :         conForm = (Form_pg_constraint) GETSTRUCT(contup);
    8461         [ +  + ]:          87 :         if (!conForm->convalidated)
    8462         [ +  - ]:           4 :             ereport(ERROR,
    8463                 :             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8464                 :             :                     errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
    8465                 :             :                            NameStr(conForm->conname), RelationGetRelationName(rel)),
    8466                 :             :                     errhint("You might need to validate it using %s.",
    8467                 :             :                             "ALTER TABLE ... VALIDATE CONSTRAINT"));
    8468                 :             :     }
    8469                 :             : 
    8470         [ +  + ]:          83 :     if (attTup->attidentity)
    8471         [ +  - ]:          12 :         ereport(ERROR,
    8472                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8473                 :             :                  errmsg("column \"%s\" of relation \"%s\" is already an identity column",
    8474                 :             :                         colName, RelationGetRelationName(rel))));
    8475                 :             : 
    8476         [ +  + ]:          71 :     if (attTup->atthasdef)
    8477         [ +  - ]:           4 :         ereport(ERROR,
    8478                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8479                 :             :                  errmsg("column \"%s\" of relation \"%s\" already has a default value",
    8480                 :             :                         colName, RelationGetRelationName(rel))));
    8481                 :             : 
    8482                 :          67 :     attTup->attidentity = cdef->identity;
    8483                 :          67 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8484                 :             : 
    8485         [ -  + ]:          67 :     InvokeObjectPostAlterHook(RelationRelationId,
    8486                 :             :                               RelationGetRelid(rel),
    8487                 :             :                               attTup->attnum);
    8488                 :          67 :     ObjectAddressSubSet(address, RelationRelationId,
    8489                 :             :                         RelationGetRelid(rel), attnum);
    8490                 :          67 :     heap_freetuple(tuple);
    8491                 :             : 
    8492                 :          67 :     table_close(attrelation, RowExclusiveLock);
    8493                 :             : 
    8494                 :             :     /*
    8495                 :             :      * Recurse to propagate the identity column to partitions.  Identity is
    8496                 :             :      * not inherited in regular inheritance children.
    8497                 :             :      */
    8498   [ +  -  +  + ]:          67 :     if (recurse && ispartitioned)
    8499                 :             :     {
    8500                 :             :         List       *children;
    8501                 :             :         ListCell   *lc;
    8502                 :             : 
    8503                 :           6 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8504                 :             : 
    8505   [ +  +  +  +  :          10 :         foreach(lc, children)
                   +  + ]
    8506                 :             :         {
    8507                 :             :             Relation    childrel;
    8508                 :             : 
    8509                 :           4 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8510                 :           4 :             ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
    8511                 :           4 :             table_close(childrel, NoLock);
    8512                 :             :         }
    8513                 :             :     }
    8514                 :             : 
    8515                 :          67 :     return address;
    8516                 :             : }
    8517                 :             : 
    8518                 :             : /*
    8519                 :             :  * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
    8520                 :             :  *
    8521                 :             :  * Return the address of the affected column.
    8522                 :             :  */
    8523                 :             : static ObjectAddress
    8524                 :          49 : ATExecSetIdentity(Relation rel, const char *colName, Node *def,
    8525                 :             :                   LOCKMODE lockmode, bool recurse, bool recursing)
    8526                 :             : {
    8527                 :             :     ListCell   *option;
    8528                 :          49 :     DefElem    *generatedEl = NULL;
    8529                 :             :     HeapTuple   tuple;
    8530                 :             :     Form_pg_attribute attTup;
    8531                 :             :     AttrNumber  attnum;
    8532                 :             :     Relation    attrelation;
    8533                 :             :     ObjectAddress address;
    8534                 :             :     bool        ispartitioned;
    8535                 :             : 
    8536                 :          49 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8537   [ +  +  +  + ]:          49 :     if (ispartitioned && !recurse)
    8538         [ +  - ]:           4 :         ereport(ERROR,
    8539                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8540                 :             :                  errmsg("cannot change identity column of only the partitioned table"),
    8541                 :             :                  errhint("Do not specify the ONLY keyword.")));
    8542                 :             : 
    8543   [ +  +  +  + ]:          45 :     if (rel->rd_rel->relispartition && !recursing)
    8544         [ +  - ]:           8 :         ereport(ERROR,
    8545                 :             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8546                 :             :                 errmsg("cannot change identity column of a partition"));
    8547                 :             : 
    8548   [ +  +  +  +  :          66 :     foreach(option, castNode(List, def))
                   +  + ]
    8549                 :             :     {
    8550                 :          29 :         DefElem    *defel = lfirst_node(DefElem, option);
    8551                 :             : 
    8552         [ +  - ]:          29 :         if (strcmp(defel->defname, "generated") == 0)
    8553                 :             :         {
    8554         [ -  + ]:          29 :             if (generatedEl)
    8555         [ #  # ]:           0 :                 ereport(ERROR,
    8556                 :             :                         (errcode(ERRCODE_SYNTAX_ERROR),
    8557                 :             :                          errmsg("conflicting or redundant options")));
    8558                 :          29 :             generatedEl = defel;
    8559                 :             :         }
    8560                 :             :         else
    8561         [ #  # ]:           0 :             elog(ERROR, "option \"%s\" not recognized",
    8562                 :             :                  defel->defname);
    8563                 :             :     }
    8564                 :             : 
    8565                 :             :     /*
    8566                 :             :      * Even if there is nothing to change here, we run all the checks.  There
    8567                 :             :      * will be a subsequent ALTER SEQUENCE that relies on everything being
    8568                 :             :      * there.
    8569                 :             :      */
    8570                 :             : 
    8571                 :          37 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8572                 :          37 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8573         [ -  + ]:          37 :     if (!HeapTupleIsValid(tuple))
    8574         [ #  # ]:           0 :         ereport(ERROR,
    8575                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8576                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8577                 :             :                         colName, RelationGetRelationName(rel))));
    8578                 :             : 
    8579                 :          37 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8580                 :          37 :     attnum = attTup->attnum;
    8581                 :             : 
    8582         [ -  + ]:          37 :     if (attnum <= 0)
    8583         [ #  # ]:           0 :         ereport(ERROR,
    8584                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8585                 :             :                  errmsg("cannot alter system column \"%s\"",
    8586                 :             :                         colName)));
    8587                 :             : 
    8588         [ +  + ]:          37 :     if (!attTup->attidentity)
    8589         [ +  - ]:           4 :         ereport(ERROR,
    8590                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8591                 :             :                  errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8592                 :             :                         colName, RelationGetRelationName(rel))));
    8593                 :             : 
    8594         [ +  + ]:          33 :     if (generatedEl)
    8595                 :             :     {
    8596                 :          29 :         attTup->attidentity = defGetInt32(generatedEl);
    8597                 :          29 :         CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8598                 :             : 
    8599         [ -  + ]:          29 :         InvokeObjectPostAlterHook(RelationRelationId,
    8600                 :             :                                   RelationGetRelid(rel),
    8601                 :             :                                   attTup->attnum);
    8602                 :          29 :         ObjectAddressSubSet(address, RelationRelationId,
    8603                 :             :                             RelationGetRelid(rel), attnum);
    8604                 :             :     }
    8605                 :             :     else
    8606                 :           4 :         address = InvalidObjectAddress;
    8607                 :             : 
    8608                 :          33 :     heap_freetuple(tuple);
    8609                 :          33 :     table_close(attrelation, RowExclusiveLock);
    8610                 :             : 
    8611                 :             :     /*
    8612                 :             :      * Recurse to propagate the identity change to partitions. Identity is not
    8613                 :             :      * inherited in regular inheritance children.
    8614                 :             :      */
    8615   [ +  +  +  -  :          33 :     if (generatedEl && recurse && ispartitioned)
                   +  + ]
    8616                 :             :     {
    8617                 :             :         List       *children;
    8618                 :             :         ListCell   *lc;
    8619                 :             : 
    8620                 :           4 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8621                 :             : 
    8622   [ +  -  +  +  :          12 :         foreach(lc, children)
                   +  + ]
    8623                 :             :         {
    8624                 :             :             Relation    childrel;
    8625                 :             : 
    8626                 :           8 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8627                 :           8 :             ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
    8628                 :           8 :             table_close(childrel, NoLock);
    8629                 :             :         }
    8630                 :             :     }
    8631                 :             : 
    8632                 :          33 :     return address;
    8633                 :             : }
    8634                 :             : 
    8635                 :             : /*
    8636                 :             :  * ALTER TABLE ALTER COLUMN DROP IDENTITY
    8637                 :             :  *
    8638                 :             :  * Return the address of the affected column.
    8639                 :             :  */
    8640                 :             : static ObjectAddress
    8641                 :          61 : ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
    8642                 :             :                    bool recurse, bool recursing)
    8643                 :             : {
    8644                 :             :     HeapTuple   tuple;
    8645                 :             :     Form_pg_attribute attTup;
    8646                 :             :     AttrNumber  attnum;
    8647                 :             :     Relation    attrelation;
    8648                 :             :     ObjectAddress address;
    8649                 :             :     Oid         seqid;
    8650                 :             :     ObjectAddress seqaddress;
    8651                 :             :     bool        ispartitioned;
    8652                 :             : 
    8653                 :          61 :     ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
    8654   [ +  +  +  + ]:          61 :     if (ispartitioned && !recurse)
    8655         [ +  - ]:           4 :         ereport(ERROR,
    8656                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8657                 :             :                  errmsg("cannot drop identity from a column of only the partitioned table"),
    8658                 :             :                  errhint("Do not specify the ONLY keyword.")));
    8659                 :             : 
    8660   [ +  +  +  + ]:          57 :     if (rel->rd_rel->relispartition && !recursing)
    8661         [ +  - ]:           4 :         ereport(ERROR,
    8662                 :             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8663                 :             :                 errmsg("cannot drop identity from a column of a partition"));
    8664                 :             : 
    8665                 :          53 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8666                 :          53 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8667         [ -  + ]:          53 :     if (!HeapTupleIsValid(tuple))
    8668         [ #  # ]:           0 :         ereport(ERROR,
    8669                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8670                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8671                 :             :                         colName, RelationGetRelationName(rel))));
    8672                 :             : 
    8673                 :          53 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8674                 :          53 :     attnum = attTup->attnum;
    8675                 :             : 
    8676         [ -  + ]:          53 :     if (attnum <= 0)
    8677         [ #  # ]:           0 :         ereport(ERROR,
    8678                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8679                 :             :                  errmsg("cannot alter system column \"%s\"",
    8680                 :             :                         colName)));
    8681                 :             : 
    8682         [ +  + ]:          53 :     if (!attTup->attidentity)
    8683                 :             :     {
    8684         [ +  + ]:           8 :         if (!missing_ok)
    8685         [ +  - ]:           4 :             ereport(ERROR,
    8686                 :             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8687                 :             :                      errmsg("column \"%s\" of relation \"%s\" is not an identity column",
    8688                 :             :                             colName, RelationGetRelationName(rel))));
    8689                 :             :         else
    8690                 :             :         {
    8691         [ +  - ]:           4 :             ereport(NOTICE,
    8692                 :             :                     (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
    8693                 :             :                             colName, RelationGetRelationName(rel))));
    8694                 :           4 :             heap_freetuple(tuple);
    8695                 :           4 :             table_close(attrelation, RowExclusiveLock);
    8696                 :           4 :             return InvalidObjectAddress;
    8697                 :             :         }
    8698                 :             :     }
    8699                 :             : 
    8700                 :          45 :     attTup->attidentity = '\0';
    8701                 :          45 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    8702                 :             : 
    8703         [ -  + ]:          45 :     InvokeObjectPostAlterHook(RelationRelationId,
    8704                 :             :                               RelationGetRelid(rel),
    8705                 :             :                               attTup->attnum);
    8706                 :          45 :     ObjectAddressSubSet(address, RelationRelationId,
    8707                 :             :                         RelationGetRelid(rel), attnum);
    8708                 :          45 :     heap_freetuple(tuple);
    8709                 :             : 
    8710                 :          45 :     table_close(attrelation, RowExclusiveLock);
    8711                 :             : 
    8712                 :             :     /*
    8713                 :             :      * Recurse to drop the identity from column in partitions.  Identity is
    8714                 :             :      * not inherited in regular inheritance children so ignore them.
    8715                 :             :      */
    8716   [ +  -  +  + ]:          45 :     if (recurse && ispartitioned)
    8717                 :             :     {
    8718                 :             :         List       *children;
    8719                 :             :         ListCell   *lc;
    8720                 :             : 
    8721                 :           4 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
    8722                 :             : 
    8723   [ +  -  +  +  :           8 :         foreach(lc, children)
                   +  + ]
    8724                 :             :         {
    8725                 :             :             Relation    childrel;
    8726                 :             : 
    8727                 :           4 :             childrel = table_open(lfirst_oid(lc), NoLock);
    8728                 :           4 :             ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
    8729                 :           4 :             table_close(childrel, NoLock);
    8730                 :             :         }
    8731                 :             :     }
    8732                 :             : 
    8733         [ +  + ]:          45 :     if (!recursing)
    8734                 :             :     {
    8735                 :             :         /* drop the internal sequence */
    8736                 :          21 :         seqid = getIdentitySequence(rel, attnum, false);
    8737                 :          21 :         deleteDependencyRecordsForClass(RelationRelationId, seqid,
    8738                 :             :                                         RelationRelationId, DEPENDENCY_INTERNAL);
    8739                 :          21 :         CommandCounterIncrement();
    8740                 :          21 :         seqaddress.classId = RelationRelationId;
    8741                 :          21 :         seqaddress.objectId = seqid;
    8742                 :          21 :         seqaddress.objectSubId = 0;
    8743                 :          21 :         performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
    8744                 :             :     }
    8745                 :             : 
    8746                 :          45 :     return address;
    8747                 :             : }
    8748                 :             : 
    8749                 :             : /*
    8750                 :             :  * ALTER TABLE ALTER COLUMN SET EXPRESSION
    8751                 :             :  *
    8752                 :             :  * Return the address of the affected column.
    8753                 :             :  */
    8754                 :             : static ObjectAddress
    8755                 :         193 : ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
    8756                 :             :                     Node *newExpr, LOCKMODE lockmode)
    8757                 :             : {
    8758                 :             :     HeapTuple   tuple;
    8759                 :             :     Form_pg_attribute attTup;
    8760                 :             :     AttrNumber  attnum;
    8761                 :             :     char        attgenerated;
    8762                 :             :     bool        rewrite;
    8763                 :             :     Oid         attrdefoid;
    8764                 :             :     ObjectAddress address;
    8765                 :             :     Expr       *defval;
    8766                 :             :     NewColumnValue *newval;
    8767                 :             :     RawColumnDefault *rawEnt;
    8768                 :             : 
    8769                 :         193 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    8770         [ -  + ]:         193 :     if (!HeapTupleIsValid(tuple))
    8771         [ #  # ]:           0 :         ereport(ERROR,
    8772                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8773                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8774                 :             :                         colName, RelationGetRelationName(rel))));
    8775                 :             : 
    8776                 :         193 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8777                 :             : 
    8778                 :         193 :     attnum = attTup->attnum;
    8779         [ -  + ]:         193 :     if (attnum <= 0)
    8780         [ #  # ]:           0 :         ereport(ERROR,
    8781                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8782                 :             :                  errmsg("cannot alter system column \"%s\"",
    8783                 :             :                         colName)));
    8784                 :             : 
    8785                 :         193 :     attgenerated = attTup->attgenerated;
    8786         [ +  + ]:         193 :     if (!attgenerated)
    8787         [ +  - ]:           8 :         ereport(ERROR,
    8788                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8789                 :             :                  errmsg("column \"%s\" of relation \"%s\" is not a generated column",
    8790                 :             :                         colName, RelationGetRelationName(rel))));
    8791                 :             : 
    8792   [ +  +  +  + ]:         185 :     if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
    8793                 :          16 :         tab->verify_new_notnull = true;
    8794                 :             : 
    8795                 :             :     /*
    8796                 :             :      * We need to prevent this because a change of expression could affect a
    8797                 :             :      * row filter and inject expressions that are not permitted in a row
    8798                 :             :      * filter.  XXX We could try to have a more precise check to catch only
    8799                 :             :      * publications with row filters, or even re-verify the row filter
    8800                 :             :      * expressions.
    8801                 :             :      */
    8802   [ +  +  +  + ]:         273 :     if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
    8803                 :          88 :         GetRelationIncludedPublications(RelationGetRelid(rel)) != NIL)
    8804         [ +  - ]:           4 :         ereport(ERROR,
    8805                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8806                 :             :                  errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
    8807                 :             :                  errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
    8808                 :             :                            colName, RelationGetRelationName(rel))));
    8809                 :             : 
    8810                 :         181 :     rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
    8811                 :             : 
    8812                 :         181 :     ReleaseSysCache(tuple);
    8813                 :             : 
    8814         [ +  + ]:         181 :     if (rewrite)
    8815                 :             :     {
    8816                 :             :         /*
    8817                 :             :          * Clear all the missing values if we're rewriting the table, since
    8818                 :             :          * this renders them pointless.
    8819                 :             :          */
    8820                 :          97 :         RelationClearMissing(rel);
    8821                 :             : 
    8822                 :             :         /* make sure we don't conflict with later attribute modifications */
    8823                 :          97 :         CommandCounterIncrement();
    8824                 :             :     }
    8825                 :             : 
    8826                 :             :     /*
    8827                 :             :      * Find everything that depends on the column (constraints, indexes, etc),
    8828                 :             :      * and record enough information to let us recreate the objects.
    8829                 :             :      */
    8830                 :         181 :     RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
    8831                 :             : 
    8832                 :             :     /*
    8833                 :             :      * Find whole-row referenced objects that depend on the column
    8834                 :             :      * (constraints, indexes, etc.), and record enough information to let us
    8835                 :             :      * recreate the objects.
    8836                 :             :      */
    8837                 :         181 :     RememberWholeRowDependentForRebuilding(tab, AT_SetExpression, rel);
    8838                 :             : 
    8839                 :             :     /*
    8840                 :             :      * Drop the dependency records of the GENERATED expression, in particular
    8841                 :             :      * its INTERNAL dependency on the column, which would otherwise cause
    8842                 :             :      * dependency.c to refuse to perform the deletion.
    8843                 :             :      */
    8844                 :         181 :     attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    8845         [ -  + ]:         181 :     if (!OidIsValid(attrdefoid))
    8846         [ #  # ]:           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    8847                 :             :              RelationGetRelid(rel), attnum);
    8848                 :         181 :     (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    8849                 :             : 
    8850                 :             :     /* Make above changes visible */
    8851                 :         181 :     CommandCounterIncrement();
    8852                 :             : 
    8853                 :             :     /*
    8854                 :             :      * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    8855                 :             :      * safety, but at present we do not expect anything to depend on the
    8856                 :             :      * expression.
    8857                 :             :      */
    8858                 :         181 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    8859                 :             :                       false, false);
    8860                 :             : 
    8861                 :             :     /* Prepare to store the new expression, in the catalogs */
    8862                 :         181 :     rawEnt = palloc_object(RawColumnDefault);
    8863                 :         181 :     rawEnt->attnum = attnum;
    8864                 :         181 :     rawEnt->raw_default = newExpr;
    8865                 :         181 :     rawEnt->generated = attgenerated;
    8866                 :             : 
    8867                 :             :     /* Store the generated expression */
    8868                 :         181 :     AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
    8869                 :             :                               false, true, false, NULL);
    8870                 :             : 
    8871                 :             :     /* Make above new expression visible */
    8872                 :         181 :     CommandCounterIncrement();
    8873                 :             : 
    8874         [ +  + ]:         181 :     if (rewrite)
    8875                 :             :     {
    8876                 :             :         /* Prepare for table rewrite */
    8877                 :          97 :         defval = (Expr *) build_column_default(rel, attnum);
    8878                 :             : 
    8879                 :          97 :         newval = palloc0_object(NewColumnValue);
    8880                 :          97 :         newval->attnum = attnum;
    8881                 :          97 :         newval->expr = expression_planner(defval);
    8882                 :          97 :         newval->is_generated = true;
    8883                 :             : 
    8884                 :          97 :         tab->newvals = lappend(tab->newvals, newval);
    8885                 :          97 :         tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
    8886                 :             :     }
    8887                 :             : 
    8888                 :             :     /* Drop any pg_statistic entry for the column */
    8889                 :         181 :     RemoveStatistics(RelationGetRelid(rel), attnum);
    8890                 :             : 
    8891         [ -  + ]:         181 :     InvokeObjectPostAlterHook(RelationRelationId,
    8892                 :             :                               RelationGetRelid(rel), attnum);
    8893                 :             : 
    8894                 :         181 :     ObjectAddressSubSet(address, RelationRelationId,
    8895                 :             :                         RelationGetRelid(rel), attnum);
    8896                 :         181 :     return address;
    8897                 :             : }
    8898                 :             : 
    8899                 :             : /*
    8900                 :             :  * ALTER TABLE ALTER COLUMN DROP EXPRESSION
    8901                 :             :  */
    8902                 :             : static void
    8903                 :          97 : ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
    8904                 :             : {
    8905                 :             :     /*
    8906                 :             :      * Reject ONLY if there are child tables -- but only, of course, at the
    8907                 :             :      * top of the tree, otherwise it'd be impossible to run this command with
    8908                 :             :      * trees deeper than two levels.  Caller already got lock.
    8909                 :             :      */
    8910   [ +  +  +  +  :         109 :     if (!recurse && !recursing &&
                   +  - ]
    8911                 :          12 :         find_inheritance_children(RelationGetRelid(rel), NoLock))
    8912         [ +  - ]:          12 :         ereport(ERROR,
    8913                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8914                 :             :                  errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
    8915                 :             : 
    8916                 :             :     /*
    8917                 :             :      * Cannot drop generation expression from inherited columns.
    8918                 :             :      */
    8919         [ +  + ]:          85 :     if (!recursing)
    8920                 :             :     {
    8921                 :             :         HeapTuple   tuple;
    8922                 :             :         Form_pg_attribute attTup;
    8923                 :             : 
    8924                 :          57 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
    8925         [ -  + ]:          57 :         if (!HeapTupleIsValid(tuple))
    8926         [ #  # ]:           0 :             ereport(ERROR,
    8927                 :             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    8928                 :             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    8929                 :             :                             cmd->name, RelationGetRelationName(rel))));
    8930                 :             : 
    8931                 :          57 :         attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8932                 :             : 
    8933         [ +  + ]:          57 :         if (attTup->attinhcount > 0)
    8934         [ +  - ]:          12 :             ereport(ERROR,
    8935                 :             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    8936                 :             :                      errmsg("cannot drop generation expression from inherited column")));
    8937                 :             :     }
    8938                 :          73 : }
    8939                 :             : 
    8940                 :             : /*
    8941                 :             :  * Return the address of the affected column.
    8942                 :             :  */
    8943                 :             : static ObjectAddress
    8944                 :          65 : ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
    8945                 :             : {
    8946                 :             :     HeapTuple   tuple;
    8947                 :             :     Form_pg_attribute attTup;
    8948                 :             :     AttrNumber  attnum;
    8949                 :             :     Relation    attrelation;
    8950                 :             :     Oid         attrdefoid;
    8951                 :             :     ObjectAddress address;
    8952                 :             : 
    8953                 :          65 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    8954                 :          65 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    8955         [ -  + ]:          65 :     if (!HeapTupleIsValid(tuple))
    8956         [ #  # ]:           0 :         ereport(ERROR,
    8957                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    8958                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    8959                 :             :                         colName, RelationGetRelationName(rel))));
    8960                 :             : 
    8961                 :          65 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
    8962                 :          65 :     attnum = attTup->attnum;
    8963                 :             : 
    8964         [ -  + ]:          65 :     if (attnum <= 0)
    8965         [ #  # ]:           0 :         ereport(ERROR,
    8966                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8967                 :             :                  errmsg("cannot alter system column \"%s\"",
    8968                 :             :                         colName)));
    8969                 :             : 
    8970                 :             :     /*
    8971                 :             :      * TODO: This could be done, but it would need a table rewrite to
    8972                 :             :      * materialize the generated values.  Note that for the time being, we
    8973                 :             :      * still error with missing_ok, so that we don't silently leave the column
    8974                 :             :      * as generated.
    8975                 :             :      */
    8976         [ +  + ]:          65 :     if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
    8977         [ +  - ]:           8 :         ereport(ERROR,
    8978                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    8979                 :             :                  errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
    8980                 :             :                  errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
    8981                 :             :                            colName, RelationGetRelationName(rel))));
    8982                 :             : 
    8983         [ +  + ]:          57 :     if (!attTup->attgenerated)
    8984                 :             :     {
    8985         [ +  + ]:          16 :         if (!missing_ok)
    8986         [ +  - ]:           8 :             ereport(ERROR,
    8987                 :             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    8988                 :             :                      errmsg("column \"%s\" of relation \"%s\" is not a generated column",
    8989                 :             :                             colName, RelationGetRelationName(rel))));
    8990                 :             :         else
    8991                 :             :         {
    8992         [ +  - ]:           8 :             ereport(NOTICE,
    8993                 :             :                     (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
    8994                 :             :                             colName, RelationGetRelationName(rel))));
    8995                 :           8 :             heap_freetuple(tuple);
    8996                 :           8 :             table_close(attrelation, RowExclusiveLock);
    8997                 :           8 :             return InvalidObjectAddress;
    8998                 :             :         }
    8999                 :             :     }
    9000                 :             : 
    9001                 :             :     /*
    9002                 :             :      * Mark the column as no longer generated.  (The atthasdef flag needs to
    9003                 :             :      * get cleared too, but RemoveAttrDefault will handle that.)
    9004                 :             :      */
    9005                 :          41 :     attTup->attgenerated = '\0';
    9006                 :          41 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    9007                 :             : 
    9008         [ -  + ]:          41 :     InvokeObjectPostAlterHook(RelationRelationId,
    9009                 :             :                               RelationGetRelid(rel),
    9010                 :             :                               attnum);
    9011                 :          41 :     heap_freetuple(tuple);
    9012                 :             : 
    9013                 :          41 :     table_close(attrelation, RowExclusiveLock);
    9014                 :             : 
    9015                 :             :     /*
    9016                 :             :      * Drop the dependency records of the GENERATED expression, in particular
    9017                 :             :      * its INTERNAL dependency on the column, which would otherwise cause
    9018                 :             :      * dependency.c to refuse to perform the deletion.
    9019                 :             :      */
    9020                 :          41 :     attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
    9021         [ -  + ]:          41 :     if (!OidIsValid(attrdefoid))
    9022         [ #  # ]:           0 :         elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
    9023                 :             :              RelationGetRelid(rel), attnum);
    9024                 :          41 :     (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
    9025                 :             : 
    9026                 :             :     /* Make above changes visible */
    9027                 :          41 :     CommandCounterIncrement();
    9028                 :             : 
    9029                 :             :     /*
    9030                 :             :      * Get rid of the GENERATED expression itself.  We use RESTRICT here for
    9031                 :             :      * safety, but at present we do not expect anything to depend on the
    9032                 :             :      * default.
    9033                 :             :      */
    9034                 :          41 :     RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
    9035                 :             :                       false, false);
    9036                 :             : 
    9037                 :          41 :     ObjectAddressSubSet(address, RelationRelationId,
    9038                 :             :                         RelationGetRelid(rel), attnum);
    9039                 :          41 :     return address;
    9040                 :             : }
    9041                 :             : 
    9042                 :             : /*
    9043                 :             :  * ALTER TABLE ALTER COLUMN SET STATISTICS
    9044                 :             :  *
    9045                 :             :  * Return value is the address of the modified column
    9046                 :             :  */
    9047                 :             : static ObjectAddress
    9048                 :         111 : ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
    9049                 :             : {
    9050                 :         111 :     int         newtarget = 0;
    9051                 :             :     bool        newtarget_default;
    9052                 :             :     Relation    attrelation;
    9053                 :             :     HeapTuple   tuple,
    9054                 :             :                 newtuple;
    9055                 :             :     Form_pg_attribute attrtuple;
    9056                 :             :     AttrNumber  attnum;
    9057                 :             :     ObjectAddress address;
    9058                 :             :     Datum       repl_val[Natts_pg_attribute];
    9059                 :             :     bool        repl_null[Natts_pg_attribute];
    9060                 :             :     bool        repl_repl[Natts_pg_attribute];
    9061                 :             : 
    9062                 :             :     /*
    9063                 :             :      * We allow referencing columns by numbers only for indexes, since table
    9064                 :             :      * column numbers could contain gaps if columns are later dropped.
    9065                 :             :      */
    9066         [ +  + ]:         111 :     if (rel->rd_rel->relkind != RELKIND_INDEX &&
    9067   [ +  -  -  + ]:          69 :         rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
    9068                 :             :         !colName)
    9069         [ #  # ]:           0 :         ereport(ERROR,
    9070                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9071                 :             :                  errmsg("cannot refer to non-index column by number")));
    9072                 :             : 
    9073                 :             :     /* -1 was used in previous versions for the default setting */
    9074   [ +  +  +  + ]:         111 :     if (newValue && intVal(newValue) != -1)
    9075                 :             :     {
    9076                 :          80 :         newtarget = intVal(newValue);
    9077                 :          80 :         newtarget_default = false;
    9078                 :             :     }
    9079                 :             :     else
    9080                 :          31 :         newtarget_default = true;
    9081                 :             : 
    9082         [ +  + ]:         111 :     if (!newtarget_default)
    9083                 :             :     {
    9084                 :             :         /*
    9085                 :             :          * Limit target to a sane range
    9086                 :             :          */
    9087         [ -  + ]:          80 :         if (newtarget < 0)
    9088                 :             :         {
    9089         [ #  # ]:           0 :             ereport(ERROR,
    9090                 :             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    9091                 :             :                      errmsg("statistics target %d is too low",
    9092                 :             :                             newtarget)));
    9093                 :             :         }
    9094         [ -  + ]:          80 :         else if (newtarget > MAX_STATISTICS_TARGET)
    9095                 :             :         {
    9096                 :           0 :             newtarget = MAX_STATISTICS_TARGET;
    9097         [ #  # ]:           0 :             ereport(WARNING,
    9098                 :             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    9099                 :             :                      errmsg("lowering statistics target to %d",
    9100                 :             :                             newtarget)));
    9101                 :             :         }
    9102                 :             :     }
    9103                 :             : 
    9104                 :         111 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    9105                 :             : 
    9106         [ +  + ]:         111 :     if (colName)
    9107                 :             :     {
    9108                 :          69 :         tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    9109                 :             : 
    9110         [ +  + ]:          69 :         if (!HeapTupleIsValid(tuple))
    9111         [ +  - ]:           8 :             ereport(ERROR,
    9112                 :             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    9113                 :             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    9114                 :             :                             colName, RelationGetRelationName(rel))));
    9115                 :             :     }
    9116                 :             :     else
    9117                 :             :     {
    9118                 :          42 :         tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
    9119                 :             : 
    9120         [ +  + ]:          42 :         if (!HeapTupleIsValid(tuple))
    9121         [ +  - ]:           8 :             ereport(ERROR,
    9122                 :             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    9123                 :             :                      errmsg("column number %d of relation \"%s\" does not exist",
    9124                 :             :                             colNum, RelationGetRelationName(rel))));
    9125                 :             :     }
    9126                 :             : 
    9127                 :          95 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9128                 :             : 
    9129                 :          95 :     attnum = attrtuple->attnum;
    9130         [ -  + ]:          95 :     if (attnum <= 0)
    9131         [ #  # ]:           0 :         ereport(ERROR,
    9132                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9133                 :             :                  errmsg("cannot alter system column \"%s\"",
    9134                 :             :                         colName)));
    9135                 :             : 
    9136                 :             :     /*
    9137                 :             :      * Prevent this as long as the ANALYZE code skips virtual generated
    9138                 :             :      * columns.
    9139                 :             :      */
    9140         [ -  + ]:          95 :     if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
    9141         [ #  # ]:           0 :         ereport(ERROR,
    9142                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9143                 :             :                  errmsg("cannot alter statistics on virtual generated column \"%s\"",
    9144                 :             :                         colName)));
    9145                 :             : 
    9146         [ +  + ]:          95 :     if (rel->rd_rel->relkind == RELKIND_INDEX ||
    9147         [ -  + ]:          61 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
    9148                 :             :     {
    9149         [ +  + ]:          34 :         if (attnum > rel->rd_index->indnkeyatts)
    9150         [ +  - ]:           4 :             ereport(ERROR,
    9151                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9152                 :             :                      errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
    9153                 :             :                             NameStr(attrtuple->attname), RelationGetRelationName(rel))));
    9154         [ +  + ]:          30 :         else if (rel->rd_index->indkey.values[attnum - 1] != 0)
    9155         [ +  - ]:          12 :             ereport(ERROR,
    9156                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9157                 :             :                      errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
    9158                 :             :                             NameStr(attrtuple->attname), RelationGetRelationName(rel)),
    9159                 :             :                      errhint("Alter statistics on table column instead.")));
    9160                 :             :     }
    9161                 :             : 
    9162                 :             :     /* Build new tuple. */
    9163                 :          79 :     memset(repl_null, false, sizeof(repl_null));
    9164                 :          79 :     memset(repl_repl, false, sizeof(repl_repl));
    9165         [ +  + ]:          79 :     if (!newtarget_default)
    9166                 :          48 :         repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
    9167                 :             :     else
    9168                 :          31 :         repl_null[Anum_pg_attribute_attstattarget - 1] = true;
    9169                 :          79 :     repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
    9170                 :          79 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    9171                 :             :                                  repl_val, repl_null, repl_repl);
    9172                 :          79 :     CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
    9173                 :             : 
    9174         [ -  + ]:          79 :     InvokeObjectPostAlterHook(RelationRelationId,
    9175                 :             :                               RelationGetRelid(rel),
    9176                 :             :                               attrtuple->attnum);
    9177                 :          79 :     ObjectAddressSubSet(address, RelationRelationId,
    9178                 :             :                         RelationGetRelid(rel), attnum);
    9179                 :             : 
    9180                 :          79 :     heap_freetuple(newtuple);
    9181                 :             : 
    9182                 :          79 :     ReleaseSysCache(tuple);
    9183                 :             : 
    9184                 :          79 :     table_close(attrelation, RowExclusiveLock);
    9185                 :             : 
    9186                 :          79 :     return address;
    9187                 :             : }
    9188                 :             : 
    9189                 :             : /*
    9190                 :             :  * Return value is the address of the modified column
    9191                 :             :  */
    9192                 :             : static ObjectAddress
    9193                 :          21 : ATExecSetOptions(Relation rel, const char *colName, Node *options,
    9194                 :             :                  bool isReset, LOCKMODE lockmode)
    9195                 :             : {
    9196                 :             :     Relation    attrelation;
    9197                 :             :     HeapTuple   tuple,
    9198                 :             :                 newtuple;
    9199                 :             :     Form_pg_attribute attrtuple;
    9200                 :             :     AttrNumber  attnum;
    9201                 :             :     Datum       datum,
    9202                 :             :                 newOptions;
    9203                 :             :     bool        isnull;
    9204                 :             :     ObjectAddress address;
    9205                 :             :     Datum       repl_val[Natts_pg_attribute];
    9206                 :             :     bool        repl_null[Natts_pg_attribute];
    9207                 :             :     bool        repl_repl[Natts_pg_attribute];
    9208                 :             : 
    9209                 :          21 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    9210                 :             : 
    9211                 :          21 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    9212                 :             : 
    9213         [ -  + ]:          21 :     if (!HeapTupleIsValid(tuple))
    9214         [ #  # ]:           0 :         ereport(ERROR,
    9215                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    9216                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    9217                 :             :                         colName, RelationGetRelationName(rel))));
    9218                 :          21 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9219                 :             : 
    9220                 :          21 :     attnum = attrtuple->attnum;
    9221         [ -  + ]:          21 :     if (attnum <= 0)
    9222         [ #  # ]:           0 :         ereport(ERROR,
    9223                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9224                 :             :                  errmsg("cannot alter system column \"%s\"",
    9225                 :             :                         colName)));
    9226                 :             : 
    9227                 :             :     /* Generate new proposed attoptions (text array) */
    9228                 :          21 :     datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
    9229                 :             :                             &isnull);
    9230         [ +  + ]:          21 :     newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
    9231                 :             :                                      castNode(List, options), NULL, NULL,
    9232                 :             :                                      false, isReset);
    9233                 :             :     /* Validate new options */
    9234                 :          21 :     (void) attribute_reloptions(newOptions, true);
    9235                 :             : 
    9236                 :             :     /* Build new tuple. */
    9237                 :          21 :     memset(repl_null, false, sizeof(repl_null));
    9238                 :          21 :     memset(repl_repl, false, sizeof(repl_repl));
    9239         [ +  - ]:          21 :     if (newOptions != (Datum) 0)
    9240                 :          21 :         repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
    9241                 :             :     else
    9242                 :           0 :         repl_null[Anum_pg_attribute_attoptions - 1] = true;
    9243                 :          21 :     repl_repl[Anum_pg_attribute_attoptions - 1] = true;
    9244                 :          21 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
    9245                 :             :                                  repl_val, repl_null, repl_repl);
    9246                 :             : 
    9247                 :             :     /* Update system catalog. */
    9248                 :          21 :     CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
    9249                 :             : 
    9250         [ -  + ]:          21 :     InvokeObjectPostAlterHook(RelationRelationId,
    9251                 :             :                               RelationGetRelid(rel),
    9252                 :             :                               attrtuple->attnum);
    9253                 :          21 :     ObjectAddressSubSet(address, RelationRelationId,
    9254                 :             :                         RelationGetRelid(rel), attnum);
    9255                 :             : 
    9256                 :          21 :     heap_freetuple(newtuple);
    9257                 :             : 
    9258                 :          21 :     ReleaseSysCache(tuple);
    9259                 :             : 
    9260                 :          21 :     table_close(attrelation, RowExclusiveLock);
    9261                 :             : 
    9262                 :          21 :     return address;
    9263                 :             : }
    9264                 :             : 
    9265                 :             : /*
    9266                 :             :  * Helper function for ATExecSetStorage and ATExecSetCompression
    9267                 :             :  *
    9268                 :             :  * Set the attstorage and/or attcompression fields for index columns
    9269                 :             :  * associated with the specified table column.
    9270                 :             :  */
    9271                 :             : static void
    9272                 :         218 : SetIndexStorageProperties(Relation rel, Relation attrelation,
    9273                 :             :                           AttrNumber attnum,
    9274                 :             :                           bool setstorage, char newstorage,
    9275                 :             :                           bool setcompression, char newcompression,
    9276                 :             :                           LOCKMODE lockmode)
    9277                 :             : {
    9278                 :             :     ListCell   *lc;
    9279                 :             : 
    9280   [ +  +  +  +  :         276 :     foreach(lc, RelationGetIndexList(rel))
                   +  + ]
    9281                 :             :     {
    9282                 :          58 :         Oid         indexoid = lfirst_oid(lc);
    9283                 :             :         Relation    indrel;
    9284                 :          58 :         AttrNumber  indattnum = 0;
    9285                 :             :         HeapTuple   tuple;
    9286                 :             : 
    9287                 :          58 :         indrel = index_open(indexoid, lockmode);
    9288                 :             : 
    9289         [ +  + ]:          97 :         for (int i = 0; i < indrel->rd_index->indnatts; i++)
    9290                 :             :         {
    9291         [ +  + ]:          62 :             if (indrel->rd_index->indkey.values[i] == attnum)
    9292                 :             :             {
    9293                 :          23 :                 indattnum = i + 1;
    9294                 :          23 :                 break;
    9295                 :             :             }
    9296                 :             :         }
    9297                 :             : 
    9298         [ +  + ]:          58 :         if (indattnum == 0)
    9299                 :             :         {
    9300                 :          35 :             index_close(indrel, lockmode);
    9301                 :          35 :             continue;
    9302                 :             :         }
    9303                 :             : 
    9304                 :          23 :         tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
    9305                 :             : 
    9306         [ +  - ]:          23 :         if (HeapTupleIsValid(tuple))
    9307                 :             :         {
    9308                 :          23 :             Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9309                 :             : 
    9310         [ +  + ]:          23 :             if (setstorage)
    9311                 :          15 :                 attrtuple->attstorage = newstorage;
    9312                 :             : 
    9313         [ +  + ]:          23 :             if (setcompression)
    9314                 :           8 :                 attrtuple->attcompression = newcompression;
    9315                 :             : 
    9316                 :          23 :             CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    9317                 :             : 
    9318         [ -  + ]:          23 :             InvokeObjectPostAlterHook(RelationRelationId,
    9319                 :             :                                       RelationGetRelid(rel),
    9320                 :             :                                       attrtuple->attnum);
    9321                 :             : 
    9322                 :          23 :             heap_freetuple(tuple);
    9323                 :             :         }
    9324                 :             : 
    9325                 :          23 :         index_close(indrel, lockmode);
    9326                 :             :     }
    9327                 :         218 : }
    9328                 :             : 
    9329                 :             : /*
    9330                 :             :  * ALTER TABLE ALTER COLUMN SET STORAGE
    9331                 :             :  *
    9332                 :             :  * Return value is the address of the modified column
    9333                 :             :  */
    9334                 :             : static ObjectAddress
    9335                 :         182 : ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
    9336                 :             : {
    9337                 :             :     Relation    attrelation;
    9338                 :             :     HeapTuple   tuple;
    9339                 :             :     Form_pg_attribute attrtuple;
    9340                 :             :     AttrNumber  attnum;
    9341                 :             :     ObjectAddress address;
    9342                 :             : 
    9343                 :         182 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
    9344                 :             : 
    9345                 :         182 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
    9346                 :             : 
    9347         [ +  + ]:         182 :     if (!HeapTupleIsValid(tuple))
    9348         [ +  - ]:           8 :         ereport(ERROR,
    9349                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    9350                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    9351                 :             :                         colName, RelationGetRelationName(rel))));
    9352                 :         174 :     attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
    9353                 :             : 
    9354                 :         174 :     attnum = attrtuple->attnum;
    9355         [ -  + ]:         174 :     if (attnum <= 0)
    9356         [ #  # ]:           0 :         ereport(ERROR,
    9357                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9358                 :             :                  errmsg("cannot alter system column \"%s\"",
    9359                 :             :                         colName)));
    9360                 :             : 
    9361                 :         174 :     attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
    9362                 :             : 
    9363                 :         174 :     CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
    9364                 :             : 
    9365         [ -  + ]:         174 :     InvokeObjectPostAlterHook(RelationRelationId,
    9366                 :             :                               RelationGetRelid(rel),
    9367                 :             :                               attrtuple->attnum);
    9368                 :             : 
    9369                 :             :     /*
    9370                 :             :      * Apply the change to indexes as well (only for simple index columns,
    9371                 :             :      * matching behavior of index.c ConstructTupleDescriptor()).
    9372                 :             :      */
    9373                 :         174 :     SetIndexStorageProperties(rel, attrelation, attnum,
    9374                 :         174 :                               true, attrtuple->attstorage,
    9375                 :             :                               false, 0,
    9376                 :             :                               lockmode);
    9377                 :             : 
    9378                 :         174 :     heap_freetuple(tuple);
    9379                 :             : 
    9380                 :         174 :     table_close(attrelation, RowExclusiveLock);
    9381                 :             : 
    9382                 :         174 :     ObjectAddressSubSet(address, RelationRelationId,
    9383                 :             :                         RelationGetRelid(rel), attnum);
    9384                 :         174 :     return address;
    9385                 :             : }
    9386                 :             : 
    9387                 :             : 
    9388                 :             : /*
    9389                 :             :  * ALTER TABLE DROP COLUMN
    9390                 :             :  *
    9391                 :             :  * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
    9392                 :             :  * because we have to decide at runtime whether to recurse or not depending
    9393                 :             :  * on whether attinhcount goes to zero or not.  (We can't check this in a
    9394                 :             :  * static pre-pass because it won't handle multiple inheritance situations
    9395                 :             :  * correctly.)
    9396                 :             :  */
    9397                 :             : static void
    9398                 :        1143 : ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
    9399                 :             :                  AlterTableCmd *cmd, LOCKMODE lockmode,
    9400                 :             :                  AlterTableUtilityContext *context)
    9401                 :             : {
    9402   [ +  +  +  + ]:        1143 :     if (rel->rd_rel->reloftype && !recursing)
    9403         [ +  - ]:           4 :         ereport(ERROR,
    9404                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    9405                 :             :                  errmsg("cannot drop column from typed table")));
    9406                 :             : 
    9407         [ +  + ]:        1139 :     if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
    9408                 :          54 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
    9409                 :             : 
    9410         [ +  + ]:        1135 :     if (recurse)
    9411                 :         977 :         cmd->recurse = true;
    9412                 :        1135 : }
    9413                 :             : 
    9414                 :             : /*
    9415                 :             :  * Drops column 'colName' from relation 'rel' and returns the address of the
    9416                 :             :  * dropped column.  The column is also dropped (or marked as no longer
    9417                 :             :  * inherited from relation) from the relation's inheritance children, if any.
    9418                 :             :  *
    9419                 :             :  * In the recursive invocations for inheritance child relations, instead of
    9420                 :             :  * dropping the column directly (if to be dropped at all), its object address
    9421                 :             :  * is added to 'addrs', which must be non-NULL in such invocations.  All
    9422                 :             :  * columns are dropped at the same time after all the children have been
    9423                 :             :  * checked recursively.
    9424                 :             :  */
    9425                 :             : static ObjectAddress
    9426                 :        1508 : ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
    9427                 :             :                  DropBehavior behavior,
    9428                 :             :                  bool recurse, bool recursing,
    9429                 :             :                  bool missing_ok, LOCKMODE lockmode,
    9430                 :             :                  ObjectAddresses *addrs)
    9431                 :             : {
    9432                 :             :     HeapTuple   tuple;
    9433                 :             :     Form_pg_attribute targetatt;
    9434                 :             :     AttrNumber  attnum;
    9435                 :             :     List       *children;
    9436                 :             :     ObjectAddress object;
    9437                 :             :     bool        is_expr;
    9438                 :             : 
    9439                 :             :     /* At top level, permission check was done in ATPrepCmd, else do it */
    9440         [ +  + ]:        1508 :     if (recursing)
    9441                 :         373 :         ATSimplePermissions(AT_DropColumn, rel,
    9442                 :             :                             ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
    9443                 :             : 
    9444                 :             :     /* Initialize addrs on the first invocation */
    9445                 :             :     Assert(!recursing || addrs != NULL);
    9446                 :             : 
    9447                 :             :     /* since this function recurses, it could be driven to stack overflow */
    9448                 :        1508 :     check_stack_depth();
    9449                 :             : 
    9450         [ +  + ]:        1508 :     if (!recursing)
    9451                 :        1135 :         addrs = new_object_addresses();
    9452                 :             : 
    9453                 :             :     /*
    9454                 :             :      * get the number of the attribute
    9455                 :             :      */
    9456                 :        1508 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
    9457         [ +  + ]:        1508 :     if (!HeapTupleIsValid(tuple))
    9458                 :             :     {
    9459         [ +  + ]:          40 :         if (!missing_ok)
    9460                 :             :         {
    9461         [ +  - ]:          24 :             ereport(ERROR,
    9462                 :             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
    9463                 :             :                      errmsg("column \"%s\" of relation \"%s\" does not exist",
    9464                 :             :                             colName, RelationGetRelationName(rel))));
    9465                 :             :         }
    9466                 :             :         else
    9467                 :             :         {
    9468         [ +  - ]:          16 :             ereport(NOTICE,
    9469                 :             :                     (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
    9470                 :             :                             colName, RelationGetRelationName(rel))));
    9471                 :          16 :             return InvalidObjectAddress;
    9472                 :             :         }
    9473                 :             :     }
    9474                 :        1468 :     targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
    9475                 :             : 
    9476                 :        1468 :     attnum = targetatt->attnum;
    9477                 :             : 
    9478                 :             :     /* Can't drop a system attribute */
    9479         [ +  + ]:        1468 :     if (attnum <= 0)
    9480         [ +  - ]:           4 :         ereport(ERROR,
    9481                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9482                 :             :                  errmsg("cannot drop system column \"%s\"",
    9483                 :             :                         colName)));
    9484                 :             : 
    9485                 :             :     /*
    9486                 :             :      * Don't drop inherited columns, unless recursing (presumably from a drop
    9487                 :             :      * of the parent column)
    9488                 :             :      */
    9489   [ +  +  +  + ]:        1464 :     if (targetatt->attinhcount > 0 && !recursing)
    9490         [ +  - ]:          32 :         ereport(ERROR,
    9491                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9492                 :             :                  errmsg("cannot drop inherited column \"%s\"",
    9493                 :             :                         colName)));
    9494                 :             : 
    9495                 :             :     /*
    9496                 :             :      * Don't drop columns used in the partition key, either.  (If we let this
    9497                 :             :      * go through, the key column's dependencies would cause a cascaded drop
    9498                 :             :      * of the whole table, which is surely not what the user expected.)
    9499                 :             :      */
    9500         [ +  + ]:        1432 :     if (has_partition_attrs(rel,
    9501                 :             :                             bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
    9502                 :             :                             &is_expr))
    9503         [ +  - ]:          20 :         ereport(ERROR,
    9504                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9505                 :             :                  errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
    9506                 :             :                         colName, RelationGetRelationName(rel))));
    9507                 :             : 
    9508                 :        1412 :     ReleaseSysCache(tuple);
    9509                 :             : 
    9510                 :             :     /*
    9511                 :             :      * Propagate to children as appropriate.  Unlike most other ALTER
    9512                 :             :      * routines, we have to do this one level of recursion at a time; we can't
    9513                 :             :      * use find_all_inheritors to do it in one pass.
    9514                 :             :      */
    9515                 :             :     children =
    9516                 :        1412 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
    9517                 :             : 
    9518         [ +  + ]:        1412 :     if (children)
    9519                 :             :     {
    9520                 :             :         Relation    attr_rel;
    9521                 :             :         ListCell   *child;
    9522                 :             : 
    9523                 :             :         /*
    9524                 :             :          * In case of a partitioned table, the column must be dropped from the
    9525                 :             :          * partitions as well.
    9526                 :             :          */
    9527   [ +  +  +  + ]:         204 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
    9528         [ +  - ]:           4 :             ereport(ERROR,
    9529                 :             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
    9530                 :             :                      errmsg("cannot drop column from only the partitioned table when partitions exist"),
    9531                 :             :                      errhint("Do not specify the ONLY keyword.")));
    9532                 :             : 
    9533                 :         200 :         attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
    9534   [ +  -  +  +  :         593 :         foreach(child, children)
                   +  + ]
    9535                 :             :         {
    9536                 :         397 :             Oid         childrelid = lfirst_oid(child);
    9537                 :             :             Relation    childrel;
    9538                 :             :             Form_pg_attribute childatt;
    9539                 :             : 
    9540                 :             :             /* find_inheritance_children already got lock */
    9541                 :         397 :             childrel = table_open(childrelid, NoLock);
    9542                 :         397 :             CheckAlterTableIsSafe(childrel);
    9543                 :             : 
    9544                 :         397 :             tuple = SearchSysCacheCopyAttName(childrelid, colName);
    9545         [ -  + ]:         397 :             if (!HeapTupleIsValid(tuple))   /* shouldn't happen */
    9546         [ #  # ]:           0 :                 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
    9547                 :             :                      colName, childrelid);
    9548                 :         397 :             childatt = (Form_pg_attribute) GETSTRUCT(tuple);
    9549                 :             : 
    9550         [ -  + ]:         397 :             if (childatt->attinhcount <= 0) /* shouldn't happen */
    9551         [ #  # ]:           0 :                 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
    9552                 :             :                      childrelid, colName);
    9553                 :             : 
    9554         [ +  + ]:         397 :             if (recurse)
    9555                 :             :             {
    9556                 :             :                 /*
    9557                 :             :                  * If the child column has other definition sources, just
    9558                 :             :                  * decrement its inheritance count; if not, recurse to delete
    9559                 :             :                  * it.
    9560                 :             :                  */
    9561   [ +  -  +  + ]:         381 :                 if (childatt->attinhcount == 1 && !childatt->attislocal)
    9562                 :             :                 {
    9563                 :             :                     /* Time to delete this child column, too */
    9564                 :         373 :                     ATExecDropColumn(wqueue, childrel, colName,
    9565                 :             :                                      behavior, true, true,
    9566                 :             :                                      false, lockmode, addrs);
    9567                 :             :                 }
    9568                 :             :                 else
    9569                 :             :                 {
    9570                 :             :                     /* Child column must survive my deletion */
    9571                 :           8 :                     childatt->attinhcount--;
    9572                 :             : 
    9573                 :           8 :                     CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    9574                 :             : 
    9575                 :             :                     /* Make update visible */
    9576                 :           8 :                     CommandCounterIncrement();
    9577                 :             :                 }
    9578                 :             :             }
    9579                 :             :             else
    9580                 :             :             {
    9581                 :             :                 /*
    9582                 :             :                  * If we were told to drop ONLY in this table (no recursion),
    9583                 :             :                  * we need to mark the inheritors' attributes as locally
    9584                 :             :                  * defined rather than inherited.
    9585                 :             :                  */
    9586                 :          16 :                 childatt->attinhcount--;
    9587                 :          16 :                 childatt->attislocal = true;
    9588                 :             : 
    9589                 :          16 :                 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
    9590                 :             : 
    9591                 :             :                 /* Make update visible */
    9592                 :          16 :                 CommandCounterIncrement();
    9593                 :             :             }
    9594                 :             : 
    9595                 :         393 :             heap_freetuple(tuple);
    9596                 :             : 
    9597                 :         393 :             table_close(childrel, NoLock);
    9598                 :             :         }
    9599                 :         196 :         table_close(attr_rel, RowExclusiveLock);
    9600                 :             :     }
    9601                 :             : 
    9602                 :             :     /* Add object to delete */
    9603                 :        1404 :     object.classId = RelationRelationId;
    9604                 :        1404 :     object.objectId = RelationGetRelid(rel);
    9605                 :        1404 :     object.objectSubId = attnum;
    9606                 :        1404 :     add_exact_object_address(&object, addrs);
    9607                 :             : 
    9608         [ +  + ]:        1404 :     if (!recursing)
    9609                 :             :     {
    9610                 :             :         /* Recursion has ended, drop everything that was collected */
    9611                 :        1035 :         performMultipleDeletions(addrs, behavior, 0);
    9612                 :         991 :         free_object_addresses(addrs);
    9613                 :             :     }
    9614                 :             : 
    9615                 :        1360 :     return object;
    9616                 :             : }
    9617                 :             : 
    9618                 :             : /*
    9619                 :             :  * Prepare to add a primary key on a table, by adding not-null constraints
    9620                 :             :  * on all columns.
    9621                 :             :  *
    9622                 :             :  * The not-null constraints for a primary key must cover the whole inheritance
    9623                 :             :  * hierarchy (failing to ensure that leads to funny corner cases).  For the
    9624                 :             :  * normal case where we're asked to recurse, this routine checks if the
    9625                 :             :  * not-null constraints exist already, and if not queues a requirement for
    9626                 :             :  * them to be created by phase 2.
    9627                 :             :  *
    9628                 :             :  * For the case where we're asked not to recurse, we verify that a not-null
    9629                 :             :  * constraint exists on each column of each (direct) child table, throwing an
    9630                 :             :  * error if not.  Not throwing an error would also work, because a not-null
    9631                 :             :  * constraint would be created anyway, but it'd cause a silent scan of the
    9632                 :             :  * child table to verify absence of nulls.  We prefer to let the user know so
    9633                 :             :  * that they can add the constraint manually without having to hold
    9634                 :             :  * AccessExclusiveLock while at it.
    9635                 :             :  *
    9636                 :             :  * However, it's also important that we do not acquire locks on children if
    9637                 :             :  * the not-null constraints already exist on the parent, to avoid risking
    9638                 :             :  * deadlocks during parallel pg_restore of PKs on partitioned tables.
    9639                 :             :  */
    9640                 :             : static void
    9641                 :       10446 : ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
    9642                 :             :                     bool recurse, LOCKMODE lockmode,
    9643                 :             :                     AlterTableUtilityContext *context)
    9644                 :             : {
    9645                 :             :     Constraint *pkconstr;
    9646                 :       10446 :     List       *children = NIL;
    9647                 :       10446 :     bool        got_children = false;
    9648                 :             : 
    9649                 :       10446 :     pkconstr = castNode(Constraint, cmd->def);
    9650         [ +  + ]:       10446 :     if (pkconstr->contype != CONSTR_PRIMARY)
    9651                 :        6185 :         return;
    9652                 :             : 
    9653                 :             :     /* Verify that columns are not-null, or request that they be made so */
    9654   [ +  +  +  +  :        9139 :     foreach_node(String, column, pkconstr->keys)
                   +  + ]
    9655                 :             :     {
    9656                 :             :         AlterTableCmd *newcmd;
    9657                 :             :         Constraint *nnconstr;
    9658                 :             :         HeapTuple   tuple;
    9659                 :             : 
    9660                 :             :         /*
    9661                 :             :          * First check if a suitable constraint exists.  If it does, we don't
    9662                 :             :          * need to request another one.  We do need to bail out if it's not
    9663                 :             :          * valid, though.
    9664                 :             :          */
    9665                 :         657 :         tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
    9666         [ +  + ]:         657 :         if (tuple != NULL)
    9667                 :             :         {
    9668                 :         322 :             verifyNotNullPKCompatible(tuple, strVal(column));
    9669                 :             : 
    9670                 :             :             /* All good with this one; don't request another */
    9671                 :         314 :             heap_freetuple(tuple);
    9672                 :         314 :             continue;
    9673                 :             :         }
    9674         [ +  + ]:         335 :         else if (!recurse)
    9675                 :             :         {
    9676                 :             :             /*
    9677                 :             :              * No constraint on this column.  Asked not to recurse, we won't
    9678                 :             :              * create one here, but verify that all children have one.
    9679                 :             :              */
    9680         [ +  - ]:          24 :             if (!got_children)
    9681                 :             :             {
    9682                 :          24 :                 children = find_inheritance_children(RelationGetRelid(rel),
    9683                 :             :                                                      lockmode);
    9684                 :             :                 /* only search for children on the first time through */
    9685                 :          24 :                 got_children = true;
    9686                 :             :             }
    9687                 :             : 
    9688   [ +  -  +  +  :          48 :             foreach_oid(childrelid, children)
                   +  + ]
    9689                 :             :             {
    9690                 :             :                 HeapTuple   tup;
    9691                 :             : 
    9692                 :          24 :                 tup = findNotNullConstraint(childrelid, strVal(column));
    9693         [ +  + ]:          24 :                 if (!tup)
    9694         [ +  - ]:           4 :                     ereport(ERROR,
    9695                 :             :                             errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
    9696                 :             :                                    strVal(column), get_rel_name(childrelid)));
    9697                 :             :                 /* verify it's good enough */
    9698                 :          20 :                 verifyNotNullPKCompatible(tup, strVal(column));
    9699                 :             :             }
    9700                 :             :         }
    9701                 :             : 
    9702                 :             :         /* This column is not already not-null, so add it to the queue */
    9703                 :         323 :         nnconstr = makeNotNullConstraint(column);
    9704                 :             : 
    9705                 :         323 :         newcmd = makeNode(AlterTableCmd);
    9706                 :         323 :         newcmd->subtype = AT_AddConstraint;
    9707                 :             :         /* note we force recurse=true here; see above */
    9708                 :         323 :         newcmd->recurse = true;
    9709                 :         323 :         newcmd->def = (Node *) nnconstr;
    9710                 :             : 
    9711                 :         323 :         ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
    9712                 :             :     }
    9713                 :             : }
    9714                 :             : 
    9715                 :             : /*
    9716                 :             :  * Verify whether the given not-null constraint is compatible with a
    9717                 :             :  * primary key.  If not, an error is thrown.
    9718                 :             :  */
    9719                 :             : static void
    9720                 :         342 : verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
    9721                 :             : {
    9722                 :         342 :     Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
    9723                 :             : 
    9724         [ -  + ]:         342 :     if (conForm->contype != CONSTRAINT_NOTNULL)
    9725         [ #  # ]:           0 :         elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
    9726                 :             : 
    9727                 :             :     /* a NO INHERIT constraint is no good */
    9728         [ +  + ]:         342 :     if (conForm->connoinherit)
    9729         [ +  - ]:           8 :         ereport(ERROR,
    9730                 :             :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    9731                 :             :                 errmsg("cannot create primary key on column \"%s\"", colname),
    9732                 :             :         /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
    9733                 :             :                 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
    9734                 :             :                           NameStr(conForm->conname), colname,
    9735                 :             :                           get_rel_name(conForm->conrelid), "NO INHERIT"),
    9736                 :             :                 errhint("You might need to make the existing constraint inheritable using %s.",
    9737                 :             :                         "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
    9738                 :             : 
    9739                 :             :     /* an unvalidated constraint is no good */
    9740         [ +  + ]:         334 :     if (!conForm->convalidated)
    9741         [ +  - ]:           8 :         ereport(ERROR,
    9742                 :             :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
    9743                 :             :                 errmsg("cannot create primary key on column \"%s\"", colname),
    9744                 :             :         /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
    9745                 :             :                 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
    9746                 :             :                           NameStr(conForm->conname), colname,
    9747                 :             :                           get_rel_name(conForm->conrelid), "NOT VALID"),
    9748                 :             :                 errhint("You might need to validate it using %s.",
    9749                 :             :                         "ALTER TABLE ... VALIDATE CONSTRAINT"));
    9750                 :         326 : }
    9751                 :             : 
    9752                 :             : /*
    9753                 :             :  * ALTER TABLE ADD INDEX
    9754                 :             :  *
    9755                 :             :  * There is no such command in the grammar, but parse_utilcmd.c converts
    9756                 :             :  * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands.  This lets
    9757                 :             :  * us schedule creation of the index at the appropriate time during ALTER.
    9758                 :             :  *
    9759                 :             :  * Return value is the address of the new index.
    9760                 :             :  */
    9761                 :             : static ObjectAddress
    9762                 :        1108 : ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
    9763                 :             :                IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9764                 :             : {
    9765                 :             :     bool        check_rights;
    9766                 :             :     bool        skip_build;
    9767                 :             :     bool        quiet;
    9768                 :             :     ObjectAddress address;
    9769                 :             : 
    9770                 :             :     Assert(IsA(stmt, IndexStmt));
    9771                 :             :     Assert(!stmt->concurrent);
    9772                 :             : 
    9773                 :             :     /* The IndexStmt has already been through transformIndexStmt */
    9774                 :             :     Assert(stmt->transformed);
    9775                 :             : 
    9776                 :             :     /* suppress schema rights check when rebuilding existing index */
    9777                 :        1108 :     check_rights = !is_rebuild;
    9778                 :             :     /* skip index build if phase 3 will do it or we're reusing an old one */
    9779   [ +  +  +  + ]:        1108 :     skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
    9780                 :             :     /* suppress notices when rebuilding existing index */
    9781                 :        1108 :     quiet = is_rebuild;
    9782                 :             : 
    9783                 :        1108 :     address = DefineIndex(NULL,
    9784                 :             :                           RelationGetRelid(rel),
    9785                 :             :                           stmt,
    9786                 :             :                           InvalidOid,   /* no predefined OID */
    9787                 :             :                           InvalidOid,   /* no parent index */
    9788                 :             :                           InvalidOid,   /* no parent constraint */
    9789                 :             :                           -1,   /* total_parts unknown */
    9790                 :             :                           true, /* is_alter_table */
    9791                 :             :                           check_rights,
    9792                 :             :                           false,    /* check_not_in_use - we did it already */
    9793                 :             :                           skip_build,
    9794                 :             :                           quiet);
    9795                 :             : 
    9796                 :             :     /*
    9797                 :             :      * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
    9798                 :             :      * new index instead of building from scratch.  Restore associated fields.
    9799                 :             :      * This may store InvalidSubTransactionId in both fields, in which case
    9800                 :             :      * relcache.c will assume it can rebuild the relcache entry.  Hence, do
    9801                 :             :      * this after the CCI that made catalog rows visible to any rebuild.  The
    9802                 :             :      * DROP of the old edition of this index will have scheduled the storage
    9803                 :             :      * for deletion at commit, so cancel that pending deletion.
    9804                 :             :      */
    9805         [ +  + ]:         995 :     if (RelFileNumberIsValid(stmt->oldNumber))
    9806                 :             :     {
    9807                 :          49 :         Relation    irel = index_open(address.objectId, NoLock);
    9808                 :             : 
    9809                 :          49 :         irel->rd_createSubid = stmt->oldCreateSubid;
    9810                 :          49 :         irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
    9811                 :          49 :         RelationPreserveStorage(irel->rd_locator, true);
    9812                 :          49 :         index_close(irel, NoLock);
    9813                 :             :     }
    9814                 :             : 
    9815                 :         995 :     return address;
    9816                 :             : }
    9817                 :             : 
    9818                 :             : /*
    9819                 :             :  * ALTER TABLE ADD STATISTICS
    9820                 :             :  *
    9821                 :             :  * This is no such command in the grammar, but we use this internally to add
    9822                 :             :  * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
    9823                 :             :  * column type change.
    9824                 :             :  */
    9825                 :             : static ObjectAddress
    9826                 :          57 : ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
    9827                 :             :                     CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
    9828                 :             : {
    9829                 :             :     ObjectAddress address;
    9830                 :             : 
    9831                 :             :     Assert(IsA(stmt, CreateStatsStmt));
    9832                 :             : 
    9833                 :             :     /* The CreateStatsStmt has already been through transformStatsStmt */
    9834                 :             :     Assert(stmt->transformed);
    9835                 :             : 
    9836                 :             :     /* The owner must be set to the original statistics owner */
    9837                 :             :     Assert(OidIsValid(stmt->owner));
    9838                 :             : 
    9839                 :          57 :     address = CreateStatistics(list_make1_oid(RelationGetRelid(rel)),
    9840                 :          57 :                                stmt, !is_rebuild);
    9841                 :             : 
    9842                 :          57 :     return address;
    9843                 :             : }
    9844                 :             : 
    9845                 :             : /*
    9846                 :             :  * ALTER TABLE ADD CONSTRAINT USING INDEX
    9847                 :             :  *
    9848                 :             :  * Returns the address of the new constraint.
    9849                 :             :  */
    9850                 :             : static ObjectAddress
    9851                 :        6653 : ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
    9852                 :             :                          IndexStmt *stmt, LOCKMODE lockmode)
    9853                 :             : {
    9854                 :        6653 :     Oid         index_oid = stmt->indexOid;
    9855                 :             :     Relation    indexRel;
    9856                 :             :     char       *indexName;
    9857                 :             :     IndexInfo  *indexInfo;
    9858                 :             :     char       *constraintName;
    9859                 :             :     char        constraintType;
    9860                 :             :     ObjectAddress address;
    9861                 :             :     uint16      flags;
    9862                 :             : 
    9863                 :             :     Assert(IsA(stmt, IndexStmt));
    9864                 :             :     Assert(OidIsValid(index_oid));
    9865                 :             :     Assert(stmt->isconstraint);
    9866                 :             : 
    9867                 :             :     /*
    9868                 :             :      * Doing this on partitioned tables is not a simple feature to implement,
    9869                 :             :      * so let's punt for now.
    9870                 :             :      */
    9871         [ +  + ]:        6653 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
    9872         [ +  - ]:           4 :         ereport(ERROR,
    9873                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    9874                 :             :                  errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
    9875                 :             : 
    9876                 :        6649 :     indexRel = index_open(index_oid, AccessShareLock);
    9877                 :             : 
    9878                 :        6649 :     indexName = pstrdup(RelationGetRelationName(indexRel));
    9879                 :             : 
    9880                 :        6649 :     indexInfo = BuildIndexInfo(indexRel);
    9881                 :             : 
    9882                 :             :     /* this should have been checked at parse time */
    9883         [ -  + ]:        6649 :     if (!indexInfo->ii_Unique)
    9884         [ #  # ]:           0 :         elog(ERROR, "index \"%s\" is not unique", indexName);
    9885                 :             : 
    9886                 :             :     /*
    9887                 :             :      * Determine name to assign to constraint.  We require a constraint to
    9888                 :             :      * have the same name as the underlying index; therefore, use the index's
    9889                 :             :      * existing name as the default constraint name, and if the user
    9890                 :             :      * explicitly gives some other name for the constraint, rename the index
    9891                 :             :      * to match.
    9892                 :             :      */
    9893                 :        6649 :     constraintName = stmt->idxname;
    9894         [ +  + ]:        6649 :     if (constraintName == NULL)
    9895                 :        6632 :         constraintName = indexName;
    9896         [ +  + ]:          17 :     else if (strcmp(constraintName, indexName) != 0)
    9897                 :             :     {
    9898         [ +  - ]:          13 :         ereport(NOTICE,
    9899                 :             :                 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
    9900                 :             :                         indexName, constraintName)));
    9901                 :          13 :         RenameRelationInternal(index_oid, constraintName, false, true);
    9902                 :             :     }
    9903                 :             : 
    9904                 :             :     /* Extra checks needed if making primary key */
    9905         [ +  + ]:        6649 :     if (stmt->primary)
    9906                 :        3721 :         index_check_primary_key(rel, indexInfo, true, stmt);
    9907                 :             : 
    9908                 :             :     /* Note we currently don't support EXCLUSION constraints here */
    9909         [ +  + ]:        6645 :     if (stmt->primary)
    9910                 :        3717 :         constraintType = CONSTRAINT_PRIMARY;
    9911                 :             :     else
    9912                 :        2928 :         constraintType = CONSTRAINT_UNIQUE;
    9913                 :             : 
    9914                 :             :     /* Create the catalog entries for the constraint */
    9915                 :        6645 :     flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
    9916                 :             :         INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
    9917         [ -  + ]:       13290 :         (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
    9918         [ -  + ]:        6645 :         (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
    9919                 :        6645 :         (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
    9920                 :             : 
    9921                 :        6645 :     address = index_constraint_create(rel,
    9922                 :             :                                       index_oid,
    9923                 :             :                                       InvalidOid,
    9924                 :             :                                       indexInfo,
    9925                 :             :                                       constraintName,
    9926                 :             :                                       constraintType,
    9927                 :             :                                       flags,
    9928                 :             :                                       allowSystemTableMods,
    9929                 :             :                                       false);   /* is_internal */
    9930                 :             : 
    9931                 :        6645 :     index_close(indexRel, NoLock);
    9932                 :             : 
    9933                 :        6645 :     return address;
    9934                 :             : }
    9935                 :             : 
    9936                 :             : /*
    9937                 :             :  * ALTER TABLE ADD CONSTRAINT
    9938                 :             :  *
    9939                 :             :  * Return value is the address of the new constraint; if no constraint was
    9940                 :             :  * added, InvalidObjectAddress is returned.
    9941                 :             :  */
    9942                 :             : static ObjectAddress
    9943                 :        8381 : ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
    9944                 :             :                     Constraint *newConstraint, bool recurse, bool is_readd,
    9945                 :             :                     LOCKMODE lockmode)
    9946                 :             : {
    9947                 :        8381 :     ObjectAddress address = InvalidObjectAddress;
    9948                 :             : 
    9949                 :             :     Assert(IsA(newConstraint, Constraint));
    9950                 :             : 
    9951                 :             :     /*
    9952                 :             :      * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
    9953                 :             :      * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
    9954                 :             :      * parse_utilcmd.c).
    9955                 :             :      */
    9956      [ +  +  - ]:        8381 :     switch (newConstraint->contype)
    9957                 :             :     {
    9958                 :        6509 :         case CONSTR_CHECK:
    9959                 :             :         case CONSTR_NOTNULL:
    9960                 :             :             address =
    9961                 :        6509 :                 ATAddCheckNNConstraint(wqueue, tab, rel,
    9962                 :             :                                        newConstraint, recurse, false, is_readd,
    9963                 :             :                                        lockmode);
    9964                 :        6409 :             break;
    9965                 :             : 
    9966                 :        1872 :         case CONSTR_FOREIGN:
    9967                 :             : 
    9968                 :             :             /*
    9969                 :             :              * Assign or validate constraint name
    9970                 :             :              */
    9971         [ +  + ]:        1872 :             if (newConstraint->conname)
    9972                 :             :             {
    9973         [ -  + ]:         761 :                 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
    9974                 :             :                                          RelationGetRelid(rel),
    9975                 :         761 :                                          newConstraint->conname))
    9976         [ #  # ]:           0 :                     ereport(ERROR,
    9977                 :             :                             (errcode(ERRCODE_DUPLICATE_OBJECT),
    9978                 :             :                              errmsg("constraint \"%s\" for relation \"%s\" already exists",
    9979                 :             :                                     newConstraint->conname,
    9980                 :             :                                     RelationGetRelationName(rel))));
    9981                 :             :             }
    9982                 :             :             else
    9983                 :        1111 :                 newConstraint->conname =
    9984                 :        1111 :                     ChooseConstraintName(RelationGetRelationName(rel),
    9985                 :        1111 :                                          ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
    9986                 :             :                                          "fkey",
    9987                 :        1111 :                                          RelationGetNamespace(rel),
    9988                 :             :                                          NIL);
    9989                 :             : 
    9990                 :        1872 :             address = ATAddForeignKeyConstraint(wqueue, tab, rel,
    9991                 :             :                                                 newConstraint,
    9992                 :             :                                                 recurse, false,
    9993                 :             :                                                 lockmode);
    9994                 :        1503 :             break;
    9995                 :             : 
    9996                 :           0 :         default:
    9997         [ #  # ]:           0 :             elog(ERROR, "unrecognized constraint type: %d",
    9998                 :             :                  (int) newConstraint->contype);
    9999                 :             :     }
   10000                 :             : 
   10001                 :        7912 :     return address;
   10002                 :             : }
   10003                 :             : 
   10004                 :             : /*
   10005                 :             :  * Generate the column-name portion of the constraint name for a new foreign
   10006                 :             :  * key given the list of column names that reference the referenced
   10007                 :             :  * table.  This will be passed to ChooseConstraintName along with the parent
   10008                 :             :  * table name and the "fkey" suffix.
   10009                 :             :  *
   10010                 :             :  * We know that less than NAMEDATALEN characters will actually be used, so we
   10011                 :             :  * can truncate the result once we've generated that many.
   10012                 :             :  *
   10013                 :             :  * XXX see also ChooseExtendedStatisticNameAddition and
   10014                 :             :  * ChooseIndexNameAddition.
   10015                 :             :  */
   10016                 :             : static char *
   10017                 :        1111 : ChooseForeignKeyConstraintNameAddition(List *colnames)
   10018                 :             : {
   10019                 :             :     char        buf[NAMEDATALEN * 2];
   10020                 :        1111 :     int         buflen = 0;
   10021                 :             :     ListCell   *lc;
   10022                 :             : 
   10023                 :        1111 :     buf[0] = '\0';
   10024   [ +  -  +  +  :        2516 :     foreach(lc, colnames)
                   +  + ]
   10025                 :             :     {
   10026                 :        1405 :         const char *name = strVal(lfirst(lc));
   10027                 :             : 
   10028         [ +  + ]:        1405 :         if (buflen > 0)
   10029                 :         294 :             buf[buflen++] = '_';    /* insert _ between names */
   10030                 :             : 
   10031                 :             :         /*
   10032                 :             :          * At this point we have buflen <= NAMEDATALEN.  name should be less
   10033                 :             :          * than NAMEDATALEN already, but use strlcpy for paranoia.
   10034                 :             :          */
   10035                 :        1405 :         strlcpy(buf + buflen, name, NAMEDATALEN);
   10036                 :        1405 :         buflen += strlen(buf + buflen);
   10037         [ -  + ]:        1405 :         if (buflen >= NAMEDATALEN)
   10038                 :           0 :             break;
   10039                 :             :     }
   10040                 :        1111 :     return pstrdup(buf);
   10041                 :             : }
   10042                 :             : 
   10043                 :             : /*
   10044                 :             :  * Add a check or not-null constraint to a single table and its children.
   10045                 :             :  * Returns the address of the constraint added to the parent relation,
   10046                 :             :  * if one gets added, or InvalidObjectAddress otherwise.
   10047                 :             :  *
   10048                 :             :  * Subroutine for ATExecAddConstraint.
   10049                 :             :  *
   10050                 :             :  * We must recurse to child tables during execution, rather than using
   10051                 :             :  * ALTER TABLE's normal prep-time recursion.  The reason is that all the
   10052                 :             :  * constraints *must* be given the same name, else they won't be seen as
   10053                 :             :  * related later.  If the user didn't explicitly specify a name, then
   10054                 :             :  * AddRelationNewConstraints would normally assign different names to the
   10055                 :             :  * child constraints.  To fix that, we must capture the name assigned at
   10056                 :             :  * the parent table and pass that down.
   10057                 :             :  */
   10058                 :             : static ObjectAddress
   10059                 :        7192 : ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
   10060                 :             :                        Constraint *constr, bool recurse, bool recursing,
   10061                 :             :                        bool is_readd, LOCKMODE lockmode)
   10062                 :             : {
   10063                 :             :     List       *newcons;
   10064                 :             :     ListCell   *lcon;
   10065                 :             :     List       *children;
   10066                 :             :     ListCell   *child;
   10067                 :        7192 :     ObjectAddress address = InvalidObjectAddress;
   10068                 :             : 
   10069                 :             :     /* Guard against stack overflow due to overly deep inheritance tree. */
   10070                 :        7192 :     check_stack_depth();
   10071                 :             : 
   10072                 :             :     /* At top level, permission check was done in ATPrepCmd, else do it */
   10073         [ +  + ]:        7192 :     if (recursing)
   10074                 :         683 :         ATSimplePermissions(AT_AddConstraint, rel,
   10075                 :             :                             ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   10076                 :             : 
   10077                 :             :     /*
   10078                 :             :      * Call AddRelationNewConstraints to do the work, making sure it works on
   10079                 :             :      * a copy of the Constraint so transformExpr can't modify the original. It
   10080                 :             :      * returns a list of cooked constraints.
   10081                 :             :      *
   10082                 :             :      * If the constraint ends up getting merged with a pre-existing one, it's
   10083                 :             :      * omitted from the returned list, which is what we want: we do not need
   10084                 :             :      * to do any validation work.  That can only happen at child tables,
   10085                 :             :      * though, since we disallow merging at the top level.
   10086                 :             :      */
   10087                 :        7192 :     newcons = AddRelationNewConstraints(rel, NIL,
   10088                 :             :                                         list_make1(copyObject(constr)),
   10089         [ +  + ]:        7192 :                                         recursing || is_readd,  /* allow_merge */
   10090                 :             :                                         !recursing, /* is_local */
   10091                 :             :                                         is_readd,   /* is_internal */
   10092         [ +  + ]:       14384 :                                         NULL);  /* queryString not available
   10093                 :             :                                                  * here */
   10094                 :             : 
   10095                 :             :     /* we don't expect more than one constraint here */
   10096                 :             :     Assert(list_length(newcons) <= 1);
   10097                 :             : 
   10098                 :             :     /* Add each to-be-validated constraint to Phase 3's queue */
   10099   [ +  +  +  +  :       14062 :     foreach(lcon, newcons)
                   +  + ]
   10100                 :             :     {
   10101                 :        6966 :         CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
   10102                 :             : 
   10103   [ +  +  +  + ]:        6966 :         if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
   10104                 :             :         {
   10105                 :             :             NewConstraint *newcon;
   10106                 :             : 
   10107                 :         783 :             newcon = palloc0_object(NewConstraint);
   10108                 :         783 :             newcon->name = ccon->name;
   10109                 :         783 :             newcon->contype = ccon->contype;
   10110                 :         783 :             newcon->qual = ccon->expr;
   10111                 :             : 
   10112                 :         783 :             tab->constraints = lappend(tab->constraints, newcon);
   10113                 :             :         }
   10114                 :             : 
   10115                 :             :         /* Save the actually assigned name if it was defaulted */
   10116         [ +  + ]:        6966 :         if (constr->conname == NULL)
   10117                 :        5528 :             constr->conname = ccon->name;
   10118                 :             : 
   10119                 :             :         /*
   10120                 :             :          * If adding a valid not-null constraint, set the pg_attribute flag
   10121                 :             :          * and tell phase 3 to verify existing rows, if needed.  For an
   10122                 :             :          * invalid constraint, just set attnotnull, without queueing
   10123                 :             :          * verification.
   10124                 :             :          */
   10125         [ +  + ]:        6966 :         if (constr->contype == CONSTR_NOTNULL)
   10126                 :        5878 :             set_attnotnull(wqueue, rel, ccon->attnum,
   10127                 :        5878 :                            !constr->skip_validation);
   10128                 :             : 
   10129                 :        6966 :         ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
   10130                 :             :     }
   10131                 :             : 
   10132                 :             :     /* At this point we must have a locked-down name to use */
   10133                 :             :     Assert(newcons == NIL || constr->conname != NULL);
   10134                 :             : 
   10135                 :             :     /* Advance command counter in case same table is visited multiple times */
   10136                 :        7096 :     CommandCounterIncrement();
   10137                 :             : 
   10138                 :             :     /*
   10139                 :             :      * If the constraint got merged with an existing constraint, we're done.
   10140                 :             :      * We mustn't recurse to child tables in this case, because they've
   10141                 :             :      * already got the constraint, and visiting them again would lead to an
   10142                 :             :      * incorrect value for coninhcount.
   10143                 :             :      */
   10144         [ +  + ]:        7096 :     if (newcons == NIL)
   10145                 :         130 :         return address;
   10146                 :             : 
   10147                 :             :     /*
   10148                 :             :      * If adding a NO INHERIT constraint, no need to find our children.
   10149                 :             :      */
   10150         [ +  + ]:        6966 :     if (constr->is_no_inherit)
   10151                 :          56 :         return address;
   10152                 :             : 
   10153                 :             :     /*
   10154                 :             :      * Propagate to children as appropriate.  Unlike most other ALTER
   10155                 :             :      * routines, we have to do this one level of recursion at a time; we can't
   10156                 :             :      * use find_all_inheritors to do it in one pass.
   10157                 :             :      */
   10158                 :             :     children =
   10159                 :        6910 :         find_inheritance_children(RelationGetRelid(rel), lockmode);
   10160                 :             : 
   10161                 :             :     /*
   10162                 :             :      * Check if ONLY was specified with ALTER TABLE.  If so, allow the
   10163                 :             :      * constraint creation only if there are no children currently. Error out
   10164                 :             :      * otherwise.
   10165                 :             :      */
   10166   [ +  +  +  + ]:        6910 :     if (!recurse && children != NIL)
   10167         [ +  - ]:           4 :         ereport(ERROR,
   10168                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10169                 :             :                  errmsg("constraint must be added to child tables too")));
   10170                 :             : 
   10171                 :             :     /*
   10172                 :             :      * Recurse to create the constraint on each child.
   10173                 :             :      */
   10174   [ +  +  +  +  :        7569 :     foreach(child, children)
                   +  + ]
   10175                 :             :     {
   10176                 :         683 :         Oid         childrelid = lfirst_oid(child);
   10177                 :             :         Relation    childrel;
   10178                 :             :         AlteredTableInfo *childtab;
   10179                 :             : 
   10180                 :             :         /* find_inheritance_children already got lock */
   10181                 :         683 :         childrel = table_open(childrelid, NoLock);
   10182                 :         683 :         CheckAlterTableIsSafe(childrel);
   10183                 :             : 
   10184                 :             :         /* Find or create work queue entry for this table */
   10185                 :         683 :         childtab = ATGetQueueEntry(wqueue, childrel);
   10186                 :             : 
   10187                 :             :         /* Recurse to this child */
   10188                 :         683 :         ATAddCheckNNConstraint(wqueue, childtab, childrel,
   10189                 :             :                                constr, recurse, true, is_readd, lockmode);
   10190                 :             : 
   10191                 :         663 :         table_close(childrel, NoLock);
   10192                 :             :     }
   10193                 :             : 
   10194                 :        6886 :     return address;
   10195                 :             : }
   10196                 :             : 
   10197                 :             : /*
   10198                 :             :  * Add a foreign-key constraint to a single table; return the new constraint's
   10199                 :             :  * address.
   10200                 :             :  *
   10201                 :             :  * Subroutine for ATExecAddConstraint.  Must already hold exclusive
   10202                 :             :  * lock on the rel, and have done appropriate validity checks for it.
   10203                 :             :  * We do permissions checks here, however.
   10204                 :             :  *
   10205                 :             :  * When the referenced or referencing tables (or both) are partitioned,
   10206                 :             :  * multiple pg_constraint rows are required -- one for each partitioned table
   10207                 :             :  * and each partition on each side (fortunately, not one for every combination
   10208                 :             :  * thereof).  We also need action triggers on each leaf partition on the
   10209                 :             :  * referenced side, and check triggers on each leaf partition on the
   10210                 :             :  * referencing side.
   10211                 :             :  */
   10212                 :             : static ObjectAddress
   10213                 :        1872 : ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
   10214                 :             :                           Constraint *fkconstraint,
   10215                 :             :                           bool recurse, bool recursing, LOCKMODE lockmode)
   10216                 :             : {
   10217                 :             :     Relation    pkrel;
   10218                 :        1872 :     int16       pkattnum[INDEX_MAX_KEYS] = {0};
   10219                 :        1872 :     int16       fkattnum[INDEX_MAX_KEYS] = {0};
   10220                 :        1872 :     Oid         pktypoid[INDEX_MAX_KEYS] = {0};
   10221                 :        1872 :     Oid         fktypoid[INDEX_MAX_KEYS] = {0};
   10222                 :        1872 :     Oid         pkcolloid[INDEX_MAX_KEYS] = {0};
   10223                 :        1872 :     Oid         fkcolloid[INDEX_MAX_KEYS] = {0};
   10224                 :        1872 :     Oid         opclasses[INDEX_MAX_KEYS] = {0};
   10225                 :        1872 :     Oid         pfeqoperators[INDEX_MAX_KEYS] = {0};
   10226                 :        1872 :     Oid         ppeqoperators[INDEX_MAX_KEYS] = {0};
   10227                 :        1872 :     Oid         ffeqoperators[INDEX_MAX_KEYS] = {0};
   10228                 :        1872 :     int16       fkdelsetcols[INDEX_MAX_KEYS] = {0};
   10229                 :             :     bool        with_period;
   10230                 :             :     bool        pk_has_without_overlaps;
   10231                 :             :     int         i;
   10232                 :             :     int         numfks,
   10233                 :             :                 numpks,
   10234                 :             :                 numfkdelsetcols;
   10235                 :             :     Oid         indexOid;
   10236                 :             :     bool        old_check_ok;
   10237                 :             :     ObjectAddress address;
   10238                 :        1872 :     ListCell   *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
   10239                 :             : 
   10240                 :             :     /*
   10241                 :             :      * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
   10242                 :             :      * delete rows out from under us.
   10243                 :             :      */
   10244         [ +  + ]:        1872 :     if (OidIsValid(fkconstraint->old_pktable_oid))
   10245                 :          48 :         pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
   10246                 :             :     else
   10247                 :        1824 :         pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
   10248                 :             : 
   10249                 :             :     /*
   10250                 :             :      * Validity checks (permission checks wait till we have the column
   10251                 :             :      * numbers)
   10252                 :             :      */
   10253   [ +  +  +  + ]:        1868 :     if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   10254         [ +  - ]:           4 :         ereport(ERROR,
   10255                 :             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10256                 :             :                 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
   10257                 :             :                        RelationGetRelationName(rel),
   10258                 :             :                        RelationGetRelationName(pkrel)));
   10259                 :             : 
   10260         [ +  + ]:        1864 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
   10261         [ -  + ]:         236 :         pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
   10262         [ #  # ]:           0 :         ereport(ERROR,
   10263                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10264                 :             :                  errmsg("referenced relation \"%s\" is not a table",
   10265                 :             :                         RelationGetRelationName(pkrel))));
   10266                 :             : 
   10267                 :             :     /*
   10268                 :             :      * Conflict log tables are used internally for logical replication
   10269                 :             :      * conflict logging and should not be referenced by foreign keys, as it
   10270                 :             :      * could disrupt conflict logging.
   10271                 :             :      */
   10272         [ +  + ]:        1864 :     if (IsConflictLogTableClass(pkrel->rd_rel))
   10273         [ +  - ]:           4 :         ereport(ERROR,
   10274                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10275                 :             :                  errmsg("cannot reference conflict log table \"%s\"",
   10276                 :             :                         RelationGetRelationName(pkrel)),
   10277                 :             :                  errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
   10278                 :             : 
   10279   [ +  +  +  + ]:        1860 :     if (!allowSystemTableMods && IsSystemRelation(pkrel))
   10280         [ +  - ]:           1 :         ereport(ERROR,
   10281                 :             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   10282                 :             :                  errmsg("permission denied: \"%s\" is a system catalog",
   10283                 :             :                         RelationGetRelationName(pkrel))));
   10284                 :             : 
   10285                 :             :     /*
   10286                 :             :      * References from permanent or unlogged tables to temp tables, and from
   10287                 :             :      * permanent tables to unlogged tables, are disallowed because the
   10288                 :             :      * referenced data can vanish out from under us.  References from temp
   10289                 :             :      * tables to any other table type are also disallowed, because other
   10290                 :             :      * backends might need to run the RI triggers on the perm table, but they
   10291                 :             :      * can't reliably see tuples in the local buffers of other backends.
   10292                 :             :      */
   10293   [ +  +  +  - ]:        1859 :     switch (rel->rd_rel->relpersistence)
   10294                 :             :     {
   10295                 :        1666 :         case RELPERSISTENCE_PERMANENT:
   10296         [ -  + ]:        1666 :             if (!RelationIsPermanent(pkrel))
   10297         [ #  # ]:           0 :                 ereport(ERROR,
   10298                 :             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10299                 :             :                          errmsg("constraints on permanent tables may reference only permanent tables")));
   10300                 :        1666 :             break;
   10301                 :           8 :         case RELPERSISTENCE_UNLOGGED:
   10302         [ +  - ]:           8 :             if (!RelationIsPermanent(pkrel)
   10303         [ -  + ]:           8 :                 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
   10304         [ #  # ]:           0 :                 ereport(ERROR,
   10305                 :             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10306                 :             :                          errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
   10307                 :           8 :             break;
   10308                 :         185 :         case RELPERSISTENCE_TEMP:
   10309         [ -  + ]:         185 :             if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   10310         [ #  # ]:           0 :                 ereport(ERROR,
   10311                 :             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10312                 :             :                          errmsg("constraints on temporary tables may reference only temporary tables")));
   10313   [ +  -  -  + ]:         185 :             if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
   10314         [ #  # ]:           0 :                 ereport(ERROR,
   10315                 :             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   10316                 :             :                          errmsg("constraints on temporary tables must involve temporary tables of this session")));
   10317                 :         185 :             break;
   10318                 :             :     }
   10319                 :             : 
   10320                 :             :     /*
   10321                 :             :      * Look up the referencing attributes to make sure they exist, and record
   10322                 :             :      * their attnums and type and collation OIDs.
   10323                 :             :      */
   10324                 :        1859 :     numfks = transformColumnNameList(RelationGetRelid(rel),
   10325                 :             :                                      fkconstraint->fk_attrs,
   10326                 :             :                                      fkattnum, fktypoid, fkcolloid);
   10327   [ +  +  +  + ]:        1839 :     with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
   10328   [ +  +  +  + ]:        1839 :     if (with_period && !fkconstraint->fk_with_period)
   10329         [ +  - ]:          16 :         ereport(ERROR,
   10330                 :             :                 errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10331                 :             :                 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
   10332                 :             : 
   10333                 :        1823 :     numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
   10334                 :             :                                               fkconstraint->fk_del_set_cols,
   10335                 :             :                                               fkdelsetcols, NULL, NULL);
   10336                 :        1819 :     numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
   10337                 :             :                                                    numfkdelsetcols,
   10338                 :             :                                                    fkdelsetcols,
   10339                 :             :                                                    fkconstraint->fk_del_set_cols);
   10340                 :             : 
   10341                 :             :     /*
   10342                 :             :      * If the attribute list for the referenced table was omitted, lookup the
   10343                 :             :      * definition of the primary key and use it.  Otherwise, validate the
   10344                 :             :      * supplied attribute list.  In either case, discover the index OID and
   10345                 :             :      * index opclasses, and the attnums and type and collation OIDs of the
   10346                 :             :      * attributes.
   10347                 :             :      */
   10348         [ +  + ]:        1815 :     if (fkconstraint->pk_attrs == NIL)
   10349                 :             :     {
   10350                 :         900 :         numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
   10351                 :             :                                             &fkconstraint->pk_attrs,
   10352                 :             :                                             pkattnum, pktypoid, pkcolloid,
   10353                 :             :                                             opclasses, &pk_has_without_overlaps);
   10354                 :             : 
   10355                 :             :         /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
   10356   [ +  +  +  + ]:         900 :         if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
   10357         [ +  - ]:          16 :             ereport(ERROR,
   10358                 :             :                     errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10359                 :             :                     errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
   10360                 :             :     }
   10361                 :             :     else
   10362                 :             :     {
   10363                 :         915 :         numpks = transformColumnNameList(RelationGetRelid(pkrel),
   10364                 :             :                                          fkconstraint->pk_attrs,
   10365                 :             :                                          pkattnum, pktypoid, pkcolloid);
   10366                 :             : 
   10367                 :             :         /* Since we got pk_attrs, one should be a period. */
   10368   [ +  +  +  + ]:         895 :         if (with_period && !fkconstraint->pk_with_period)
   10369         [ +  - ]:          16 :             ereport(ERROR,
   10370                 :             :                     errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10371                 :             :                     errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
   10372                 :             : 
   10373                 :             :         /* Look for an index matching the column list */
   10374                 :         879 :         indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
   10375                 :             :                                            with_period, opclasses, &pk_has_without_overlaps);
   10376                 :             :     }
   10377                 :             : 
   10378                 :             :     /*
   10379                 :             :      * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
   10380                 :             :      * must use PERIOD.
   10381                 :             :      */
   10382   [ +  +  +  + ]:        1739 :     if (pk_has_without_overlaps && !with_period)
   10383         [ +  - ]:           8 :         ereport(ERROR,
   10384                 :             :                 errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10385                 :             :                 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
   10386                 :             : 
   10387                 :             :     /*
   10388                 :             :      * Now we can check permissions.
   10389                 :             :      */
   10390                 :        1731 :     checkFkeyPermissions(pkrel, pkattnum, numpks);
   10391                 :             : 
   10392                 :             :     /*
   10393                 :             :      * Check some things for generated columns.
   10394                 :             :      */
   10395         [ +  + ]:        4043 :     for (i = 0; i < numfks; i++)
   10396                 :             :     {
   10397                 :        2332 :         char        attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
   10398                 :             : 
   10399         [ +  + ]:        2332 :         if (attgenerated)
   10400                 :             :         {
   10401                 :             :             /*
   10402                 :             :              * Check restrictions on UPDATE/DELETE actions, per SQL standard
   10403                 :             :              */
   10404         [ +  - ]:          32 :             if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
   10405         [ +  - ]:          32 :                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
   10406         [ +  + ]:          32 :                 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
   10407         [ +  - ]:           8 :                 ereport(ERROR,
   10408                 :             :                         (errcode(ERRCODE_SYNTAX_ERROR),
   10409                 :             :                          errmsg("invalid %s action for foreign key constraint containing generated column",
   10410                 :             :                                 "ON UPDATE")));
   10411         [ +  + ]:          24 :             if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
   10412         [ -  + ]:          16 :                 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
   10413         [ +  - ]:           8 :                 ereport(ERROR,
   10414                 :             :                         (errcode(ERRCODE_SYNTAX_ERROR),
   10415                 :             :                          errmsg("invalid %s action for foreign key constraint containing generated column",
   10416                 :             :                                 "ON DELETE")));
   10417                 :             :         }
   10418                 :             : 
   10419                 :             :         /*
   10420                 :             :          * FKs on virtual columns are not supported.  This would require
   10421                 :             :          * various additional support in ri_triggers.c, including special
   10422                 :             :          * handling in ri_NullCheck(), ri_KeysEqual(),
   10423                 :             :          * RI_FKey_fk_upd_check_required() (since all virtual columns appear
   10424                 :             :          * as NULL there).  Also not really practical as long as you can't
   10425                 :             :          * index virtual columns.
   10426                 :             :          */
   10427         [ +  + ]:        2316 :         if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
   10428         [ +  - ]:           4 :             ereport(ERROR,
   10429                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10430                 :             :                      errmsg("foreign key constraints on virtual generated columns are not supported")));
   10431                 :             :     }
   10432                 :             : 
   10433                 :             :     /*
   10434                 :             :      * Some actions are currently unsupported for foreign keys using PERIOD.
   10435                 :             :      */
   10436         [ +  + ]:        1711 :     if (fkconstraint->fk_with_period)
   10437                 :             :     {
   10438         [ +  + ]:         178 :         if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
   10439         [ +  + ]:         170 :             fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
   10440         [ +  + ]:         158 :             fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
   10441         [ +  + ]:         146 :             fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
   10442         [ +  - ]:          44 :             ereport(ERROR,
   10443                 :             :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10444                 :             :                     errmsg("unsupported %s action for foreign key constraint using PERIOD",
   10445                 :             :                            "ON UPDATE"));
   10446                 :             : 
   10447         [ +  + ]:         134 :         if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
   10448         [ +  - ]:         130 :             fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
   10449         [ +  - ]:         130 :             fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
   10450         [ -  + ]:         130 :             fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
   10451         [ +  - ]:           4 :             ereport(ERROR,
   10452                 :             :                     errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   10453                 :             :                     errmsg("unsupported %s action for foreign key constraint using PERIOD",
   10454                 :             :                            "ON DELETE"));
   10455                 :             :     }
   10456                 :             : 
   10457                 :             :     /*
   10458                 :             :      * Look up the equality operators to use in the constraint.
   10459                 :             :      *
   10460                 :             :      * Note that we have to be careful about the difference between the actual
   10461                 :             :      * PK column type and the opclass' declared input type, which might be
   10462                 :             :      * only binary-compatible with it.  The declared opcintype is the right
   10463                 :             :      * thing to probe pg_amop with.
   10464                 :             :      */
   10465         [ -  + ]:        1663 :     if (numfks != numpks)
   10466         [ #  # ]:           0 :         ereport(ERROR,
   10467                 :             :                 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   10468                 :             :                  errmsg("number of referencing and referenced columns for foreign key disagree")));
   10469                 :             : 
   10470                 :             :     /*
   10471                 :             :      * On the strength of a previous constraint, we might avoid scanning
   10472                 :             :      * tables to validate this one.  See below.
   10473                 :             :      */
   10474                 :        1663 :     old_check_ok = (fkconstraint->old_conpfeqop != NIL);
   10475                 :             :     Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
   10476                 :             : 
   10477         [ +  + ]:        3623 :     for (i = 0; i < numpks; i++)
   10478                 :             :     {
   10479                 :        2120 :         Oid         pktype = pktypoid[i];
   10480                 :        2120 :         Oid         fktype = fktypoid[i];
   10481                 :             :         Oid         fktyped;
   10482                 :        2120 :         Oid         pkcoll = pkcolloid[i];
   10483                 :        2120 :         Oid         fkcoll = fkcolloid[i];
   10484                 :             :         HeapTuple   cla_ht;
   10485                 :             :         Form_pg_opclass cla_tup;
   10486                 :             :         Oid         amid;
   10487                 :             :         Oid         opfamily;
   10488                 :             :         Oid         opcintype;
   10489                 :             :         bool        for_overlaps;
   10490                 :             :         CompareType cmptype;
   10491                 :             :         Oid         pfeqop;
   10492                 :             :         Oid         ppeqop;
   10493                 :             :         Oid         ffeqop;
   10494                 :             :         int16       eqstrategy;
   10495                 :             :         Oid         pfeqop_right;
   10496                 :             : 
   10497                 :             :         /* We need several fields out of the pg_opclass entry */
   10498                 :        2120 :         cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
   10499         [ -  + ]:        2120 :         if (!HeapTupleIsValid(cla_ht))
   10500         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
   10501                 :        2120 :         cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
   10502                 :        2120 :         amid = cla_tup->opcmethod;
   10503                 :        2120 :         opfamily = cla_tup->opcfamily;
   10504                 :        2120 :         opcintype = cla_tup->opcintype;
   10505                 :        2120 :         ReleaseSysCache(cla_ht);
   10506                 :             : 
   10507                 :             :         /*
   10508                 :             :          * Get strategy number from index AM.
   10509                 :             :          *
   10510                 :             :          * For a normal foreign-key constraint, this should not fail, since we
   10511                 :             :          * already checked that the index is unique and should therefore have
   10512                 :             :          * appropriate equal operators.  For a period foreign key, this could
   10513                 :             :          * fail if we selected a non-matching exclusion constraint earlier.
   10514                 :             :          * (XXX Maybe we should do these lookups earlier so we don't end up
   10515                 :             :          * doing that.)
   10516                 :             :          */
   10517   [ +  +  +  + ]:        2120 :         for_overlaps = with_period && i == numpks - 1;
   10518         [ +  + ]:        2120 :         cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
   10519                 :        2120 :         eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
   10520         [ -  + ]:        2120 :         if (eqstrategy == InvalidStrategy)
   10521   [ #  #  #  # ]:           0 :             ereport(ERROR,
   10522                 :             :                     errcode(ERRCODE_UNDEFINED_OBJECT),
   10523                 :             :                     for_overlaps
   10524                 :             :                     ? errmsg("could not identify an overlaps operator for foreign key")
   10525                 :             :                     : errmsg("could not identify an equality operator for foreign key"),
   10526                 :             :                     errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
   10527                 :             :                               cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
   10528                 :             : 
   10529                 :             :         /*
   10530                 :             :          * There had better be a primary equality operator for the index.
   10531                 :             :          * We'll use it for PK = PK comparisons.
   10532                 :             :          */
   10533                 :        2120 :         ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
   10534                 :             :                                      eqstrategy);
   10535                 :             : 
   10536         [ -  + ]:        2120 :         if (!OidIsValid(ppeqop))
   10537         [ #  # ]:           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
   10538                 :             :                  eqstrategy, opcintype, opcintype, opfamily);
   10539                 :             : 
   10540                 :             :         /*
   10541                 :             :          * Are there equality operators that take exactly the FK type? Assume
   10542                 :             :          * we should look through any domain here.
   10543                 :             :          */
   10544                 :        2120 :         fktyped = getBaseType(fktype);
   10545                 :             : 
   10546                 :        2120 :         pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
   10547                 :             :                                      eqstrategy);
   10548         [ +  + ]:        2120 :         if (OidIsValid(pfeqop))
   10549                 :             :         {
   10550                 :        1663 :             pfeqop_right = fktyped;
   10551                 :        1663 :             ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
   10552                 :             :                                          eqstrategy);
   10553                 :             :         }
   10554                 :             :         else
   10555                 :             :         {
   10556                 :             :             /* keep compiler quiet */
   10557                 :         457 :             pfeqop_right = InvalidOid;
   10558                 :         457 :             ffeqop = InvalidOid;
   10559                 :             :         }
   10560                 :             : 
   10561   [ +  +  -  + ]:        2120 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
   10562                 :             :         {
   10563                 :             :             /*
   10564                 :             :              * Otherwise, look for an implicit cast from the FK type to the
   10565                 :             :              * opcintype, and if found, use the primary equality operator.
   10566                 :             :              * This is a bit tricky because opcintype might be a polymorphic
   10567                 :             :              * type such as ANYARRAY or ANYENUM; so what we have to test is
   10568                 :             :              * whether the two actual column types can be concurrently cast to
   10569                 :             :              * that type.  (Otherwise, we'd fail to reject combinations such
   10570                 :             :              * as int[] and point[].)
   10571                 :             :              */
   10572                 :             :             Oid         input_typeids[2];
   10573                 :             :             Oid         target_typeids[2];
   10574                 :             : 
   10575                 :         457 :             input_typeids[0] = pktype;
   10576                 :         457 :             input_typeids[1] = fktype;
   10577                 :         457 :             target_typeids[0] = opcintype;
   10578                 :         457 :             target_typeids[1] = opcintype;
   10579         [ +  + ]:         457 :             if (can_coerce_type(2, input_typeids, target_typeids,
   10580                 :             :                                 COERCION_IMPLICIT))
   10581                 :             :             {
   10582                 :         305 :                 pfeqop = ffeqop = ppeqop;
   10583                 :         305 :                 pfeqop_right = opcintype;
   10584                 :             :             }
   10585                 :             :         }
   10586                 :             : 
   10587   [ +  +  -  + ]:        2120 :         if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
   10588         [ +  - ]:         152 :             ereport(ERROR,
   10589                 :             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   10590                 :             :                      errmsg("foreign key constraint \"%s\" cannot be implemented",
   10591                 :             :                             fkconstraint->conname),
   10592                 :             :                      errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
   10593                 :             :                                "are of incompatible types: %s and %s.",
   10594                 :             :                                strVal(list_nth(fkconstraint->fk_attrs, i)),
   10595                 :             :                                strVal(list_nth(fkconstraint->pk_attrs, i)),
   10596                 :             :                                format_type_be(fktype),
   10597                 :             :                                format_type_be(pktype))));
   10598                 :             : 
   10599                 :             :         /*
   10600                 :             :          * This shouldn't be possible, but better check to make sure we have a
   10601                 :             :          * consistent state for the check below.
   10602                 :             :          */
   10603   [ +  +  +  -  :        1968 :         if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
             +  +  -  + ]
   10604         [ #  # ]:           0 :             elog(ERROR, "key columns are not both collatable");
   10605                 :             : 
   10606   [ +  +  +  - ]:        1968 :         if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
   10607                 :             :         {
   10608                 :             :             bool        pkcolldet;
   10609                 :             :             bool        fkcolldet;
   10610                 :             : 
   10611                 :          73 :             pkcolldet = get_collation_isdeterministic(pkcoll);
   10612                 :          73 :             fkcolldet = get_collation_isdeterministic(fkcoll);
   10613                 :             : 
   10614                 :             :             /*
   10615                 :             :              * SQL requires that both collations are the same.  This is
   10616                 :             :              * because we need a consistent notion of equality on both
   10617                 :             :              * columns.  We relax this by allowing different collations if
   10618                 :             :              * they are both deterministic.  (This is also for backward
   10619                 :             :              * compatibility, because PostgreSQL has always allowed this.)
   10620                 :             :              */
   10621   [ +  +  +  +  :          73 :             if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
                   +  + ]
   10622         [ +  - ]:           8 :                 ereport(ERROR,
   10623                 :             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
   10624                 :             :                          errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
   10625                 :             :                          errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
   10626                 :             :                                    "have incompatible collations: \"%s\" and \"%s\".  "
   10627                 :             :                                    "If either collation is nondeterministic, then both collations have to be the same.",
   10628                 :             :                                    strVal(list_nth(fkconstraint->fk_attrs, i)),
   10629                 :             :                                    strVal(list_nth(fkconstraint->pk_attrs, i)),
   10630                 :             :                                    get_collation_name(fkcoll),
   10631                 :             :                                    get_collation_name(pkcoll))));
   10632                 :             :         }
   10633                 :             : 
   10634         [ +  + ]:        1960 :         if (old_check_ok)
   10635                 :             :         {
   10636                 :             :             /*
   10637                 :             :              * When a pfeqop changes, revalidate the constraint.  We could
   10638                 :             :              * permit intra-opfamily changes, but that adds subtle complexity
   10639                 :             :              * without any concrete benefit for core types.  We need not
   10640                 :             :              * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
   10641                 :             :              */
   10642                 :           4 :             old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
   10643                 :           4 :             old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
   10644                 :             :                                     old_pfeqop_item);
   10645                 :             :         }
   10646         [ +  + ]:        1960 :         if (old_check_ok)
   10647                 :             :         {
   10648                 :             :             Oid         old_fktype;
   10649                 :             :             Oid         new_fktype;
   10650                 :             :             CoercionPathType old_pathtype;
   10651                 :             :             CoercionPathType new_pathtype;
   10652                 :             :             Oid         old_castfunc;
   10653                 :             :             Oid         new_castfunc;
   10654                 :             :             Oid         old_fkcoll;
   10655                 :             :             Oid         new_fkcoll;
   10656                 :           4 :             Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
   10657                 :           4 :                                                    fkattnum[i] - 1);
   10658                 :             : 
   10659                 :             :             /*
   10660                 :             :              * Identify coercion pathways from each of the old and new FK-side
   10661                 :             :              * column types to the right (foreign) operand type of the pfeqop.
   10662                 :             :              * We may assume that pg_constraint.conkey is not changing.
   10663                 :             :              */
   10664                 :           4 :             old_fktype = attr->atttypid;
   10665                 :           4 :             new_fktype = fktype;
   10666                 :           4 :             old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
   10667                 :             :                                         &old_castfunc);
   10668                 :           4 :             new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
   10669                 :             :                                         &new_castfunc);
   10670                 :             : 
   10671                 :           4 :             old_fkcoll = attr->attcollation;
   10672                 :           4 :             new_fkcoll = fkcoll;
   10673                 :             : 
   10674                 :             :             /*
   10675                 :             :              * Upon a change to the cast from the FK column to its pfeqop
   10676                 :             :              * operand, revalidate the constraint.  For this evaluation, a
   10677                 :             :              * binary coercion cast is equivalent to no cast at all.  While
   10678                 :             :              * type implementors should design implicit casts with an eye
   10679                 :             :              * toward consistency of operations like equality, we cannot
   10680                 :             :              * assume here that they have done so.
   10681                 :             :              *
   10682                 :             :              * A function with a polymorphic argument could change behavior
   10683                 :             :              * arbitrarily in response to get_fn_expr_argtype().  Therefore,
   10684                 :             :              * when the cast destination is polymorphic, we only avoid
   10685                 :             :              * revalidation if the input type has not changed at all.  Given
   10686                 :             :              * just the core data types and operator classes, this requirement
   10687                 :             :              * prevents no would-be optimizations.
   10688                 :             :              *
   10689                 :             :              * If the cast converts from a base type to a domain thereon, then
   10690                 :             :              * that domain type must be the opcintype of the unique index.
   10691                 :             :              * Necessarily, the primary key column must then be of the domain
   10692                 :             :              * type.  Since the constraint was previously valid, all values on
   10693                 :             :              * the foreign side necessarily exist on the primary side and in
   10694                 :             :              * turn conform to the domain.  Consequently, we need not treat
   10695                 :             :              * domains specially here.
   10696                 :             :              *
   10697                 :             :              * If the collation changes, revalidation is required, unless both
   10698                 :             :              * collations are deterministic, because those share the same
   10699                 :             :              * notion of equality (because texteq reduces to bitwise
   10700                 :             :              * equality).
   10701                 :             :              *
   10702                 :             :              * We need not directly consider the PK type.  It's necessarily
   10703                 :             :              * binary coercible to the opcintype of the unique index column,
   10704                 :             :              * and ri_triggers.c will only deal with PK datums in terms of
   10705                 :             :              * that opcintype.  Changing the opcintype also changes pfeqop.
   10706                 :             :              */
   10707                 :           4 :             old_check_ok = (new_pathtype == old_pathtype &&
   10708   [ +  -  +  - ]:           4 :                             new_castfunc == old_castfunc &&
   10709   [ +  -  +  -  :           4 :                             (!IsPolymorphicType(pfeqop_right) ||
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  +  -  -  
                +  -  - ]
   10710   [ +  -  -  + ]:           8 :                              new_fktype == old_fktype) &&
   10711         [ #  # ]:           0 :                             (new_fkcoll == old_fkcoll ||
   10712         [ #  # ]:           0 :                              (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
   10713                 :             :         }
   10714                 :             : 
   10715                 :        1960 :         pfeqoperators[i] = pfeqop;
   10716                 :        1960 :         ppeqoperators[i] = ppeqop;
   10717                 :        1960 :         ffeqoperators[i] = ffeqop;
   10718                 :             :     }
   10719                 :             : 
   10720                 :             :     /*
   10721                 :             :      * For FKs with PERIOD we need additional operators to check whether the
   10722                 :             :      * referencing row's range is contained by the aggregated ranges of the
   10723                 :             :      * referenced row(s). For rangetypes and multirangetypes this is
   10724                 :             :      * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
   10725                 :             :      * support for now. FKs will look these up at "runtime", but we should
   10726                 :             :      * make sure the lookup works here, even if we don't use the values.
   10727                 :             :      */
   10728         [ +  + ]:        1503 :     if (with_period)
   10729                 :             :     {
   10730                 :             :         Oid         periodoperoid;
   10731                 :             :         Oid         aggedperiodoperoid;
   10732                 :             :         Oid         intersectoperoid;
   10733                 :             : 
   10734                 :         118 :         FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
   10735                 :             :                           &intersectoperoid);
   10736                 :             :     }
   10737                 :             : 
   10738                 :             :     /* First, create the constraint catalog entry itself. */
   10739                 :        1503 :     address = addFkConstraint(addFkBothSides,
   10740                 :             :                               fkconstraint->conname, fkconstraint, rel, pkrel,
   10741                 :             :                               indexOid,
   10742                 :             :                               InvalidOid,   /* no parent constraint */
   10743                 :             :                               numfks,
   10744                 :             :                               pkattnum,
   10745                 :             :                               fkattnum,
   10746                 :             :                               pfeqoperators,
   10747                 :             :                               ppeqoperators,
   10748                 :             :                               ffeqoperators,
   10749                 :             :                               numfkdelsetcols,
   10750                 :             :                               fkdelsetcols,
   10751                 :             :                               false,
   10752                 :             :                               with_period);
   10753                 :             : 
   10754                 :             :     /* Next process the action triggers at the referenced side and recurse */
   10755                 :        1503 :     addFkRecurseReferenced(fkconstraint, rel, pkrel,
   10756                 :             :                            indexOid,
   10757                 :             :                            address.objectId,
   10758                 :             :                            numfks,
   10759                 :             :                            pkattnum,
   10760                 :             :                            fkattnum,
   10761                 :             :                            pfeqoperators,
   10762                 :             :                            ppeqoperators,
   10763                 :             :                            ffeqoperators,
   10764                 :             :                            numfkdelsetcols,
   10765                 :             :                            fkdelsetcols,
   10766                 :             :                            old_check_ok,
   10767                 :             :                            InvalidOid, InvalidOid,
   10768                 :             :                            with_period);
   10769                 :             : 
   10770                 :             :     /* Lastly create the check triggers at the referencing side and recurse */
   10771                 :        1503 :     addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
   10772                 :             :                             indexOid,
   10773                 :             :                             address.objectId,
   10774                 :             :                             numfks,
   10775                 :             :                             pkattnum,
   10776                 :             :                             fkattnum,
   10777                 :             :                             pfeqoperators,
   10778                 :             :                             ppeqoperators,
   10779                 :             :                             ffeqoperators,
   10780                 :             :                             numfkdelsetcols,
   10781                 :             :                             fkdelsetcols,
   10782                 :             :                             old_check_ok,
   10783                 :             :                             lockmode,
   10784                 :             :                             InvalidOid, InvalidOid,
   10785                 :             :                             with_period);
   10786                 :             : 
   10787                 :             :     /*
   10788                 :             :      * Done.  Close pk table, but keep lock until we've committed.
   10789                 :             :      */
   10790                 :        1503 :     table_close(pkrel, NoLock);
   10791                 :             : 
   10792                 :        1503 :     return address;
   10793                 :             : }
   10794                 :             : 
   10795                 :             : /*
   10796                 :             :  * validateFkOnDeleteSetColumns
   10797                 :             :  *      Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
   10798                 :             :  *      column lists are valid.
   10799                 :             :  *
   10800                 :             :  * If there are duplicates in the fksetcolsattnums[] array, this silently
   10801                 :             :  * removes the dups.  The new count of numfksetcols is returned.
   10802                 :             :  */
   10803                 :             : static int
   10804                 :        1819 : validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
   10805                 :             :                              int numfksetcols, int16 *fksetcolsattnums,
   10806                 :             :                              List *fksetcols)
   10807                 :             : {
   10808                 :        1819 :     int         numcolsout = 0;
   10809                 :             : 
   10810         [ +  + ]:        1839 :     for (int i = 0; i < numfksetcols; i++)
   10811                 :             :     {
   10812                 :          24 :         int16       setcol_attnum = fksetcolsattnums[i];
   10813                 :          24 :         bool        seen = false;
   10814                 :             : 
   10815                 :             :         /* Make sure it's in fkattnums[] */
   10816         [ +  + ]:          44 :         for (int j = 0; j < numfks; j++)
   10817                 :             :         {
   10818         [ +  + ]:          40 :             if (fkattnums[j] == setcol_attnum)
   10819                 :             :             {
   10820                 :          20 :                 seen = true;
   10821                 :          20 :                 break;
   10822                 :             :             }
   10823                 :             :         }
   10824                 :             : 
   10825         [ +  + ]:          24 :         if (!seen)
   10826                 :             :         {
   10827                 :           4 :             char       *col = strVal(list_nth(fksetcols, i));
   10828                 :             : 
   10829         [ +  - ]:           4 :             ereport(ERROR,
   10830                 :             :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   10831                 :             :                      errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
   10832                 :             :         }
   10833                 :             : 
   10834                 :             :         /* Now check for dups */
   10835                 :          20 :         seen = false;
   10836         [ +  + ]:          20 :         for (int j = 0; j < numcolsout; j++)
   10837                 :             :         {
   10838         [ +  - ]:           4 :             if (fksetcolsattnums[j] == setcol_attnum)
   10839                 :             :             {
   10840                 :           4 :                 seen = true;
   10841                 :           4 :                 break;
   10842                 :             :             }
   10843                 :             :         }
   10844         [ +  + ]:          20 :         if (!seen)
   10845                 :          16 :             fksetcolsattnums[numcolsout++] = setcol_attnum;
   10846                 :             :     }
   10847                 :        1815 :     return numcolsout;
   10848                 :             : }
   10849                 :             : 
   10850                 :             : /*
   10851                 :             :  * addFkConstraint
   10852                 :             :  *      Install pg_constraint entries to implement a foreign key constraint.
   10853                 :             :  *      Caller must separately invoke addFkRecurseReferenced and
   10854                 :             :  *      addFkRecurseReferencing, as appropriate, to install pg_trigger entries
   10855                 :             :  *      and (for partitioned tables) recurse to partitions.
   10856                 :             :  *
   10857                 :             :  * fkside: the side of the FK (or both) to create.  Caller should
   10858                 :             :  *      call addFkRecurseReferenced if this is addFkReferencedSide,
   10859                 :             :  *      addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
   10860                 :             :  *      addFkBothSides.
   10861                 :             :  * constraintname: the base name for the constraint being added,
   10862                 :             :  *      copied to fkconstraint->conname if the latter is not set
   10863                 :             :  * fkconstraint: the constraint being added
   10864                 :             :  * rel: the root referencing relation
   10865                 :             :  * pkrel: the referenced relation; might be a partition, if recursing
   10866                 :             :  * indexOid: the OID of the index (on pkrel) implementing this constraint
   10867                 :             :  * parentConstr: the OID of a parent constraint; InvalidOid if this is a
   10868                 :             :  *      top-level constraint
   10869                 :             :  * numfks: the number of columns in the foreign key
   10870                 :             :  * pkattnum: the attnum array of referenced attributes
   10871                 :             :  * fkattnum: the attnum array of referencing attributes
   10872                 :             :  * pf/pp/ffeqoperators: OID array of operators between columns
   10873                 :             :  * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
   10874                 :             :  *      (...) clause
   10875                 :             :  * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
   10876                 :             :  *      NULL/DEFAULT clause
   10877                 :             :  * with_period: true if this is a temporal FK
   10878                 :             :  */
   10879                 :             : static ObjectAddress
   10880                 :        2870 : addFkConstraint(addFkConstraintSides fkside,
   10881                 :             :                 char *constraintname, Constraint *fkconstraint,
   10882                 :             :                 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
   10883                 :             :                 int numfks, int16 *pkattnum,
   10884                 :             :                 int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
   10885                 :             :                 Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
   10886                 :             :                 bool is_internal, bool with_period)
   10887                 :             : {
   10888                 :             :     ObjectAddress address;
   10889                 :             :     Oid         constrOid;
   10890                 :             :     char       *conname;
   10891                 :             :     bool        conislocal;
   10892                 :             :     int16       coninhcount;
   10893                 :             :     bool        connoinherit;
   10894                 :             : 
   10895                 :             :     /*
   10896                 :             :      * Verify relkind for each referenced partition.  At the top level, this
   10897                 :             :      * is redundant with a previous check, but we need it when recursing.
   10898                 :             :      */
   10899         [ +  + ]:        2870 :     if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
   10900         [ -  + ]:         589 :         pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
   10901         [ #  # ]:           0 :         ereport(ERROR,
   10902                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   10903                 :             :                  errmsg("referenced relation \"%s\" is not a table",
   10904                 :             :                         RelationGetRelationName(pkrel))));
   10905                 :             : 
   10906                 :             :     /*
   10907                 :             :      * Caller supplies us with a constraint name; however, it may be used in
   10908                 :             :      * this partition, so come up with a different one in that case.  Unless
   10909                 :             :      * truncation to NAMEDATALEN dictates otherwise, the new name will be the
   10910                 :             :      * supplied name with an underscore and digit(s) appended.
   10911                 :             :      */
   10912         [ +  + ]:        2870 :     if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
   10913                 :             :                              RelationGetRelid(rel),
   10914                 :             :                              constraintname))
   10915                 :         811 :         conname = ChooseConstraintName(constraintname,
   10916                 :             :                                        NULL,
   10917                 :             :                                        "",
   10918                 :         811 :                                        RelationGetNamespace(rel), NIL);
   10919                 :             :     else
   10920                 :        2059 :         conname = constraintname;
   10921                 :             : 
   10922         [ +  + ]:        2870 :     if (fkconstraint->conname == NULL)
   10923                 :         283 :         fkconstraint->conname = pstrdup(conname);
   10924                 :             : 
   10925         [ +  + ]:        2870 :     if (OidIsValid(parentConstr))
   10926                 :             :     {
   10927                 :        1367 :         conislocal = false;
   10928                 :        1367 :         coninhcount = 1;
   10929                 :        1367 :         connoinherit = false;
   10930                 :             :     }
   10931                 :             :     else
   10932                 :             :     {
   10933                 :        1503 :         conislocal = true;
   10934                 :        1503 :         coninhcount = 0;
   10935                 :             : 
   10936                 :             :         /*
   10937                 :             :          * always inherit for partitioned tables, never for legacy inheritance
   10938                 :             :          */
   10939                 :        1503 :         connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
   10940                 :             :     }
   10941                 :             : 
   10942                 :             :     /*
   10943                 :             :      * Record the FK constraint in pg_constraint.
   10944                 :             :      */
   10945                 :        2870 :     constrOid = CreateConstraintEntry(conname,
   10946                 :        2870 :                                       RelationGetNamespace(rel),
   10947                 :             :                                       CONSTRAINT_FOREIGN,
   10948                 :        2870 :                                       fkconstraint->deferrable,
   10949                 :        2870 :                                       fkconstraint->initdeferred,
   10950                 :        2870 :                                       fkconstraint->is_enforced,
   10951                 :        2870 :                                       fkconstraint->initially_valid,
   10952                 :             :                                       parentConstr,
   10953                 :             :                                       RelationGetRelid(rel),
   10954                 :             :                                       fkattnum,
   10955                 :             :                                       numfks,
   10956                 :             :                                       numfks,
   10957                 :             :                                       InvalidOid,   /* not a domain constraint */
   10958                 :             :                                       indexOid,
   10959                 :             :                                       RelationGetRelid(pkrel),
   10960                 :             :                                       pkattnum,
   10961                 :             :                                       pfeqoperators,
   10962                 :             :                                       ppeqoperators,
   10963                 :             :                                       ffeqoperators,
   10964                 :             :                                       numfks,
   10965                 :        2870 :                                       fkconstraint->fk_upd_action,
   10966                 :        2870 :                                       fkconstraint->fk_del_action,
   10967                 :             :                                       fkdelsetcols,
   10968                 :             :                                       numfkdelsetcols,
   10969                 :        2870 :                                       fkconstraint->fk_matchtype,
   10970                 :             :                                       NULL, /* no exclusion constraint */
   10971                 :             :                                       NULL, /* no check constraint */
   10972                 :             :                                       NULL,
   10973                 :             :                                       conislocal,   /* islocal */
   10974                 :             :                                       coninhcount,  /* inhcount */
   10975                 :             :                                       connoinherit, /* conNoInherit */
   10976                 :             :                                       with_period,  /* conPeriod */
   10977                 :             :                                       is_internal); /* is_internal */
   10978                 :             : 
   10979                 :        2870 :     ObjectAddressSet(address, ConstraintRelationId, constrOid);
   10980                 :             : 
   10981                 :             :     /*
   10982                 :             :      * In partitioning cases, create the dependency entries for this
   10983                 :             :      * constraint.  (For non-partitioned cases, relevant entries were created
   10984                 :             :      * by CreateConstraintEntry.)
   10985                 :             :      *
   10986                 :             :      * On the referenced side, we need the constraint to have an internal
   10987                 :             :      * dependency on its parent constraint; this means that this constraint
   10988                 :             :      * cannot be dropped on its own -- only through the parent constraint. It
   10989                 :             :      * also means the containing partition cannot be dropped on its own, but
   10990                 :             :      * it can be detached, at which point this dependency is removed (after
   10991                 :             :      * verifying that no rows are referenced via this FK.)
   10992                 :             :      *
   10993                 :             :      * When processing the referencing side, we link the constraint via the
   10994                 :             :      * special partitioning dependencies: the parent constraint is the primary
   10995                 :             :      * dependent, and the partition on which the foreign key exists is the
   10996                 :             :      * secondary dependency.  That way, this constraint is dropped if either
   10997                 :             :      * of these objects is.
   10998                 :             :      *
   10999                 :             :      * Note that this is only necessary for the subsidiary pg_constraint rows
   11000                 :             :      * in partitions; the topmost row doesn't need any of this.
   11001                 :             :      */
   11002         [ +  + ]:        2870 :     if (OidIsValid(parentConstr))
   11003                 :             :     {
   11004                 :             :         ObjectAddress referenced;
   11005                 :             : 
   11006                 :        1367 :         ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
   11007                 :             : 
   11008                 :             :         Assert(fkside != addFkBothSides);
   11009         [ +  + ]:        1367 :         if (fkside == addFkReferencedSide)
   11010                 :         807 :             recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
   11011                 :             :         else
   11012                 :             :         {
   11013                 :         560 :             recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
   11014                 :         560 :             ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
   11015                 :         560 :             recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
   11016                 :             :         }
   11017                 :             :     }
   11018                 :             : 
   11019                 :             :     /* make new constraint visible, in case we add more */
   11020                 :        2870 :     CommandCounterIncrement();
   11021                 :             : 
   11022                 :        2870 :     return address;
   11023                 :             : }
   11024                 :             : 
   11025                 :             : /*
   11026                 :             :  * addFkRecurseReferenced
   11027                 :             :  *      Recursive helper for the referenced side of foreign key creation,
   11028                 :             :  *      which creates the action triggers and recurses
   11029                 :             :  *
   11030                 :             :  * If the referenced relation is a plain relation, create the necessary action
   11031                 :             :  * triggers that implement the constraint.  If the referenced relation is a
   11032                 :             :  * partitioned table, then we create a pg_constraint row referencing the parent
   11033                 :             :  * of the referencing side for it and recurse on this routine for each
   11034                 :             :  * partition.
   11035                 :             :  *
   11036                 :             :  * fkconstraint: the constraint being added
   11037                 :             :  * rel: the root referencing relation
   11038                 :             :  * pkrel: the referenced relation; might be a partition, if recursing
   11039                 :             :  * indexOid: the OID of the index (on pkrel) implementing this constraint
   11040                 :             :  * parentConstr: the OID of a parent constraint; InvalidOid if this is a
   11041                 :             :  *      top-level constraint
   11042                 :             :  * numfks: the number of columns in the foreign key
   11043                 :             :  * pkattnum: the attnum array of referenced attributes
   11044                 :             :  * fkattnum: the attnum array of referencing attributes
   11045                 :             :  * numfkdelsetcols: the number of columns in the ON DELETE SET
   11046                 :             :  *      NULL/DEFAULT (...) clause
   11047                 :             :  * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
   11048                 :             :  *      NULL/DEFAULT clause
   11049                 :             :  * pf/pp/ffeqoperators: OID array of operators between columns
   11050                 :             :  * old_check_ok: true if this constraint replaces an existing one that
   11051                 :             :  *      was already validated (thus this one doesn't need validation)
   11052                 :             :  * parentDelTrigger and parentUpdTrigger: when recursively called on a
   11053                 :             :  *      partition, the OIDs of the parent action triggers for DELETE and
   11054                 :             :  *      UPDATE respectively.
   11055                 :             :  * with_period: true if this is a temporal FK
   11056                 :             :  */
   11057                 :             : static void
   11058                 :        2382 : addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
   11059                 :             :                        Relation pkrel, Oid indexOid, Oid parentConstr,
   11060                 :             :                        int numfks,
   11061                 :             :                        int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
   11062                 :             :                        Oid *ppeqoperators, Oid *ffeqoperators,
   11063                 :             :                        int numfkdelsetcols, int16 *fkdelsetcols,
   11064                 :             :                        bool old_check_ok,
   11065                 :             :                        Oid parentDelTrigger, Oid parentUpdTrigger,
   11066                 :             :                        bool with_period)
   11067                 :             : {
   11068                 :        2382 :     Oid         deleteTriggerOid = InvalidOid,
   11069                 :        2382 :                 updateTriggerOid = InvalidOid;
   11070                 :             : 
   11071                 :             :     Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
   11072                 :             :     Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
   11073                 :             : 
   11074                 :             :     /*
   11075                 :             :      * Create action triggers to enforce the constraint, or skip them if the
   11076                 :             :      * constraint is NOT ENFORCED.
   11077                 :             :      */
   11078         [ +  + ]:        2382 :     if (fkconstraint->is_enforced)
   11079                 :        2329 :         createForeignKeyActionTriggers(RelationGetRelid(rel),
   11080                 :             :                                        RelationGetRelid(pkrel),
   11081                 :             :                                        fkconstraint,
   11082                 :             :                                        parentConstr, indexOid,
   11083                 :             :                                        parentDelTrigger, parentUpdTrigger,
   11084                 :             :                                        &deleteTriggerOid, &updateTriggerOid);
   11085                 :             : 
   11086                 :             :     /*
   11087                 :             :      * If the referenced table is partitioned, recurse on ourselves to handle
   11088                 :             :      * each partition.  We need one pg_constraint row created for each
   11089                 :             :      * partition in addition to the pg_constraint row for the parent table.
   11090                 :             :      */
   11091         [ +  + ]:        2382 :     if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11092                 :             :     {
   11093                 :         373 :         PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
   11094                 :             : 
   11095         [ +  + ]:        1048 :         for (int i = 0; i < pd->nparts; i++)
   11096                 :             :         {
   11097                 :             :             Relation    partRel;
   11098                 :             :             AttrMap    *map;
   11099                 :             :             AttrNumber *mapped_pkattnum;
   11100                 :             :             Oid         partIndexId;
   11101                 :             :             ObjectAddress address;
   11102                 :             : 
   11103                 :             :             /* XXX would it be better to acquire these locks beforehand? */
   11104                 :         675 :             partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
   11105                 :             : 
   11106                 :             :             /*
   11107                 :             :              * Map the attribute numbers in the referenced side of the FK
   11108                 :             :              * definition to match the partition's column layout.
   11109                 :             :              */
   11110                 :         675 :             map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
   11111                 :             :                                                RelationGetDescr(pkrel),
   11112                 :             :                                                false);
   11113         [ +  + ]:         675 :             if (map)
   11114                 :             :             {
   11115                 :          89 :                 mapped_pkattnum = palloc_array(AttrNumber, numfks);
   11116         [ +  + ]:         186 :                 for (int j = 0; j < numfks; j++)
   11117                 :          97 :                     mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
   11118                 :             :             }
   11119                 :             :             else
   11120                 :         586 :                 mapped_pkattnum = pkattnum;
   11121                 :             : 
   11122                 :             :             /* Determine the index to use at this level */
   11123                 :         675 :             partIndexId = index_get_partition(partRel, indexOid);
   11124         [ -  + ]:         675 :             if (!OidIsValid(partIndexId))
   11125         [ #  # ]:           0 :                 elog(ERROR, "index for %u not found in partition %s",
   11126                 :             :                      indexOid, RelationGetRelationName(partRel));
   11127                 :             : 
   11128                 :             :             /* Create entry at this level ... */
   11129                 :         675 :             address = addFkConstraint(addFkReferencedSide,
   11130                 :             :                                       fkconstraint->conname, fkconstraint, rel,
   11131                 :             :                                       partRel, partIndexId, parentConstr,
   11132                 :             :                                       numfks, mapped_pkattnum,
   11133                 :             :                                       fkattnum, pfeqoperators, ppeqoperators,
   11134                 :             :                                       ffeqoperators, numfkdelsetcols,
   11135                 :             :                                       fkdelsetcols, true, with_period);
   11136                 :             :             /* ... and recurse to our children */
   11137                 :         675 :             addFkRecurseReferenced(fkconstraint, rel, partRel,
   11138                 :             :                                    partIndexId, address.objectId, numfks,
   11139                 :             :                                    mapped_pkattnum, fkattnum,
   11140                 :             :                                    pfeqoperators, ppeqoperators, ffeqoperators,
   11141                 :             :                                    numfkdelsetcols, fkdelsetcols,
   11142                 :             :                                    old_check_ok,
   11143                 :             :                                    deleteTriggerOid, updateTriggerOid,
   11144                 :             :                                    with_period);
   11145                 :             : 
   11146                 :             :             /* Done -- clean up (but keep the lock) */
   11147                 :         675 :             table_close(partRel, NoLock);
   11148         [ +  + ]:         675 :             if (map)
   11149                 :             :             {
   11150                 :          89 :                 pfree(mapped_pkattnum);
   11151                 :          89 :                 free_attrmap(map);
   11152                 :             :             }
   11153                 :             :         }
   11154                 :             :     }
   11155                 :        2382 : }
   11156                 :             : 
   11157                 :             : /*
   11158                 :             :  * addFkRecurseReferencing
   11159                 :             :  *      Recursive helper for the referencing side of foreign key creation,
   11160                 :             :  *      which creates the check triggers and recurses
   11161                 :             :  *
   11162                 :             :  * If the referencing relation is a plain relation, create the necessary check
   11163                 :             :  * triggers that implement the constraint, and set up for Phase 3 constraint
   11164                 :             :  * verification.  If the referencing relation is a partitioned table, then
   11165                 :             :  * we create a pg_constraint row for it and recurse on this routine for each
   11166                 :             :  * partition.
   11167                 :             :  *
   11168                 :             :  * We assume that the referenced relation is locked against concurrent
   11169                 :             :  * deletions.  If it's a partitioned relation, every partition must be so
   11170                 :             :  * locked.
   11171                 :             :  *
   11172                 :             :  * wqueue: the ALTER TABLE work queue; NULL when not running as part
   11173                 :             :  *      of an ALTER TABLE sequence.
   11174                 :             :  * fkconstraint: the constraint being added
   11175                 :             :  * rel: the referencing relation; might be a partition, if recursing
   11176                 :             :  * pkrel: the root referenced relation
   11177                 :             :  * indexOid: the OID of the index (on pkrel) implementing this constraint
   11178                 :             :  * parentConstr: the OID of the parent constraint (there is always one)
   11179                 :             :  * numfks: the number of columns in the foreign key
   11180                 :             :  * pkattnum: the attnum array of referenced attributes
   11181                 :             :  * fkattnum: the attnum array of referencing attributes
   11182                 :             :  * pf/pp/ffeqoperators: OID array of operators between columns
   11183                 :             :  * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
   11184                 :             :  *      (...) clause
   11185                 :             :  * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
   11186                 :             :  *      NULL/DEFAULT clause
   11187                 :             :  * old_check_ok: true if this constraint replaces an existing one that
   11188                 :             :  *      was already validated (thus this one doesn't need validation)
   11189                 :             :  * lockmode: the lockmode to acquire on partitions when recursing
   11190                 :             :  * parentInsTrigger and parentUpdTrigger: when being recursively called on
   11191                 :             :  *      a partition, the OIDs of the parent check triggers for INSERT and
   11192                 :             :  *      UPDATE respectively.
   11193                 :             :  * with_period: true if this is a temporal FK
   11194                 :             :  */
   11195                 :             : static void
   11196                 :        2063 : addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
   11197                 :             :                         Relation pkrel, Oid indexOid, Oid parentConstr,
   11198                 :             :                         int numfks, int16 *pkattnum, int16 *fkattnum,
   11199                 :             :                         Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
   11200                 :             :                         int numfkdelsetcols, int16 *fkdelsetcols,
   11201                 :             :                         bool old_check_ok, LOCKMODE lockmode,
   11202                 :             :                         Oid parentInsTrigger, Oid parentUpdTrigger,
   11203                 :             :                         bool with_period)
   11204                 :             : {
   11205                 :        2063 :     Oid         insertTriggerOid = InvalidOid,
   11206                 :        2063 :                 updateTriggerOid = InvalidOid;
   11207                 :             : 
   11208                 :             :     Assert(OidIsValid(parentConstr));
   11209                 :             :     Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
   11210                 :             :     Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
   11211                 :             : 
   11212         [ -  + ]:        2063 :     if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   11213         [ #  # ]:           0 :         ereport(ERROR,
   11214                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11215                 :             :                  errmsg("foreign key constraints are not supported on foreign tables")));
   11216                 :             : 
   11217                 :             :     /*
   11218                 :             :      * Add check triggers if the constraint is ENFORCED, and if needed,
   11219                 :             :      * schedule them to be checked in Phase 3.
   11220                 :             :      *
   11221                 :             :      * If the relation is partitioned, drill down to do it to its partitions.
   11222                 :             :      */
   11223         [ +  + ]:        2063 :     if (fkconstraint->is_enforced)
   11224                 :        2026 :         createForeignKeyCheckTriggers(RelationGetRelid(rel),
   11225                 :             :                                       RelationGetRelid(pkrel),
   11226                 :             :                                       fkconstraint,
   11227                 :             :                                       parentConstr,
   11228                 :             :                                       indexOid,
   11229                 :             :                                       parentInsTrigger, parentUpdTrigger,
   11230                 :             :                                       &insertTriggerOid, &updateTriggerOid);
   11231                 :             : 
   11232         [ +  + ]:        2063 :     if (rel->rd_rel->relkind == RELKIND_RELATION)
   11233                 :             :     {
   11234                 :             :         /*
   11235                 :             :          * Tell Phase 3 to check that the constraint is satisfied by existing
   11236                 :             :          * rows. We can skip this during table creation, when constraint is
   11237                 :             :          * specified as NOT ENFORCED, or when requested explicitly by
   11238                 :             :          * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
   11239                 :             :          * recreating a constraint following a SET DATA TYPE operation that
   11240                 :             :          * did not impugn its validity.
   11241                 :             :          */
   11242   [ +  +  +  +  :        1740 :         if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
                   +  + ]
   11243         [ +  - ]:         603 :             fkconstraint->is_enforced)
   11244                 :             :         {
   11245                 :             :             NewConstraint *newcon;
   11246                 :             :             AlteredTableInfo *tab;
   11247                 :             : 
   11248                 :         603 :             tab = ATGetQueueEntry(wqueue, rel);
   11249                 :             : 
   11250                 :         603 :             newcon = palloc0_object(NewConstraint);
   11251                 :         603 :             newcon->name = get_constraint_name(parentConstr);
   11252                 :         603 :             newcon->contype = CONSTR_FOREIGN;
   11253                 :         603 :             newcon->refrelid = RelationGetRelid(pkrel);
   11254                 :         603 :             newcon->refindid = indexOid;
   11255                 :         603 :             newcon->conid = parentConstr;
   11256                 :         603 :             newcon->conwithperiod = fkconstraint->fk_with_period;
   11257                 :         603 :             newcon->qual = (Node *) fkconstraint;
   11258                 :             : 
   11259                 :         603 :             tab->constraints = lappend(tab->constraints, newcon);
   11260                 :             :         }
   11261                 :             :     }
   11262         [ +  - ]:         323 :     else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11263                 :             :     {
   11264                 :         323 :         PartitionDesc pd = RelationGetPartitionDesc(rel, true);
   11265                 :             :         Relation    trigrel;
   11266                 :             : 
   11267                 :             :         /*
   11268                 :             :          * Triggers of the foreign keys will be manipulated a bunch of times
   11269                 :             :          * in the loop below.  To avoid repeatedly opening/closing the trigger
   11270                 :             :          * catalog relation, we open it here and pass it to the subroutines
   11271                 :             :          * called below.
   11272                 :             :          */
   11273                 :         323 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   11274                 :             : 
   11275                 :             :         /*
   11276                 :             :          * Recurse to take appropriate action on each partition; either we
   11277                 :             :          * find an existing constraint to reparent to ours, or we create a new
   11278                 :             :          * one.
   11279                 :             :          */
   11280         [ +  + ]:         608 :         for (int i = 0; i < pd->nparts; i++)
   11281                 :             :         {
   11282                 :         289 :             Relation    partition = table_open(pd->oids[i], lockmode);
   11283                 :             :             List       *partFKs;
   11284                 :             :             AttrMap    *attmap;
   11285                 :             :             AttrNumber  mapped_fkattnum[INDEX_MAX_KEYS];
   11286                 :             :             bool        attached;
   11287                 :             :             ObjectAddress address;
   11288                 :             : 
   11289                 :         289 :             CheckAlterTableIsSafe(partition);
   11290                 :             : 
   11291                 :         285 :             attmap = build_attrmap_by_name(RelationGetDescr(partition),
   11292                 :             :                                            RelationGetDescr(rel),
   11293                 :             :                                            false);
   11294         [ +  + ]:         714 :             for (int j = 0; j < numfks; j++)
   11295                 :         429 :                 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
   11296                 :             : 
   11297                 :             :             /* Check whether an existing constraint can be repurposed */
   11298                 :         285 :             partFKs = copyObject(RelationGetFKeyList(partition));
   11299                 :         285 :             attached = false;
   11300   [ +  +  +  +  :         581 :             foreach_node(ForeignKeyCacheInfo, fk, partFKs)
                   +  + ]
   11301                 :             :             {
   11302         [ +  + ]:          19 :                 if (tryAttachPartitionForeignKey(wqueue,
   11303                 :             :                                                  fk,
   11304                 :             :                                                  partition,
   11305                 :             :                                                  parentConstr,
   11306                 :             :                                                  numfks,
   11307                 :             :                                                  mapped_fkattnum,
   11308                 :             :                                                  pkattnum,
   11309                 :             :                                                  pfeqoperators,
   11310                 :             :                                                  insertTriggerOid,
   11311                 :             :                                                  updateTriggerOid,
   11312                 :             :                                                  trigrel))
   11313                 :             :                 {
   11314                 :           8 :                     attached = true;
   11315                 :           8 :                     break;
   11316                 :             :                 }
   11317                 :             :             }
   11318         [ +  + ]:         285 :             if (attached)
   11319                 :             :             {
   11320                 :           8 :                 table_close(partition, NoLock);
   11321                 :           8 :                 continue;
   11322                 :             :             }
   11323                 :             : 
   11324                 :             :             /*
   11325                 :             :              * No luck finding a good constraint to reuse; create our own.
   11326                 :             :              */
   11327                 :         277 :             address = addFkConstraint(addFkReferencingSide,
   11328                 :             :                                       fkconstraint->conname, fkconstraint,
   11329                 :             :                                       partition, pkrel, indexOid, parentConstr,
   11330                 :             :                                       numfks, pkattnum,
   11331                 :             :                                       mapped_fkattnum, pfeqoperators,
   11332                 :             :                                       ppeqoperators, ffeqoperators,
   11333                 :             :                                       numfkdelsetcols, fkdelsetcols, true,
   11334                 :             :                                       with_period);
   11335                 :             : 
   11336                 :             :             /* call ourselves to finalize the creation and we're done */
   11337                 :         277 :             addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
   11338                 :             :                                     indexOid,
   11339                 :             :                                     address.objectId,
   11340                 :             :                                     numfks,
   11341                 :             :                                     pkattnum,
   11342                 :             :                                     mapped_fkattnum,
   11343                 :             :                                     pfeqoperators,
   11344                 :             :                                     ppeqoperators,
   11345                 :             :                                     ffeqoperators,
   11346                 :             :                                     numfkdelsetcols,
   11347                 :             :                                     fkdelsetcols,
   11348                 :             :                                     old_check_ok,
   11349                 :             :                                     lockmode,
   11350                 :             :                                     insertTriggerOid,
   11351                 :             :                                     updateTriggerOid,
   11352                 :             :                                     with_period);
   11353                 :             : 
   11354                 :         277 :             table_close(partition, NoLock);
   11355                 :             :         }
   11356                 :             : 
   11357                 :         319 :         table_close(trigrel, RowExclusiveLock);
   11358                 :             :     }
   11359                 :        2059 : }
   11360                 :             : 
   11361                 :             : /*
   11362                 :             :  * CloneForeignKeyConstraints
   11363                 :             :  *      Clone foreign keys from a partitioned table to a newly acquired
   11364                 :             :  *      partition.
   11365                 :             :  *
   11366                 :             :  * partitionRel is a partition of parentRel, so we can be certain that it has
   11367                 :             :  * the same columns with the same datatypes.  The columns may be in different
   11368                 :             :  * order, though.
   11369                 :             :  *
   11370                 :             :  * wqueue must be passed to set up phase 3 constraint checking, unless the
   11371                 :             :  * referencing-side partition is known to be empty (such as in CREATE TABLE /
   11372                 :             :  * PARTITION OF).
   11373                 :             :  */
   11374                 :             : static void
   11375                 :        8153 : CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
   11376                 :             :                            Relation partitionRel)
   11377                 :             : {
   11378                 :             :     /* This only works for declarative partitioning */
   11379                 :             :     Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   11380                 :             : 
   11381                 :             :     /*
   11382                 :             :      * First, clone constraints where the parent is on the referencing side.
   11383                 :             :      */
   11384                 :        8153 :     CloneFkReferencing(wqueue, parentRel, partitionRel);
   11385                 :             : 
   11386                 :             :     /*
   11387                 :             :      * Clone constraints for which the parent is on the referenced side.
   11388                 :             :      */
   11389                 :        8141 :     CloneFkReferenced(parentRel, partitionRel);
   11390                 :        8141 : }
   11391                 :             : 
   11392                 :             : /*
   11393                 :             :  * CloneFkReferenced
   11394                 :             :  *      Subroutine for CloneForeignKeyConstraints
   11395                 :             :  *
   11396                 :             :  * Find all the FKs that have the parent relation on the referenced side;
   11397                 :             :  * clone those constraints to the given partition.  This is to be called
   11398                 :             :  * when the partition is being created or attached.
   11399                 :             :  *
   11400                 :             :  * This recurses to partitions, if the relation being attached is partitioned.
   11401                 :             :  * Recursion is done by calling addFkRecurseReferenced.
   11402                 :             :  */
   11403                 :             : static void
   11404                 :        8141 : CloneFkReferenced(Relation parentRel, Relation partitionRel)
   11405                 :             : {
   11406                 :             :     Relation    pg_constraint;
   11407                 :             :     AttrMap    *attmap;
   11408                 :             :     ListCell   *cell;
   11409                 :             :     SysScanDesc scan;
   11410                 :             :     ScanKeyData key[2];
   11411                 :             :     HeapTuple   tuple;
   11412                 :        8141 :     List       *clone = NIL;
   11413                 :             :     Relation    trigrel;
   11414                 :             : 
   11415                 :             :     /*
   11416                 :             :      * Search for any constraints where this partition's parent is in the
   11417                 :             :      * referenced side.  However, we must not clone any constraint whose
   11418                 :             :      * parent constraint is also going to be cloned, to avoid duplicates.  So
   11419                 :             :      * do it in two steps: first construct the list of constraints to clone,
   11420                 :             :      * then go over that list cloning those whose parents are not in the list.
   11421                 :             :      * (We must not rely on the parent being seen first, since the catalog
   11422                 :             :      * scan could return children first.)
   11423                 :             :      */
   11424                 :        8141 :     pg_constraint = table_open(ConstraintRelationId, RowShareLock);
   11425                 :        8141 :     ScanKeyInit(&key[0],
   11426                 :             :                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   11427                 :             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
   11428                 :        8141 :     ScanKeyInit(&key[1],
   11429                 :             :                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   11430                 :             :                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   11431                 :             :     /* This is a seqscan, as we don't have a usable index ... */
   11432                 :        8141 :     scan = systable_beginscan(pg_constraint, InvalidOid, true,
   11433                 :             :                               NULL, 2, key);
   11434         [ +  + ]:        8421 :     while ((tuple = systable_getnext(scan)) != NULL)
   11435                 :             :     {
   11436                 :         280 :         Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   11437                 :             : 
   11438                 :         280 :         clone = lappend_oid(clone, constrForm->oid);
   11439                 :             :     }
   11440                 :        8141 :     systable_endscan(scan);
   11441                 :        8141 :     table_close(pg_constraint, RowShareLock);
   11442                 :             : 
   11443                 :             :     /*
   11444                 :             :      * Triggers of the foreign keys will be manipulated a bunch of times in
   11445                 :             :      * the loop below.  To avoid repeatedly opening/closing the trigger
   11446                 :             :      * catalog relation, we open it here and pass it to the subroutines called
   11447                 :             :      * below.
   11448                 :             :      */
   11449                 :        8141 :     trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   11450                 :             : 
   11451                 :        8141 :     attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
   11452                 :             :                                    RelationGetDescr(parentRel),
   11453                 :             :                                    false);
   11454   [ +  +  +  +  :        8421 :     foreach(cell, clone)
                   +  + ]
   11455                 :             :     {
   11456                 :         280 :         Oid         constrOid = lfirst_oid(cell);
   11457                 :             :         Form_pg_constraint constrForm;
   11458                 :             :         Relation    fkRel;
   11459                 :             :         Oid         indexOid;
   11460                 :             :         Oid         partIndexId;
   11461                 :             :         int         numfks;
   11462                 :             :         AttrNumber  conkey[INDEX_MAX_KEYS];
   11463                 :             :         AttrNumber  mapped_confkey[INDEX_MAX_KEYS];
   11464                 :             :         AttrNumber  confkey[INDEX_MAX_KEYS];
   11465                 :             :         Oid         conpfeqop[INDEX_MAX_KEYS];
   11466                 :             :         Oid         conppeqop[INDEX_MAX_KEYS];
   11467                 :             :         Oid         conffeqop[INDEX_MAX_KEYS];
   11468                 :             :         int         numfkdelsetcols;
   11469                 :             :         AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   11470                 :             :         Constraint *fkconstraint;
   11471                 :             :         ObjectAddress address;
   11472                 :         280 :         Oid         deleteTriggerOid = InvalidOid,
   11473                 :         280 :                     updateTriggerOid = InvalidOid;
   11474                 :             : 
   11475                 :         280 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   11476         [ -  + ]:         280 :         if (!HeapTupleIsValid(tuple))
   11477         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   11478                 :         280 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   11479                 :             : 
   11480                 :             :         /*
   11481                 :             :          * As explained above: don't try to clone a constraint for which we're
   11482                 :             :          * going to clone the parent.
   11483                 :             :          */
   11484         [ +  + ]:         280 :         if (list_member_oid(clone, constrForm->conparentid))
   11485                 :             :         {
   11486                 :         148 :             ReleaseSysCache(tuple);
   11487                 :         148 :             continue;
   11488                 :             :         }
   11489                 :             : 
   11490                 :             :         /* We need the same lock level that CreateTrigger will acquire */
   11491                 :         132 :         fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
   11492                 :             : 
   11493                 :         132 :         indexOid = constrForm->conindid;
   11494                 :         132 :         DeconstructFkConstraintRow(tuple,
   11495                 :             :                                    &numfks,
   11496                 :             :                                    conkey,
   11497                 :             :                                    confkey,
   11498                 :             :                                    conpfeqop,
   11499                 :             :                                    conppeqop,
   11500                 :             :                                    conffeqop,
   11501                 :             :                                    &numfkdelsetcols,
   11502                 :             :                                    confdelsetcols);
   11503                 :             : 
   11504         [ +  + ]:         292 :         for (int i = 0; i < numfks; i++)
   11505                 :         160 :             mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
   11506                 :             : 
   11507                 :         132 :         fkconstraint = makeNode(Constraint);
   11508                 :         132 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   11509                 :         132 :         fkconstraint->conname = NameStr(constrForm->conname);
   11510                 :         132 :         fkconstraint->deferrable = constrForm->condeferrable;
   11511                 :         132 :         fkconstraint->initdeferred = constrForm->condeferred;
   11512                 :         132 :         fkconstraint->location = -1;
   11513                 :         132 :         fkconstraint->pktable = NULL;
   11514                 :             :         /* ->fk_attrs determined below */
   11515                 :         132 :         fkconstraint->pk_attrs = NIL;
   11516                 :         132 :         fkconstraint->fk_matchtype = constrForm->confmatchtype;
   11517                 :         132 :         fkconstraint->fk_upd_action = constrForm->confupdtype;
   11518                 :         132 :         fkconstraint->fk_del_action = constrForm->confdeltype;
   11519                 :         132 :         fkconstraint->fk_del_set_cols = NIL;
   11520                 :         132 :         fkconstraint->old_conpfeqop = NIL;
   11521                 :         132 :         fkconstraint->old_pktable_oid = InvalidOid;
   11522                 :         132 :         fkconstraint->is_enforced = constrForm->conenforced;
   11523                 :         132 :         fkconstraint->skip_validation = false;
   11524                 :         132 :         fkconstraint->initially_valid = constrForm->convalidated;
   11525                 :             : 
   11526                 :             :         /* set up colnames that are used to generate the constraint name */
   11527         [ +  + ]:         292 :         for (int i = 0; i < numfks; i++)
   11528                 :             :         {
   11529                 :             :             Form_pg_attribute att;
   11530                 :             : 
   11531                 :         160 :             att = TupleDescAttr(RelationGetDescr(fkRel),
   11532                 :         160 :                                 conkey[i] - 1);
   11533                 :         160 :             fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   11534                 :         160 :                                              makeString(NameStr(att->attname)));
   11535                 :             :         }
   11536                 :             : 
   11537                 :             :         /*
   11538                 :             :          * Add the new foreign key constraint pointing to the new partition.
   11539                 :             :          * Because this new partition appears in the referenced side of the
   11540                 :             :          * constraint, we don't need to set up for Phase 3 check.
   11541                 :             :          */
   11542                 :         132 :         partIndexId = index_get_partition(partitionRel, indexOid);
   11543         [ -  + ]:         132 :         if (!OidIsValid(partIndexId))
   11544         [ #  # ]:           0 :             elog(ERROR, "index for %u not found in partition %s",
   11545                 :             :                  indexOid, RelationGetRelationName(partitionRel));
   11546                 :             : 
   11547                 :             :         /*
   11548                 :             :          * Get the "action" triggers belonging to the constraint to pass as
   11549                 :             :          * parent OIDs for similar triggers that will be created on the
   11550                 :             :          * partition in addFkRecurseReferenced().
   11551                 :             :          */
   11552         [ +  + ]:         132 :         if (constrForm->conenforced)
   11553                 :         128 :             GetForeignKeyActionTriggers(trigrel, constrOid,
   11554                 :             :                                         constrForm->confrelid, constrForm->conrelid,
   11555                 :             :                                         &deleteTriggerOid, &updateTriggerOid);
   11556                 :             : 
   11557                 :             :         /* Add this constraint ... */
   11558                 :         132 :         address = addFkConstraint(addFkReferencedSide,
   11559                 :             :                                   fkconstraint->conname, fkconstraint, fkRel,
   11560                 :             :                                   partitionRel, partIndexId, constrOid,
   11561                 :             :                                   numfks, mapped_confkey,
   11562                 :             :                                   conkey, conpfeqop, conppeqop, conffeqop,
   11563                 :             :                                   numfkdelsetcols, confdelsetcols, false,
   11564                 :         132 :                                   constrForm->conperiod);
   11565                 :             :         /* ... and recurse */
   11566                 :         132 :         addFkRecurseReferenced(fkconstraint,
   11567                 :             :                                fkRel,
   11568                 :             :                                partitionRel,
   11569                 :             :                                partIndexId,
   11570                 :             :                                address.objectId,
   11571                 :             :                                numfks,
   11572                 :             :                                mapped_confkey,
   11573                 :             :                                conkey,
   11574                 :             :                                conpfeqop,
   11575                 :             :                                conppeqop,
   11576                 :             :                                conffeqop,
   11577                 :             :                                numfkdelsetcols,
   11578                 :             :                                confdelsetcols,
   11579                 :             :                                true,
   11580                 :             :                                deleteTriggerOid,
   11581                 :             :                                updateTriggerOid,
   11582                 :         132 :                                constrForm->conperiod);
   11583                 :             : 
   11584                 :         132 :         table_close(fkRel, NoLock);
   11585                 :         132 :         ReleaseSysCache(tuple);
   11586                 :             :     }
   11587                 :             : 
   11588                 :        8141 :     table_close(trigrel, RowExclusiveLock);
   11589                 :        8141 : }
   11590                 :             : 
   11591                 :             : /*
   11592                 :             :  * CloneFkReferencing
   11593                 :             :  *      Subroutine for CloneForeignKeyConstraints
   11594                 :             :  *
   11595                 :             :  * For each FK constraint of the parent relation in the given list, find an
   11596                 :             :  * equivalent constraint in its partition relation that can be reparented;
   11597                 :             :  * if one cannot be found, create a new constraint in the partition as its
   11598                 :             :  * child.
   11599                 :             :  *
   11600                 :             :  * If wqueue is given, it is used to set up phase-3 verification for each
   11601                 :             :  * cloned constraint; omit it if such verification is not needed
   11602                 :             :  * (example: the partition is being created anew).
   11603                 :             :  */
   11604                 :             : static void
   11605                 :        8153 : CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
   11606                 :             : {
   11607                 :             :     AttrMap    *attmap;
   11608                 :             :     List       *partFKs;
   11609                 :        8153 :     List       *clone = NIL;
   11610                 :             :     ListCell   *cell;
   11611                 :             :     Relation    trigrel;
   11612                 :             : 
   11613                 :             :     /* obtain a list of constraints that we need to clone */
   11614   [ +  +  +  +  :        9012 :     foreach(cell, RelationGetFKeyList(parentRel))
                   +  + ]
   11615                 :             :     {
   11616                 :         863 :         ForeignKeyCacheInfo *fk = lfirst(cell);
   11617                 :             : 
   11618                 :             :         /*
   11619                 :             :          * Refuse to attach a table as partition that this partitioned table
   11620                 :             :          * already has a foreign key to.  This isn't useful schema, which is
   11621                 :             :          * proven by the fact that there have been no user complaints that
   11622                 :             :          * it's already impossible to achieve this in the opposite direction,
   11623                 :             :          * i.e., creating a foreign key that references a partition.  This
   11624                 :             :          * restriction allows us to dodge some complexities around
   11625                 :             :          * pg_constraint and pg_trigger row creations that would be needed
   11626                 :             :          * during ATTACH/DETACH for this kind of relationship.
   11627                 :             :          */
   11628         [ +  + ]:         863 :         if (fk->confrelid == RelationGetRelid(partRel))
   11629         [ +  - ]:           4 :             ereport(ERROR,
   11630                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   11631                 :             :                      errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
   11632                 :             :                             RelationGetRelationName(partRel),
   11633                 :             :                             get_constraint_name(fk->conoid))));
   11634                 :             : 
   11635                 :         859 :         clone = lappend_oid(clone, fk->conoid);
   11636                 :             :     }
   11637                 :             : 
   11638                 :             :     /*
   11639                 :             :      * Silently do nothing if there's nothing to do.  In particular, this
   11640                 :             :      * avoids throwing a spurious error for foreign tables.
   11641                 :             :      */
   11642         [ +  + ]:        8149 :     if (clone == NIL)
   11643                 :        7794 :         return;
   11644                 :             : 
   11645         [ -  + ]:         355 :     if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   11646         [ #  # ]:           0 :         ereport(ERROR,
   11647                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   11648                 :             :                  errmsg("foreign key constraints are not supported on foreign tables")));
   11649                 :             : 
   11650                 :             :     /*
   11651                 :             :      * Triggers of the foreign keys will be manipulated a bunch of times in
   11652                 :             :      * the loop below.  To avoid repeatedly opening/closing the trigger
   11653                 :             :      * catalog relation, we open it here and pass it to the subroutines called
   11654                 :             :      * below.
   11655                 :             :      */
   11656                 :         355 :     trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   11657                 :             : 
   11658                 :             :     /*
   11659                 :             :      * The constraint key may differ, if the columns in the partition are
   11660                 :             :      * different.  This map is used to convert them.
   11661                 :             :      */
   11662                 :         355 :     attmap = build_attrmap_by_name(RelationGetDescr(partRel),
   11663                 :             :                                    RelationGetDescr(parentRel),
   11664                 :             :                                    false);
   11665                 :             : 
   11666                 :         355 :     partFKs = copyObject(RelationGetFKeyList(partRel));
   11667                 :             : 
   11668   [ +  -  +  +  :        1206 :     foreach(cell, clone)
                   +  + ]
   11669                 :             :     {
   11670                 :         859 :         Oid         parentConstrOid = lfirst_oid(cell);
   11671                 :             :         Form_pg_constraint constrForm;
   11672                 :             :         Relation    pkrel;
   11673                 :             :         HeapTuple   tuple;
   11674                 :             :         int         numfks;
   11675                 :             :         AttrNumber  conkey[INDEX_MAX_KEYS];
   11676                 :             :         AttrNumber  mapped_conkey[INDEX_MAX_KEYS];
   11677                 :             :         AttrNumber  confkey[INDEX_MAX_KEYS];
   11678                 :             :         Oid         conpfeqop[INDEX_MAX_KEYS];
   11679                 :             :         Oid         conppeqop[INDEX_MAX_KEYS];
   11680                 :             :         Oid         conffeqop[INDEX_MAX_KEYS];
   11681                 :             :         int         numfkdelsetcols;
   11682                 :             :         AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   11683                 :             :         Constraint *fkconstraint;
   11684                 :             :         bool        attached;
   11685                 :             :         Oid         indexOid;
   11686                 :             :         ObjectAddress address;
   11687                 :             :         ListCell   *lc;
   11688                 :         859 :         Oid         insertTriggerOid = InvalidOid,
   11689                 :         859 :                     updateTriggerOid = InvalidOid;
   11690                 :             :         bool        with_period;
   11691                 :             : 
   11692                 :         859 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
   11693         [ -  + ]:         859 :         if (!HeapTupleIsValid(tuple))
   11694         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for constraint %u",
   11695                 :             :                  parentConstrOid);
   11696                 :         859 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   11697                 :             : 
   11698                 :             :         /* Don't clone constraints whose parents are being cloned */
   11699         [ +  + ]:         859 :         if (list_member_oid(clone, constrForm->conparentid))
   11700                 :             :         {
   11701                 :         472 :             ReleaseSysCache(tuple);
   11702                 :         572 :             continue;
   11703                 :             :         }
   11704                 :             : 
   11705                 :             :         /*
   11706                 :             :          * Need to prevent concurrent deletions.  If pkrel is a partitioned
   11707                 :             :          * relation, that means to lock all partitions.
   11708                 :             :          */
   11709                 :         387 :         pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
   11710         [ +  + ]:         387 :         if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   11711                 :         164 :             (void) find_all_inheritors(RelationGetRelid(pkrel),
   11712                 :             :                                        ShareRowExclusiveLock, NULL);
   11713                 :             : 
   11714                 :         387 :         DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
   11715                 :             :                                    conpfeqop, conppeqop, conffeqop,
   11716                 :             :                                    &numfkdelsetcols, confdelsetcols);
   11717         [ +  + ]:         930 :         for (int i = 0; i < numfks; i++)
   11718                 :         543 :             mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
   11719                 :             : 
   11720                 :             :         /*
   11721                 :             :          * Get the "check" triggers belonging to the constraint, if it is
   11722                 :             :          * ENFORCED, to pass as parent OIDs for similar triggers that will be
   11723                 :             :          * created on the partition in addFkRecurseReferencing().  They are
   11724                 :             :          * also passed to tryAttachPartitionForeignKey() below to simply
   11725                 :             :          * assign as parents to the partition's existing "check" triggers,
   11726                 :             :          * that is, if the corresponding constraints is deemed attachable to
   11727                 :             :          * the parent constraint.
   11728                 :             :          */
   11729         [ +  + ]:         387 :         if (constrForm->conenforced)
   11730                 :         379 :             GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
   11731                 :             :                                        constrForm->confrelid, constrForm->conrelid,
   11732                 :             :                                        &insertTriggerOid, &updateTriggerOid);
   11733                 :             : 
   11734                 :             :         /*
   11735                 :             :          * Before creating a new constraint, see whether any existing FKs are
   11736                 :             :          * fit for the purpose.  If one is, attach the parent constraint to
   11737                 :             :          * it, and don't clone anything.  This way we avoid the expensive
   11738                 :             :          * verification step and don't end up with a duplicate FK, and we
   11739                 :             :          * don't need to recurse to partitions for this constraint.
   11740                 :             :          */
   11741                 :         387 :         attached = false;
   11742   [ +  +  +  +  :         447 :         foreach(lc, partFKs)
                   +  + ]
   11743                 :             :         {
   11744                 :         164 :             ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
   11745                 :             : 
   11746         [ +  + ]:         164 :             if (tryAttachPartitionForeignKey(wqueue,
   11747                 :             :                                              fk,
   11748                 :             :                                              partRel,
   11749                 :             :                                              parentConstrOid,
   11750                 :             :                                              numfks,
   11751                 :             :                                              mapped_conkey,
   11752                 :             :                                              confkey,
   11753                 :             :                                              conpfeqop,
   11754                 :             :                                              insertTriggerOid,
   11755                 :             :                                              updateTriggerOid,
   11756                 :             :                                              trigrel))
   11757                 :             :             {
   11758                 :         100 :                 attached = true;
   11759                 :         100 :                 table_close(pkrel, NoLock);
   11760                 :         100 :                 break;
   11761                 :             :             }
   11762                 :             :         }
   11763         [ +  + ]:         383 :         if (attached)
   11764                 :             :         {
   11765                 :         100 :             ReleaseSysCache(tuple);
   11766                 :         100 :             continue;
   11767                 :             :         }
   11768                 :             : 
   11769                 :             :         /* No dice.  Set up to create our own constraint */
   11770                 :         283 :         fkconstraint = makeNode(Constraint);
   11771                 :         283 :         fkconstraint->contype = CONSTRAINT_FOREIGN;
   11772                 :             :         /* ->conname determined below */
   11773                 :         283 :         fkconstraint->deferrable = constrForm->condeferrable;
   11774                 :         283 :         fkconstraint->initdeferred = constrForm->condeferred;
   11775                 :         283 :         fkconstraint->location = -1;
   11776                 :         283 :         fkconstraint->pktable = NULL;
   11777                 :             :         /* ->fk_attrs determined below */
   11778                 :         283 :         fkconstraint->pk_attrs = NIL;
   11779                 :         283 :         fkconstraint->fk_matchtype = constrForm->confmatchtype;
   11780                 :         283 :         fkconstraint->fk_upd_action = constrForm->confupdtype;
   11781                 :         283 :         fkconstraint->fk_del_action = constrForm->confdeltype;
   11782                 :         283 :         fkconstraint->fk_del_set_cols = NIL;
   11783                 :         283 :         fkconstraint->old_conpfeqop = NIL;
   11784                 :         283 :         fkconstraint->old_pktable_oid = InvalidOid;
   11785                 :         283 :         fkconstraint->is_enforced = constrForm->conenforced;
   11786                 :         283 :         fkconstraint->skip_validation = false;
   11787                 :         283 :         fkconstraint->initially_valid = constrForm->convalidated;
   11788         [ +  + ]:         646 :         for (int i = 0; i < numfks; i++)
   11789                 :             :         {
   11790                 :             :             Form_pg_attribute att;
   11791                 :             : 
   11792                 :         363 :             att = TupleDescAttr(RelationGetDescr(partRel),
   11793                 :         363 :                                 mapped_conkey[i] - 1);
   11794                 :         363 :             fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   11795                 :         363 :                                              makeString(NameStr(att->attname)));
   11796                 :             :         }
   11797                 :             : 
   11798                 :         283 :         indexOid = constrForm->conindid;
   11799                 :         283 :         with_period = constrForm->conperiod;
   11800                 :             : 
   11801                 :             :         /* Create the pg_constraint entry at this level */
   11802                 :         283 :         address = addFkConstraint(addFkReferencingSide,
   11803                 :         283 :                                   NameStr(constrForm->conname), fkconstraint,
   11804                 :             :                                   partRel, pkrel, indexOid, parentConstrOid,
   11805                 :             :                                   numfks, confkey,
   11806                 :             :                                   mapped_conkey, conpfeqop,
   11807                 :             :                                   conppeqop, conffeqop,
   11808                 :             :                                   numfkdelsetcols, confdelsetcols,
   11809                 :             :                                   false, with_period);
   11810                 :             : 
   11811                 :             :         /* Done with the cloned constraint's tuple */
   11812                 :         283 :         ReleaseSysCache(tuple);
   11813                 :             : 
   11814                 :             :         /* Create the check triggers, and recurse to partitions, if any */
   11815                 :         283 :         addFkRecurseReferencing(wqueue,
   11816                 :             :                                 fkconstraint,
   11817                 :             :                                 partRel,
   11818                 :             :                                 pkrel,
   11819                 :             :                                 indexOid,
   11820                 :             :                                 address.objectId,
   11821                 :             :                                 numfks,
   11822                 :             :                                 confkey,
   11823                 :             :                                 mapped_conkey,
   11824                 :             :                                 conpfeqop,
   11825                 :             :                                 conppeqop,
   11826                 :             :                                 conffeqop,
   11827                 :             :                                 numfkdelsetcols,
   11828                 :             :                                 confdelsetcols,
   11829                 :             :                                 false,  /* no old check exists */
   11830                 :             :                                 AccessExclusiveLock,
   11831                 :             :                                 insertTriggerOid,
   11832                 :             :                                 updateTriggerOid,
   11833                 :             :                                 with_period);
   11834                 :         279 :         table_close(pkrel, NoLock);
   11835                 :             :     }
   11836                 :             : 
   11837                 :         347 :     table_close(trigrel, RowExclusiveLock);
   11838                 :             : }
   11839                 :             : 
   11840                 :             : /*
   11841                 :             :  * When the parent of a partition receives [the referencing side of] a foreign
   11842                 :             :  * key, we must propagate that foreign key to the partition.  However, the
   11843                 :             :  * partition might already have an equivalent foreign key; this routine
   11844                 :             :  * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
   11845                 :             :  * by the other parameters.  If they are equivalent, create the link between
   11846                 :             :  * the two constraints and return true.
   11847                 :             :  *
   11848                 :             :  * If the given FK does not match the one defined by rest of the params,
   11849                 :             :  * return false.
   11850                 :             :  */
   11851                 :             : static bool
   11852                 :         183 : tryAttachPartitionForeignKey(List **wqueue,
   11853                 :             :                              ForeignKeyCacheInfo *fk,
   11854                 :             :                              Relation partition,
   11855                 :             :                              Oid parentConstrOid,
   11856                 :             :                              int numfks,
   11857                 :             :                              AttrNumber *mapped_conkey,
   11858                 :             :                              AttrNumber *confkey,
   11859                 :             :                              Oid *conpfeqop,
   11860                 :             :                              Oid parentInsTrigger,
   11861                 :             :                              Oid parentUpdTrigger,
   11862                 :             :                              Relation trigrel)
   11863                 :             : {
   11864                 :             :     HeapTuple   parentConstrTup;
   11865                 :             :     Form_pg_constraint parentConstr;
   11866                 :             :     HeapTuple   partcontup;
   11867                 :             :     Form_pg_constraint partConstr;
   11868                 :             : 
   11869                 :         183 :     parentConstrTup = SearchSysCache1(CONSTROID,
   11870                 :             :                                       ObjectIdGetDatum(parentConstrOid));
   11871         [ -  + ]:         183 :     if (!HeapTupleIsValid(parentConstrTup))
   11872         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
   11873                 :         183 :     parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
   11874                 :             : 
   11875                 :             :     /*
   11876                 :             :      * Do some quick & easy initial checks.  If any of these fail, we cannot
   11877                 :             :      * use this constraint.
   11878                 :             :      */
   11879   [ +  -  -  + ]:         183 :     if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
   11880                 :             :     {
   11881                 :           0 :         ReleaseSysCache(parentConstrTup);
   11882                 :           0 :         return false;
   11883                 :             :     }
   11884         [ +  + ]:         505 :     for (int i = 0; i < numfks; i++)
   11885                 :             :     {
   11886         [ +  + ]:         324 :         if (fk->conkey[i] != mapped_conkey[i] ||
   11887         [ +  - ]:         322 :             fk->confkey[i] != confkey[i] ||
   11888         [ -  + ]:         322 :             fk->conpfeqop[i] != conpfeqop[i])
   11889                 :             :         {
   11890                 :           2 :             ReleaseSysCache(parentConstrTup);
   11891                 :           2 :             return false;
   11892                 :             :         }
   11893                 :             :     }
   11894                 :             : 
   11895                 :             :     /* Looks good so far; perform more extensive checks. */
   11896                 :         181 :     partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
   11897         [ -  + ]:         181 :     if (!HeapTupleIsValid(partcontup))
   11898         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   11899                 :         181 :     partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
   11900                 :             : 
   11901                 :             :     /*
   11902                 :             :      * An error should be raised if the constraint enforceability is
   11903                 :             :      * different. Returning false without raising an error, as we do for other
   11904                 :             :      * attributes, could lead to a duplicate constraint with the same
   11905                 :             :      * enforceability as the parent. While this may be acceptable, it may not
   11906                 :             :      * be ideal. Therefore, it's better to raise an error and allow the user
   11907                 :             :      * to correct the enforceability before proceeding.
   11908                 :             :      */
   11909         [ +  + ]:         181 :     if (partConstr->conenforced != parentConstr->conenforced)
   11910         [ +  - ]:           4 :         ereport(ERROR,
   11911                 :             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   11912                 :             :                  errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
   11913                 :             :                         NameStr(parentConstr->conname),
   11914                 :             :                         NameStr(partConstr->conname),
   11915                 :             :                         RelationGetRelationName(partition))));
   11916                 :             : 
   11917         [ +  + ]:         177 :     if (OidIsValid(partConstr->conparentid) ||
   11918         [ +  + ]:         158 :         partConstr->condeferrable != parentConstr->condeferrable ||
   11919         [ +  - ]:         140 :         partConstr->condeferred != parentConstr->condeferred ||
   11920         [ +  + ]:         140 :         partConstr->confupdtype != parentConstr->confupdtype ||
   11921         [ +  - ]:         117 :         partConstr->confdeltype != parentConstr->confdeltype ||
   11922         [ +  + ]:         117 :         partConstr->confmatchtype != parentConstr->confmatchtype)
   11923                 :             :     {
   11924                 :          69 :         ReleaseSysCache(parentConstrTup);
   11925                 :          69 :         ReleaseSysCache(partcontup);
   11926                 :          69 :         return false;
   11927                 :             :     }
   11928                 :             : 
   11929                 :         108 :     ReleaseSysCache(parentConstrTup);
   11930                 :         108 :     ReleaseSysCache(partcontup);
   11931                 :             : 
   11932                 :             :     /* Looks good!  Attach this constraint. */
   11933                 :         108 :     AttachPartitionForeignKey(wqueue, partition, fk->conoid,
   11934                 :             :                               parentConstrOid, parentInsTrigger,
   11935                 :             :                               parentUpdTrigger, trigrel);
   11936                 :             : 
   11937                 :         108 :     return true;
   11938                 :             : }
   11939                 :             : 
   11940                 :             : /*
   11941                 :             :  * AttachPartitionForeignKey
   11942                 :             :  *
   11943                 :             :  * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
   11944                 :             :  * attaching the constraint, removing redundant triggers and entries from
   11945                 :             :  * pg_constraint, and setting the constraint's parent.
   11946                 :             :  */
   11947                 :             : static void
   11948                 :         108 : AttachPartitionForeignKey(List **wqueue,
   11949                 :             :                           Relation partition,
   11950                 :             :                           Oid partConstrOid,
   11951                 :             :                           Oid parentConstrOid,
   11952                 :             :                           Oid parentInsTrigger,
   11953                 :             :                           Oid parentUpdTrigger,
   11954                 :             :                           Relation trigrel)
   11955                 :             : {
   11956                 :             :     HeapTuple   parentConstrTup;
   11957                 :             :     Form_pg_constraint parentConstr;
   11958                 :             :     HeapTuple   partcontup;
   11959                 :             :     Form_pg_constraint partConstr;
   11960                 :             :     bool        queueValidation;
   11961                 :             :     Oid         partConstrFrelid;
   11962                 :             :     Oid         partConstrRelid;
   11963                 :             :     bool        parentConstrIsEnforced;
   11964                 :             : 
   11965                 :             :     /* Fetch the parent constraint tuple */
   11966                 :         108 :     parentConstrTup = SearchSysCache1(CONSTROID,
   11967                 :             :                                       ObjectIdGetDatum(parentConstrOid));
   11968         [ -  + ]:         108 :     if (!HeapTupleIsValid(parentConstrTup))
   11969         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
   11970                 :         108 :     parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
   11971                 :         108 :     parentConstrIsEnforced = parentConstr->conenforced;
   11972                 :             : 
   11973                 :             :     /* Fetch the child constraint tuple */
   11974                 :         108 :     partcontup = SearchSysCache1(CONSTROID,
   11975                 :             :                                  ObjectIdGetDatum(partConstrOid));
   11976         [ -  + ]:         108 :     if (!HeapTupleIsValid(partcontup))
   11977         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
   11978                 :         108 :     partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
   11979                 :         108 :     partConstrFrelid = partConstr->confrelid;
   11980                 :         108 :     partConstrRelid = partConstr->conrelid;
   11981                 :             : 
   11982                 :             :     /*
   11983                 :             :      * If the referenced table is partitioned, then the partition we're
   11984                 :             :      * attaching now has extra pg_constraint rows and action triggers that are
   11985                 :             :      * no longer needed.  Remove those.
   11986                 :             :      */
   11987         [ +  + ]:         108 :     if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
   11988                 :             :     {
   11989                 :          24 :         Relation    pg_constraint = table_open(ConstraintRelationId, RowShareLock);
   11990                 :             : 
   11991                 :          24 :         RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
   11992                 :             :                                   partConstrRelid);
   11993                 :             : 
   11994                 :          24 :         table_close(pg_constraint, RowShareLock);
   11995                 :             :     }
   11996                 :             : 
   11997                 :             :     /*
   11998                 :             :      * Will we need to validate this constraint?   A valid parent constraint
   11999                 :             :      * implies that all child constraints have been validated, so if this one
   12000                 :             :      * isn't, we must trigger phase 3 validation.
   12001                 :             :      */
   12002   [ +  +  +  + ]:         108 :     queueValidation = parentConstr->convalidated && !partConstr->convalidated;
   12003                 :             : 
   12004                 :         108 :     ReleaseSysCache(partcontup);
   12005                 :         108 :     ReleaseSysCache(parentConstrTup);
   12006                 :             : 
   12007                 :             :     /*
   12008                 :             :      * The action triggers in the new partition become redundant -- the parent
   12009                 :             :      * table already has equivalent ones, and those will be able to reach the
   12010                 :             :      * partition.  Remove the ones in the partition.  We identify them because
   12011                 :             :      * they have our constraint OID, as well as being on the referenced rel.
   12012                 :             :      */
   12013                 :         108 :     DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
   12014                 :             :                                      partConstrRelid);
   12015                 :             : 
   12016                 :         108 :     ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
   12017                 :             :                                   RelationGetRelid(partition));
   12018                 :             : 
   12019                 :             :     /*
   12020                 :             :      * Like the constraint, attach partition's "check" triggers to the
   12021                 :             :      * corresponding parent triggers if the constraint is ENFORCED. NOT
   12022                 :             :      * ENFORCED constraints do not have these triggers.
   12023                 :             :      */
   12024         [ +  + ]:         108 :     if (parentConstrIsEnforced)
   12025                 :             :     {
   12026                 :             :         Oid         insertTriggerOid,
   12027                 :             :                     updateTriggerOid;
   12028                 :             : 
   12029                 :         100 :         GetForeignKeyCheckTriggers(trigrel,
   12030                 :             :                                    partConstrOid, partConstrFrelid, partConstrRelid,
   12031                 :             :                                    &insertTriggerOid, &updateTriggerOid);
   12032                 :             :         Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
   12033                 :         100 :         TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
   12034                 :             :                                 RelationGetRelid(partition));
   12035                 :             :         Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
   12036                 :         100 :         TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
   12037                 :             :                                 RelationGetRelid(partition));
   12038                 :             :     }
   12039                 :             : 
   12040                 :             :     /*
   12041                 :             :      * We updated this pg_constraint row above to set its parent; validating
   12042                 :             :      * it will cause its convalidated flag to change, so we need CCI here.  In
   12043                 :             :      * addition, we need it unconditionally for the rare case where the parent
   12044                 :             :      * table has *two* identical constraints; when reaching this function for
   12045                 :             :      * the second one, we must have made our changes visible, otherwise we
   12046                 :             :      * would try to attach both to this one.
   12047                 :             :      */
   12048                 :         108 :     CommandCounterIncrement();
   12049                 :             : 
   12050                 :             :     /* If validation is needed, put it in the queue now. */
   12051         [ +  + ]:         108 :     if (queueValidation)
   12052                 :             :     {
   12053                 :             :         Relation    conrel;
   12054                 :             :         Oid         confrelid;
   12055                 :             : 
   12056                 :          12 :         conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12057                 :             : 
   12058                 :          12 :         partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
   12059         [ -  + ]:          12 :         if (!HeapTupleIsValid(partcontup))
   12060         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
   12061                 :             : 
   12062                 :          12 :         confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
   12063                 :             : 
   12064                 :             :         /* Use the same lock as for AT_ValidateConstraint */
   12065                 :          12 :         QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
   12066                 :             :                                     partcontup, ShareUpdateExclusiveLock);
   12067                 :          12 :         ReleaseSysCache(partcontup);
   12068                 :          12 :         table_close(conrel, RowExclusiveLock);
   12069                 :             :     }
   12070                 :         108 : }
   12071                 :             : 
   12072                 :             : /*
   12073                 :             :  * RemoveInheritedConstraint
   12074                 :             :  *
   12075                 :             :  * Removes the constraint and its associated trigger from the specified
   12076                 :             :  * relation, which inherited the given constraint.
   12077                 :             :  */
   12078                 :             : static void
   12079                 :          24 : RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid,
   12080                 :             :                           Oid conrelid)
   12081                 :             : {
   12082                 :             :     ObjectAddresses *objs;
   12083                 :             :     HeapTuple   consttup;
   12084                 :             :     ScanKeyData key;
   12085                 :             :     SysScanDesc scan;
   12086                 :             :     HeapTuple   trigtup;
   12087                 :             : 
   12088                 :          24 :     ScanKeyInit(&key,
   12089                 :             :                 Anum_pg_constraint_conrelid,
   12090                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   12091                 :             :                 ObjectIdGetDatum(conrelid));
   12092                 :             : 
   12093                 :          24 :     scan = systable_beginscan(conrel,
   12094                 :             :                               ConstraintRelidTypidNameIndexId,
   12095                 :             :                               true, NULL, 1, &key);
   12096                 :          24 :     objs = new_object_addresses();
   12097         [ +  + ]:         216 :     while ((consttup = systable_getnext(scan)) != NULL)
   12098                 :             :     {
   12099                 :         192 :         Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
   12100                 :             : 
   12101         [ +  + ]:         192 :         if (conform->conparentid != conoid)
   12102                 :         140 :             continue;
   12103                 :             :         else
   12104                 :             :         {
   12105                 :             :             ObjectAddress addr;
   12106                 :             :             SysScanDesc scan2;
   12107                 :             :             ScanKeyData key2;
   12108                 :             :             int         n PG_USED_FOR_ASSERTS_ONLY;
   12109                 :             : 
   12110                 :          52 :             ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
   12111                 :          52 :             add_exact_object_address(&addr, objs);
   12112                 :             : 
   12113                 :             :             /*
   12114                 :             :              * First we must delete the dependency record that binds the
   12115                 :             :              * constraint records together.
   12116                 :             :              */
   12117                 :          52 :             n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
   12118                 :             :                                                    conform->oid,
   12119                 :             :                                                    DEPENDENCY_INTERNAL,
   12120                 :             :                                                    ConstraintRelationId,
   12121                 :             :                                                    conoid);
   12122                 :             :             Assert(n == 1);     /* actually only one is expected */
   12123                 :             : 
   12124                 :             :             /*
   12125                 :             :              * Now search for the triggers for this constraint and set them up
   12126                 :             :              * for deletion too
   12127                 :             :              */
   12128                 :          52 :             ScanKeyInit(&key2,
   12129                 :             :                         Anum_pg_trigger_tgconstraint,
   12130                 :             :                         BTEqualStrategyNumber, F_OIDEQ,
   12131                 :             :                         ObjectIdGetDatum(conform->oid));
   12132                 :          52 :             scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
   12133                 :             :                                        true, NULL, 1, &key2);
   12134         [ +  + ]:         156 :             while ((trigtup = systable_getnext(scan2)) != NULL)
   12135                 :             :             {
   12136                 :         104 :                 ObjectAddressSet(addr, TriggerRelationId,
   12137                 :             :                                  ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
   12138                 :         104 :                 add_exact_object_address(&addr, objs);
   12139                 :             :             }
   12140                 :          52 :             systable_endscan(scan2);
   12141                 :             :         }
   12142                 :             :     }
   12143                 :             :     /* make the dependency deletions visible */
   12144                 :          24 :     CommandCounterIncrement();
   12145                 :          24 :     performMultipleDeletions(objs, DROP_RESTRICT,
   12146                 :             :                              PERFORM_DELETION_INTERNAL);
   12147                 :          24 :     systable_endscan(scan);
   12148                 :          24 : }
   12149                 :             : 
   12150                 :             : /*
   12151                 :             :  * DropForeignKeyConstraintTriggers
   12152                 :             :  *
   12153                 :             :  * The subroutine for tryAttachPartitionForeignKey handles the deletion of
   12154                 :             :  * action triggers for the foreign key constraint.
   12155                 :             :  *
   12156                 :             :  * If valid confrelid and conrelid values are not provided, the respective
   12157                 :             :  * trigger check will be skipped, and the trigger will be considered for
   12158                 :             :  * removal.
   12159                 :             :  */
   12160                 :             : static void
   12161                 :         160 : DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid,
   12162                 :             :                                  Oid conrelid)
   12163                 :             : {
   12164                 :             :     ScanKeyData key;
   12165                 :             :     SysScanDesc scan;
   12166                 :             :     HeapTuple   trigtup;
   12167                 :             : 
   12168                 :         160 :     ScanKeyInit(&key,
   12169                 :             :                 Anum_pg_trigger_tgconstraint,
   12170                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   12171                 :             :                 ObjectIdGetDatum(conoid));
   12172                 :         160 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   12173                 :             :                               NULL, 1, &key);
   12174         [ +  + ]:         696 :     while ((trigtup = systable_getnext(scan)) != NULL)
   12175                 :             :     {
   12176                 :         536 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   12177                 :             :         ObjectAddress trigger;
   12178                 :             : 
   12179                 :             :         /* Invalid if trigger is not for a referential integrity constraint */
   12180         [ -  + ]:         536 :         if (!OidIsValid(trgform->tgconstrrelid))
   12181                 :         200 :             continue;
   12182   [ +  +  +  + ]:         536 :         if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
   12183                 :         200 :             continue;
   12184   [ +  +  -  + ]:         336 :         if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
   12185                 :           0 :             continue;
   12186                 :             : 
   12187                 :             :         /* We should be dropping trigger related to foreign key constraint */
   12188                 :             :         Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
   12189                 :             :                trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
   12190                 :             :                trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
   12191                 :             :                trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
   12192                 :             :                trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
   12193                 :             :                trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
   12194                 :             :                trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
   12195                 :             :                trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
   12196                 :             :                trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
   12197                 :             :                trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
   12198                 :             :                trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
   12199                 :             :                trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
   12200                 :             : 
   12201                 :             :         /*
   12202                 :             :          * The constraint is originally set up to contain this trigger as an
   12203                 :             :          * implementation object, so there's a dependency record that links
   12204                 :             :          * the two; however, since the trigger is no longer needed, we remove
   12205                 :             :          * the dependency link in order to be able to drop the trigger while
   12206                 :             :          * keeping the constraint intact.
   12207                 :             :          */
   12208                 :         336 :         deleteDependencyRecordsFor(TriggerRelationId,
   12209                 :             :                                    trgform->oid,
   12210                 :             :                                    false);
   12211                 :             :         /* make dependency deletion visible to performDeletion */
   12212                 :         336 :         CommandCounterIncrement();
   12213                 :         336 :         ObjectAddressSet(trigger, TriggerRelationId,
   12214                 :             :                          trgform->oid);
   12215                 :         336 :         performDeletion(&trigger, DROP_RESTRICT, 0);
   12216                 :             :         /* make trigger drop visible, in case the loop iterates */
   12217                 :         336 :         CommandCounterIncrement();
   12218                 :             :     }
   12219                 :             : 
   12220                 :         160 :     systable_endscan(scan);
   12221                 :         160 : }
   12222                 :             : 
   12223                 :             : /*
   12224                 :             :  * GetForeignKeyActionTriggers
   12225                 :             :  *      Returns delete and update "action" triggers of the given relation
   12226                 :             :  *      belonging to the given constraint
   12227                 :             :  */
   12228                 :             : static void
   12229                 :         128 : GetForeignKeyActionTriggers(Relation trigrel,
   12230                 :             :                             Oid conoid, Oid confrelid, Oid conrelid,
   12231                 :             :                             Oid *deleteTriggerOid,
   12232                 :             :                             Oid *updateTriggerOid)
   12233                 :             : {
   12234                 :             :     ScanKeyData key;
   12235                 :             :     SysScanDesc scan;
   12236                 :             :     HeapTuple   trigtup;
   12237                 :             : 
   12238                 :         128 :     *deleteTriggerOid = *updateTriggerOid = InvalidOid;
   12239                 :         128 :     ScanKeyInit(&key,
   12240                 :             :                 Anum_pg_trigger_tgconstraint,
   12241                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   12242                 :             :                 ObjectIdGetDatum(conoid));
   12243                 :             : 
   12244                 :         128 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   12245                 :             :                               NULL, 1, &key);
   12246         [ +  - ]:         259 :     while ((trigtup = systable_getnext(scan)) != NULL)
   12247                 :             :     {
   12248                 :         259 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   12249                 :             : 
   12250         [ +  + ]:         259 :         if (trgform->tgconstrrelid != conrelid)
   12251                 :           3 :             continue;
   12252         [ -  + ]:         256 :         if (trgform->tgrelid != confrelid)
   12253                 :           0 :             continue;
   12254                 :             :         /* Only ever look at "action" triggers on the PK side. */
   12255         [ -  + ]:         256 :         if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
   12256                 :           0 :             continue;
   12257         [ +  + ]:         256 :         if (TRIGGER_FOR_DELETE(trgform->tgtype))
   12258                 :             :         {
   12259                 :             :             Assert(*deleteTriggerOid == InvalidOid);
   12260                 :         128 :             *deleteTriggerOid = trgform->oid;
   12261                 :             :         }
   12262         [ +  - ]:         128 :         else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   12263                 :             :         {
   12264                 :             :             Assert(*updateTriggerOid == InvalidOid);
   12265                 :         128 :             *updateTriggerOid = trgform->oid;
   12266                 :             :         }
   12267                 :             : #ifndef USE_ASSERT_CHECKING
   12268                 :             :         /* In an assert-enabled build, continue looking to find duplicates */
   12269   [ +  -  +  + ]:         256 :         if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
   12270                 :         128 :             break;
   12271                 :             : #endif
   12272                 :             :     }
   12273                 :             : 
   12274         [ -  + ]:         128 :     if (!OidIsValid(*deleteTriggerOid))
   12275         [ #  # ]:           0 :         elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
   12276                 :             :              conoid);
   12277         [ -  + ]:         128 :     if (!OidIsValid(*updateTriggerOid))
   12278         [ #  # ]:           0 :         elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
   12279                 :             :              conoid);
   12280                 :             : 
   12281                 :         128 :     systable_endscan(scan);
   12282                 :         128 : }
   12283                 :             : 
   12284                 :             : /*
   12285                 :             :  * GetForeignKeyCheckTriggers
   12286                 :             :  *      Returns insert and update "check" triggers of the given relation
   12287                 :             :  *      belonging to the given constraint
   12288                 :             :  */
   12289                 :             : static void
   12290                 :         551 : GetForeignKeyCheckTriggers(Relation trigrel,
   12291                 :             :                            Oid conoid, Oid confrelid, Oid conrelid,
   12292                 :             :                            Oid *insertTriggerOid,
   12293                 :             :                            Oid *updateTriggerOid)
   12294                 :             : {
   12295                 :             :     ScanKeyData key;
   12296                 :             :     SysScanDesc scan;
   12297                 :             :     HeapTuple   trigtup;
   12298                 :             : 
   12299                 :         551 :     *insertTriggerOid = *updateTriggerOid = InvalidOid;
   12300                 :         551 :     ScanKeyInit(&key,
   12301                 :             :                 Anum_pg_trigger_tgconstraint,
   12302                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   12303                 :             :                 ObjectIdGetDatum(conoid));
   12304                 :             : 
   12305                 :         551 :     scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
   12306                 :             :                               NULL, 1, &key);
   12307         [ +  - ]:        1771 :     while ((trigtup = systable_getnext(scan)) != NULL)
   12308                 :             :     {
   12309                 :        1771 :         Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
   12310                 :             : 
   12311         [ +  + ]:        1771 :         if (trgform->tgconstrrelid != confrelid)
   12312                 :         597 :             continue;
   12313         [ -  + ]:        1174 :         if (trgform->tgrelid != conrelid)
   12314                 :           0 :             continue;
   12315                 :             :         /* Only ever look at "check" triggers on the FK side. */
   12316         [ +  + ]:        1174 :         if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
   12317                 :          72 :             continue;
   12318         [ +  + ]:        1102 :         if (TRIGGER_FOR_INSERT(trgform->tgtype))
   12319                 :             :         {
   12320                 :             :             Assert(*insertTriggerOid == InvalidOid);
   12321                 :         551 :             *insertTriggerOid = trgform->oid;
   12322                 :             :         }
   12323         [ +  - ]:         551 :         else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
   12324                 :             :         {
   12325                 :             :             Assert(*updateTriggerOid == InvalidOid);
   12326                 :         551 :             *updateTriggerOid = trgform->oid;
   12327                 :             :         }
   12328                 :             : #ifndef USE_ASSERT_CHECKING
   12329                 :             :         /* In an assert-enabled build, continue looking to find duplicates. */
   12330   [ +  +  +  + ]:        1102 :         if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
   12331                 :         551 :             break;
   12332                 :             : #endif
   12333                 :             :     }
   12334                 :             : 
   12335         [ -  + ]:         551 :     if (!OidIsValid(*insertTriggerOid))
   12336         [ #  # ]:           0 :         elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
   12337                 :             :              conoid);
   12338         [ -  + ]:         551 :     if (!OidIsValid(*updateTriggerOid))
   12339         [ #  # ]:           0 :         elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
   12340                 :             :              conoid);
   12341                 :             : 
   12342                 :         551 :     systable_endscan(scan);
   12343                 :         551 : }
   12344                 :             : 
   12345                 :             : /*
   12346                 :             :  * ALTER TABLE ALTER CONSTRAINT
   12347                 :             :  *
   12348                 :             :  * Update the attributes of a constraint.
   12349                 :             :  *
   12350                 :             :  * Currently works for Foreign Key, Check, and not null constraints.
   12351                 :             :  *
   12352                 :             :  * If the constraint is modified, returns its address; otherwise, return
   12353                 :             :  * InvalidObjectAddress.
   12354                 :             :  */
   12355                 :             : static ObjectAddress
   12356                 :         336 : ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon,
   12357                 :             :                       bool recurse, LOCKMODE lockmode)
   12358                 :             : {
   12359                 :             :     Relation    conrel;
   12360                 :             :     Relation    tgrel;
   12361                 :             :     SysScanDesc scan;
   12362                 :             :     ScanKeyData skey[3];
   12363                 :             :     HeapTuple   contuple;
   12364                 :             :     Form_pg_constraint currcon;
   12365                 :             :     ObjectAddress address;
   12366                 :             : 
   12367                 :             :     /*
   12368                 :             :      * Disallow altering ONLY a partitioned table, as it would make no sense.
   12369                 :             :      * This is okay for legacy inheritance.
   12370                 :             :      */
   12371   [ +  +  -  + ]:         336 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
   12372         [ #  # ]:           0 :         ereport(ERROR,
   12373                 :             :                 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12374                 :             :                 errmsg("constraint must be altered in child tables too"),
   12375                 :             :                 errhint("Do not specify the ONLY keyword."));
   12376                 :             : 
   12377                 :             : 
   12378                 :         336 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   12379                 :         336 :     tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   12380                 :             : 
   12381                 :             :     /*
   12382                 :             :      * Find and check the target constraint
   12383                 :             :      */
   12384                 :         336 :     ScanKeyInit(&skey[0],
   12385                 :             :                 Anum_pg_constraint_conrelid,
   12386                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   12387                 :             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12388                 :         336 :     ScanKeyInit(&skey[1],
   12389                 :             :                 Anum_pg_constraint_contypid,
   12390                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   12391                 :             :                 ObjectIdGetDatum(InvalidOid));
   12392                 :         336 :     ScanKeyInit(&skey[2],
   12393                 :             :                 Anum_pg_constraint_conname,
   12394                 :             :                 BTEqualStrategyNumber, F_NAMEEQ,
   12395                 :         336 :                 CStringGetDatum(cmdcon->conname));
   12396                 :         336 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   12397                 :             :                               true, NULL, 3, skey);
   12398                 :             : 
   12399                 :             :     /* There can be at most one matching row */
   12400         [ +  + ]:         336 :     if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
   12401         [ +  - ]:           4 :         ereport(ERROR,
   12402                 :             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   12403                 :             :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12404                 :             :                         cmdcon->conname, RelationGetRelationName(rel))));
   12405                 :             : 
   12406                 :         332 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12407   [ +  +  -  + ]:         332 :     if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
   12408         [ #  # ]:           0 :         ereport(ERROR,
   12409                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12410                 :             :                  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
   12411                 :             :                         cmdcon->conname, RelationGetRelationName(rel))));
   12412         [ +  + ]:         332 :     if (cmdcon->alterEnforceability &&
   12413   [ +  +  +  + ]:         196 :         (currcon->contype != CONSTRAINT_FOREIGN && currcon->contype != CONSTRAINT_CHECK))
   12414         [ +  - ]:           8 :         ereport(ERROR,
   12415                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12416                 :             :                  errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
   12417                 :             :                         cmdcon->conname, RelationGetRelationName(rel)),
   12418                 :             :                  errhint("Only foreign key and check constraints can change enforceability.")));
   12419         [ +  + ]:         324 :     if (cmdcon->alterInheritability &&
   12420         [ +  + ]:          64 :         currcon->contype != CONSTRAINT_NOTNULL)
   12421         [ +  - ]:          16 :         ereport(ERROR,
   12422                 :             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   12423                 :             :                 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
   12424                 :             :                        cmdcon->conname, RelationGetRelationName(rel)));
   12425         [ +  + ]:         308 :     if (cmdcon->alterInheritability &&
   12426   [ +  +  +  + ]:          48 :         cmdcon->noinherit && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   12427         [ +  - ]:           4 :         ereport(ERROR,
   12428                 :             :                 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   12429                 :             :                 errmsg("not-null constraint \"%s\" on partitioned table \"%s\" cannot be NO INHERIT",
   12430                 :             :                        cmdcon->conname, RelationGetRelationName(rel)));
   12431                 :             : 
   12432                 :             :     /* Refuse to modify inheritability of inherited constraints */
   12433         [ +  + ]:         304 :     if (cmdcon->alterInheritability &&
   12434   [ +  +  +  + ]:          44 :         cmdcon->noinherit && currcon->coninhcount > 0)
   12435         [ +  - ]:           4 :         ereport(ERROR,
   12436                 :             :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12437                 :             :                 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
   12438                 :             :                        NameStr(currcon->conname),
   12439                 :             :                        RelationGetRelationName(rel)));
   12440                 :             : 
   12441                 :             :     /*
   12442                 :             :      * If it's not the topmost constraint, raise an error.
   12443                 :             :      *
   12444                 :             :      * Altering a non-topmost constraint leaves some triggers untouched, since
   12445                 :             :      * they are not directly connected to this constraint; also, pg_dump would
   12446                 :             :      * ignore the deferrability status of the individual constraint, since it
   12447                 :             :      * only dumps topmost constraints.  Avoid these problems by refusing this
   12448                 :             :      * operation and telling the user to alter the parent constraint instead.
   12449                 :             :      */
   12450         [ +  + ]:         300 :     if (OidIsValid(currcon->conparentid))
   12451                 :             :     {
   12452                 :             :         HeapTuple   tp;
   12453                 :           8 :         Oid         parent = currcon->conparentid;
   12454                 :           8 :         char       *ancestorname = NULL;
   12455                 :           8 :         char       *ancestortable = NULL;
   12456                 :             : 
   12457                 :             :         /* Loop to find the topmost constraint */
   12458         [ +  - ]:          16 :         while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
   12459                 :             :         {
   12460                 :          16 :             Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
   12461                 :             : 
   12462                 :             :             /* If no parent, this is the constraint we want */
   12463         [ +  + ]:          16 :             if (!OidIsValid(contup->conparentid))
   12464                 :             :             {
   12465                 :           8 :                 ancestorname = pstrdup(NameStr(contup->conname));
   12466                 :           8 :                 ancestortable = get_rel_name(contup->conrelid);
   12467                 :           8 :                 ReleaseSysCache(tp);
   12468                 :           8 :                 break;
   12469                 :             :             }
   12470                 :             : 
   12471                 :           8 :             parent = contup->conparentid;
   12472                 :           8 :             ReleaseSysCache(tp);
   12473                 :             :         }
   12474                 :             : 
   12475   [ +  -  +  -  :           8 :         ereport(ERROR,
                   +  - ]
   12476                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   12477                 :             :                  errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
   12478                 :             :                         cmdcon->conname, RelationGetRelationName(rel)),
   12479                 :             :                  ancestorname && ancestortable ?
   12480                 :             :                  errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
   12481                 :             :                            cmdcon->conname, ancestorname, ancestortable) : 0,
   12482                 :             :                  errhint("You may alter the constraint it derives from instead.")));
   12483                 :             :     }
   12484                 :             : 
   12485                 :         292 :     address = InvalidObjectAddress;
   12486                 :             : 
   12487                 :             :     /*
   12488                 :             :      * Do the actual catalog work, and recurse if necessary.
   12489                 :             :      */
   12490         [ +  + ]:         292 :     if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
   12491                 :             :                                       contuple, recurse, lockmode))
   12492                 :         244 :         ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
   12493                 :             : 
   12494                 :         260 :     systable_endscan(scan);
   12495                 :             : 
   12496                 :         260 :     table_close(tgrel, RowExclusiveLock);
   12497                 :         260 :     table_close(conrel, RowExclusiveLock);
   12498                 :             : 
   12499                 :         260 :     return address;
   12500                 :             : }
   12501                 :             : 
   12502                 :             : /*
   12503                 :             :  * A subroutine of ATExecAlterConstraint that calls the respective routines for
   12504                 :             :  * altering constraint's enforceability, deferrability or inheritability.
   12505                 :             :  */
   12506                 :             : static bool
   12507                 :         292 : ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon,
   12508                 :             :                               Relation conrel, Relation tgrel, Relation rel,
   12509                 :             :                               HeapTuple contuple, bool recurse,
   12510                 :             :                               LOCKMODE lockmode)
   12511                 :             : {
   12512                 :             :     Form_pg_constraint currcon;
   12513                 :         292 :     bool        changed = false;
   12514                 :         292 :     List       *otherrelids = NIL;
   12515                 :             : 
   12516                 :         292 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12517                 :             : 
   12518                 :             :     /*
   12519                 :             :      * Do the catalog work for the enforceability or deferrability change,
   12520                 :             :      * recurse if necessary.
   12521                 :             :      *
   12522                 :             :      * Note that even if deferrability is requested to be altered along with
   12523                 :             :      * enforceability, we don't need to explicitly update multiple entries in
   12524                 :             :      * pg_trigger related to deferrability.
   12525                 :             :      *
   12526                 :             :      * Modifying foreign key enforceability involves either creating or
   12527                 :             :      * dropping the trigger, during which the deferrability setting will be
   12528                 :             :      * adjusted automatically.
   12529                 :             :      */
   12530         [ +  + ]:         292 :     if (cmdcon->alterEnforceability)
   12531                 :             :     {
   12532         [ +  + ]:         188 :         if (currcon->contype == CONSTRAINT_FOREIGN)
   12533                 :          60 :             changed = ATExecAlterFKConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
   12534                 :             :                                                         currcon->conrelid,
   12535                 :             :                                                         currcon->confrelid,
   12536                 :             :                                                         contuple, lockmode,
   12537                 :             :                                                         InvalidOid, InvalidOid,
   12538                 :             :                                                         InvalidOid, InvalidOid);
   12539         [ +  - ]:         128 :         else if (currcon->contype == CONSTRAINT_CHECK)
   12540                 :         128 :             changed = ATExecAlterCheckConstrEnforceability(wqueue, cmdcon, conrel,
   12541                 :             :                                                            contuple, recurse, false,
   12542                 :             :                                                            NIL, lockmode);
   12543                 :             :     }
   12544   [ +  +  +  - ]:         168 :     else if (cmdcon->alterDeferrability &&
   12545                 :          64 :              ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
   12546                 :             :                                             contuple, recurse, &otherrelids,
   12547                 :             :                                             lockmode))
   12548                 :             :     {
   12549                 :             :         /*
   12550                 :             :          * AlterConstrUpdateConstraintEntry already invalidated relcache for
   12551                 :             :          * the relations having the constraint itself; here we also invalidate
   12552                 :             :          * for relations that have any triggers that are part of the
   12553                 :             :          * constraint.
   12554                 :             :          */
   12555   [ +  +  +  +  :         204 :         foreach_oid(relid, otherrelids)
                   +  + ]
   12556                 :          76 :             CacheInvalidateRelcacheByRelid(relid);
   12557                 :             : 
   12558                 :          64 :         changed = true;
   12559                 :             :     }
   12560                 :             : 
   12561                 :             :     /*
   12562                 :             :      * Do the catalog work for the inheritability change.
   12563                 :             :      */
   12564   [ +  +  +  - ]:         300 :     if (cmdcon->alterInheritability &&
   12565                 :          40 :         ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
   12566                 :             :                                         lockmode))
   12567                 :          36 :         changed = true;
   12568                 :             : 
   12569                 :         260 :     return changed;
   12570                 :             : }
   12571                 :             : 
   12572                 :             : /*
   12573                 :             :  * Returns true if the foreign key constraint's enforceability is altered.
   12574                 :             :  *
   12575                 :             :  * Depending on whether the constraint is being set to ENFORCED or NOT
   12576                 :             :  * ENFORCED, it creates or drops the trigger accordingly.
   12577                 :             :  *
   12578                 :             :  * Note that we must recurse even when trying to change a constraint to not
   12579                 :             :  * enforced if it is already not enforced, in case descendant constraints
   12580                 :             :  * might be enforced and need to be changed to not enforced. Conversely, we
   12581                 :             :  * should do nothing if a constraint is being set to enforced and is already
   12582                 :             :  * enforced, as descendant constraints cannot be different in that case.
   12583                 :             :  */
   12584                 :             : static bool
   12585                 :         128 : ATExecAlterFKConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
   12586                 :             :                                   Relation conrel, Relation tgrel,
   12587                 :             :                                   Oid fkrelid, Oid pkrelid,
   12588                 :             :                                   HeapTuple contuple, LOCKMODE lockmode,
   12589                 :             :                                   Oid ReferencedParentDelTrigger,
   12590                 :             :                                   Oid ReferencedParentUpdTrigger,
   12591                 :             :                                   Oid ReferencingParentInsTrigger,
   12592                 :             :                                   Oid ReferencingParentUpdTrigger)
   12593                 :             : {
   12594                 :             :     Form_pg_constraint currcon;
   12595                 :             :     Oid         conoid;
   12596                 :             :     Relation    rel;
   12597                 :         128 :     bool        changed = false;
   12598                 :             : 
   12599                 :             :     /* Since this function recurses, it could be driven to stack overflow */
   12600                 :         128 :     check_stack_depth();
   12601                 :             : 
   12602                 :             :     Assert(cmdcon->alterEnforceability);
   12603                 :             : 
   12604                 :         128 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12605                 :         128 :     conoid = currcon->oid;
   12606                 :             : 
   12607                 :             :     /* Should be foreign key constraint */
   12608                 :             :     Assert(currcon->contype == CONSTRAINT_FOREIGN);
   12609                 :             : 
   12610                 :         128 :     rel = table_open(currcon->conrelid, lockmode);
   12611                 :             : 
   12612         [ +  + ]:         128 :     if (currcon->conenforced != cmdcon->is_enforced)
   12613                 :             :     {
   12614                 :         124 :         AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
   12615                 :         124 :         changed = true;
   12616                 :             :     }
   12617                 :             : 
   12618                 :             :     /* Drop triggers */
   12619         [ +  + ]:         128 :     if (!cmdcon->is_enforced)
   12620                 :             :     {
   12621                 :             :         /*
   12622                 :             :          * When setting a constraint to NOT ENFORCED, the constraint triggers
   12623                 :             :          * need to be dropped. Therefore, we must process the child relations
   12624                 :             :          * first, followed by the parent, to account for dependencies.
   12625                 :             :          */
   12626   [ +  +  -  + ]:          92 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   12627                 :          40 :             get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
   12628                 :          12 :             AlterFKConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
   12629                 :             :                                                fkrelid, pkrelid, contuple,
   12630                 :             :                                                lockmode, InvalidOid, InvalidOid,
   12631                 :             :                                                InvalidOid, InvalidOid);
   12632                 :             : 
   12633                 :             :         /* Drop all the triggers */
   12634                 :          52 :         DropForeignKeyConstraintTriggers(tgrel, conoid, InvalidOid, InvalidOid);
   12635                 :             :     }
   12636         [ +  - ]:          76 :     else if (changed)           /* Create triggers */
   12637                 :             :     {
   12638                 :          76 :         Oid         ReferencedDelTriggerOid = InvalidOid,
   12639                 :          76 :                     ReferencedUpdTriggerOid = InvalidOid,
   12640                 :          76 :                     ReferencingInsTriggerOid = InvalidOid,
   12641                 :          76 :                     ReferencingUpdTriggerOid = InvalidOid;
   12642                 :             : 
   12643                 :             :         /* Prepare the minimal information required for trigger creation. */
   12644                 :          76 :         Constraint *fkconstraint = makeNode(Constraint);
   12645                 :             : 
   12646                 :          76 :         fkconstraint->conname = pstrdup(NameStr(currcon->conname));
   12647                 :          76 :         fkconstraint->fk_matchtype = currcon->confmatchtype;
   12648                 :          76 :         fkconstraint->fk_upd_action = currcon->confupdtype;
   12649                 :          76 :         fkconstraint->fk_del_action = currcon->confdeltype;
   12650                 :          76 :         fkconstraint->deferrable = currcon->condeferrable;
   12651                 :          76 :         fkconstraint->initdeferred = currcon->condeferred;
   12652                 :             : 
   12653                 :             :         /* Create referenced triggers */
   12654         [ +  + ]:          76 :         if (currcon->conrelid == fkrelid)
   12655                 :          48 :             createForeignKeyActionTriggers(currcon->conrelid,
   12656                 :             :                                            currcon->confrelid,
   12657                 :             :                                            fkconstraint,
   12658                 :             :                                            conoid,
   12659                 :             :                                            currcon->conindid,
   12660                 :             :                                            ReferencedParentDelTrigger,
   12661                 :             :                                            ReferencedParentUpdTrigger,
   12662                 :             :                                            &ReferencedDelTriggerOid,
   12663                 :             :                                            &ReferencedUpdTriggerOid);
   12664                 :             : 
   12665                 :             :         /* Create referencing triggers */
   12666         [ +  + ]:          76 :         if (currcon->confrelid == pkrelid)
   12667                 :          64 :             createForeignKeyCheckTriggers(currcon->conrelid,
   12668                 :             :                                           pkrelid,
   12669                 :             :                                           fkconstraint,
   12670                 :             :                                           conoid,
   12671                 :             :                                           currcon->conindid,
   12672                 :             :                                           ReferencingParentInsTrigger,
   12673                 :             :                                           ReferencingParentUpdTrigger,
   12674                 :             :                                           &ReferencingInsTriggerOid,
   12675                 :             :                                           &ReferencingUpdTriggerOid);
   12676                 :             : 
   12677                 :             :         /*
   12678                 :             :          * Tell Phase 3 to check that the constraint is satisfied by existing
   12679                 :             :          * rows.  Only applies to leaf partitions, and (for constraints that
   12680                 :             :          * reference a partitioned table) only if this is not one of the
   12681                 :             :          * pg_constraint rows that exist solely to support action triggers.
   12682                 :             :          */
   12683         [ +  + ]:          76 :         if (rel->rd_rel->relkind == RELKIND_RELATION &&
   12684         [ +  + ]:          64 :             currcon->confrelid == pkrelid)
   12685                 :             :         {
   12686                 :             :             AlteredTableInfo *tab;
   12687                 :             :             NewConstraint *newcon;
   12688                 :             : 
   12689                 :          52 :             newcon = palloc0_object(NewConstraint);
   12690                 :          52 :             newcon->name = fkconstraint->conname;
   12691                 :          52 :             newcon->contype = CONSTR_FOREIGN;
   12692                 :          52 :             newcon->refrelid = currcon->confrelid;
   12693                 :          52 :             newcon->refindid = currcon->conindid;
   12694                 :          52 :             newcon->conid = currcon->oid;
   12695                 :          52 :             newcon->qual = (Node *) fkconstraint;
   12696                 :             : 
   12697                 :             :             /* Find or create work queue entry for this table */
   12698                 :          52 :             tab = ATGetQueueEntry(wqueue, rel);
   12699                 :          52 :             tab->constraints = lappend(tab->constraints, newcon);
   12700                 :             :         }
   12701                 :             : 
   12702                 :             :         /*
   12703                 :             :          * If the table at either end of the constraint is partitioned, we
   12704                 :             :          * need to recurse and create triggers for each constraint that is a
   12705                 :             :          * child of this one.
   12706                 :             :          */
   12707   [ +  +  +  + ]:         140 :         if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   12708                 :          64 :             get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
   12709                 :          20 :             AlterFKConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
   12710                 :             :                                                fkrelid, pkrelid, contuple,
   12711                 :             :                                                lockmode,
   12712                 :             :                                                ReferencedDelTriggerOid,
   12713                 :             :                                                ReferencedUpdTriggerOid,
   12714                 :             :                                                ReferencingInsTriggerOid,
   12715                 :             :                                                ReferencingUpdTriggerOid);
   12716                 :             :     }
   12717                 :             : 
   12718                 :         128 :     table_close(rel, NoLock);
   12719                 :             : 
   12720                 :         128 :     return changed;
   12721                 :             : }
   12722                 :             : 
   12723                 :             : /*
   12724                 :             :  * Returns true if the CHECK constraint's enforceability is altered.
   12725                 :             :  */
   12726                 :             : static bool
   12727                 :         336 : ATExecAlterCheckConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
   12728                 :             :                                      Relation conrel, HeapTuple contuple,
   12729                 :             :                                      bool recurse, bool recursing,
   12730                 :             :                                      List *changing_conids,
   12731                 :             :                                      LOCKMODE lockmode)
   12732                 :             : {
   12733                 :             :     Form_pg_constraint currcon;
   12734                 :             :     Relation    rel;
   12735                 :         336 :     bool        changed = false;
   12736                 :         336 :     List       *children = NIL;
   12737                 :         336 :     bool        target_enforced = cmdcon->is_enforced;
   12738                 :         336 :     Oid         enforced_parentoid = InvalidOid;
   12739                 :             : 
   12740                 :             :     /* Since this function recurses, it could be driven to stack overflow */
   12741                 :         336 :     check_stack_depth();
   12742                 :             : 
   12743                 :             :     Assert(cmdcon->alterEnforceability);
   12744                 :             : 
   12745                 :         336 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12746                 :             : 
   12747                 :             :     Assert(currcon->contype == CONSTRAINT_CHECK);
   12748                 :             : 
   12749                 :             :     /*
   12750                 :             :      * Parent relation already locked by caller, children will be locked by
   12751                 :             :      * find_all_inheritors. So NoLock is fine here.
   12752                 :             :      */
   12753                 :         336 :     rel = table_open(currcon->conrelid, NoLock);
   12754                 :             : 
   12755                 :             :     /*
   12756                 :             :      * When setting a constraint to NOT ENFORCED, check whether any matching
   12757                 :             :      * parent constraint remains ENFORCED and is not part of this ALTER.
   12758                 :             :      *
   12759                 :             :      * For a direct ALTER of an inherited constraint, reject the command,
   12760                 :             :      * because the child cannot be weakened while its parent remains enforced.
   12761                 :             :      *
   12762                 :             :      * During recursion, another parent outside this ALTER may still enforce
   12763                 :             :      * the same constraint in a regular inheritance hierarchy. In that case,
   12764                 :             :      * keep the child constraint ENFORCED so that its merged enforceability
   12765                 :             :      * still reflects the remaining enforced parent. Partitions do not need
   12766                 :             :      * this recursive parent check because a partition can have only one
   12767                 :             :      * direct parent.
   12768                 :             :      */
   12769         [ +  + ]:         336 :     if (!cmdcon->is_enforced &&
   12770   [ +  +  +  +  :         304 :         (!recursing || !rel->rd_rel->relispartition) &&
                   +  + ]
   12771                 :         144 :         ATCheckCheckConstrHasEnforcedParent(conrel, rel, contuple,
   12772                 :             :                                             changing_conids,
   12773                 :             :                                             &enforced_parentoid))
   12774                 :             :     {
   12775         [ +  + ]:          32 :         if (!recursing)
   12776         [ +  - ]:          20 :             ereport(ERROR,
   12777                 :             :                     errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   12778                 :             :                     errmsg("cannot mark inherited constraint \"%s\" as %s",
   12779                 :             :                            NameStr(currcon->conname),
   12780                 :             :                            "NOT ENFORCED"),
   12781                 :             :                     errdetail("The matching constraint on parent table \"%s\" is %s.",
   12782                 :             :                               get_rel_name(enforced_parentoid), "ENFORCED"));
   12783                 :             : 
   12784                 :          12 :         target_enforced = true;
   12785                 :             :     }
   12786                 :             : 
   12787                 :             :     /*
   12788                 :             :      * Update to the merged enforceability if needed. This may differ from the
   12789                 :             :      * requested enforceability when another matching parent constraint
   12790                 :             :      * remains enforced.
   12791                 :             :      */
   12792         [ +  + ]:         316 :     if (currcon->conenforced != target_enforced)
   12793                 :             :     {
   12794                 :         248 :         ATAlterConstraint updatecon = *cmdcon;
   12795                 :             : 
   12796                 :         248 :         updatecon.is_enforced = target_enforced;
   12797                 :         248 :         AlterConstrUpdateConstraintEntry(&updatecon, conrel, contuple);
   12798                 :         248 :         changed = true;
   12799                 :             :     }
   12800                 :             : 
   12801                 :             :     /*
   12802                 :             :      * Note that we must recurse even when trying to change a check constraint
   12803                 :             :      * to not enforced if it is already not enforced, in case descendant
   12804                 :             :      * constraints might be enforced and need to be changed to not enforced,
   12805                 :             :      * unless they still inherit an enforced constraint from another parent.
   12806                 :             :      * Conversely, we should do nothing if a constraint is being set to
   12807                 :             :      * enforced and is already enforced, as descendant constraints cannot be
   12808                 :             :      * different in that case.
   12809                 :             :      */
   12810   [ +  +  +  + ]:         316 :     if (!cmdcon->is_enforced || changed)
   12811                 :             :     {
   12812                 :             :         /*
   12813                 :             :          * If we're recursing, the parent has already done this, so skip it.
   12814                 :             :          * Also, if the constraint is a NO INHERIT constraint, we shouldn't
   12815                 :             :          * try to look for it in the children.
   12816                 :             :          */
   12817   [ +  +  +  + ]:         300 :         if (!recursing && !currcon->connoinherit)
   12818                 :             :         {
   12819                 :             :             Assert(changing_conids == NIL);
   12820                 :             : 
   12821                 :         104 :             children = find_all_inheritors(RelationGetRelid(rel),
   12822                 :             :                                            lockmode, NULL);
   12823                 :             : 
   12824                 :             :             /*
   12825                 :             :              * When setting NOT ENFORCED, build the set of equivalent CHECK
   12826                 :             :              * constraints that this command will attempt to change before
   12827                 :             :              * visiting descendants. The root itself has already been checked
   12828                 :             :              * above.
   12829                 :             :              */
   12830         [ +  + ]:         104 :             if (!cmdcon->is_enforced)
   12831                 :          48 :                 changing_conids = list_make1_oid(currcon->oid);
   12832                 :             : 
   12833   [ +  -  +  +  :         512 :             foreach_oid(childoid, children)
                   +  + ]
   12834                 :             :             {
   12835         [ +  + ]:         320 :                 if (childoid == RelationGetRelid(rel))
   12836                 :         104 :                     continue;
   12837                 :             : 
   12838                 :             :                 /*
   12839                 :             :                  * If we are told not to recurse, there had better not be any
   12840                 :             :                  * child tables, because we can't change constraint
   12841                 :             :                  * enforceability on the parent unless we have changed
   12842                 :             :                  * enforceability for all child.
   12843                 :             :                  */
   12844         [ +  + ]:         216 :                 if (!recurse)
   12845         [ +  - ]:           8 :                     ereport(ERROR,
   12846                 :             :                             errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   12847                 :             :                             errmsg("constraint must be altered on child tables too"),
   12848                 :             :                             errhint("Do not specify the ONLY keyword."));
   12849                 :             : 
   12850                 :             :                 /*
   12851                 :             :                  * It is sufficient to look up the constraint by name here.
   12852                 :             :                  * Supported DDL ensures that inheritable CHECK constraints
   12853                 :             :                  * with the same name have equivalent definitions when they
   12854                 :             :                  * are propagated to children or when inheritance is
   12855                 :             :                  * established.  All descendants returned by
   12856                 :             :                  * find_all_inheritors must have this constraint: inherited
   12857                 :             :                  * CHECK constraints propagate to all children at
   12858                 :             :                  * inheritance-link creation time and cannot be dropped
   12859                 :             :                  * independently on child tables.
   12860                 :             :                  */
   12861         [ +  + ]:         208 :                 if (!cmdcon->is_enforced)
   12862                 :             :                     changing_conids =
   12863                 :          92 :                         list_append_unique_oid(changing_conids,
   12864                 :             :                                                get_relation_constraint_oid(childoid,
   12865                 :          92 :                                                                            cmdcon->conname,
   12866                 :             :                                                                            false));
   12867                 :             :             }
   12868                 :             :         }
   12869                 :             : 
   12870   [ +  +  +  +  :         888 :         foreach_oid(childoid, children)
                   +  + ]
   12871                 :             :         {
   12872         [ +  + ]:         304 :             if (childoid == RelationGetRelid(rel))
   12873                 :          96 :                 continue;
   12874                 :             : 
   12875                 :         208 :             AlterCheckConstrEnforceabilityRecurse(wqueue, cmdcon, conrel,
   12876                 :             :                                                   childoid, false, true,
   12877                 :             :                                                   changing_conids,
   12878                 :             :                                                   lockmode);
   12879                 :             :         }
   12880                 :             :     }
   12881                 :             : 
   12882                 :             :     /*
   12883                 :             :      * Tell Phase 3 to check that the constraint is satisfied by existing
   12884                 :             :      * rows. We only need do this when altering the constraint from NOT
   12885                 :             :      * ENFORCED to ENFORCED.
   12886                 :             :      */
   12887         [ +  + ]:         308 :     if (rel->rd_rel->relkind == RELKIND_RELATION &&
   12888   [ +  +  +  + ]:         260 :         !currcon->conenforced &&
   12889                 :             :         target_enforced)
   12890                 :             :     {
   12891                 :             :         AlteredTableInfo *tab;
   12892                 :             :         NewConstraint *newcon;
   12893                 :             :         Datum       val;
   12894                 :             :         char       *conbin;
   12895                 :             : 
   12896                 :         116 :         newcon = palloc0_object(NewConstraint);
   12897                 :         116 :         newcon->name = pstrdup(NameStr(currcon->conname));
   12898                 :         116 :         newcon->contype = CONSTR_CHECK;
   12899                 :             : 
   12900                 :         116 :         val = SysCacheGetAttrNotNull(CONSTROID, contuple,
   12901                 :             :                                      Anum_pg_constraint_conbin);
   12902                 :         116 :         conbin = TextDatumGetCString(val);
   12903                 :         116 :         newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
   12904                 :             : 
   12905                 :             :         /* Find or create work queue entry for this table */
   12906                 :         116 :         tab = ATGetQueueEntry(wqueue, rel);
   12907                 :         116 :         tab->constraints = lappend(tab->constraints, newcon);
   12908                 :             :     }
   12909                 :             : 
   12910                 :         308 :     table_close(rel, NoLock);
   12911                 :             : 
   12912                 :         308 :     return changed;
   12913                 :             : }
   12914                 :             : 
   12915                 :             : /*
   12916                 :             :  * Invokes ATExecAlterCheckConstrEnforceability for each CHECK constraint that
   12917                 :             :  * is a child of the specified constraint.
   12918                 :             :  *
   12919                 :             :  * We rely on the parent and child tables having identical CHECK constraint
   12920                 :             :  * names to retrieve the child's pg_constraint tuple.
   12921                 :             :  *
   12922                 :             :  * The arguments to this function have the same meaning as the arguments to
   12923                 :             :  * ATExecAlterCheckConstrEnforceability.
   12924                 :             :  */
   12925                 :             : static void
   12926                 :         208 : AlterCheckConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
   12927                 :             :                                       Relation conrel, Oid conrelid,
   12928                 :             :                                       bool recurse, bool recursing,
   12929                 :             :                                       List *changing_conids,
   12930                 :             :                                       LOCKMODE lockmode)
   12931                 :             : {
   12932                 :             :     SysScanDesc pscan;
   12933                 :             :     HeapTuple   childtup;
   12934                 :             :     ScanKeyData skey[3];
   12935                 :             : 
   12936                 :         208 :     ScanKeyInit(&skey[0],
   12937                 :             :                 Anum_pg_constraint_conrelid,
   12938                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   12939                 :             :                 ObjectIdGetDatum(conrelid));
   12940                 :         208 :     ScanKeyInit(&skey[1],
   12941                 :             :                 Anum_pg_constraint_contypid,
   12942                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   12943                 :             :                 ObjectIdGetDatum(InvalidOid));
   12944                 :         208 :     ScanKeyInit(&skey[2],
   12945                 :             :                 Anum_pg_constraint_conname,
   12946                 :             :                 BTEqualStrategyNumber, F_NAMEEQ,
   12947                 :         208 :                 CStringGetDatum(cmdcon->conname));
   12948                 :             : 
   12949                 :         208 :     pscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true,
   12950                 :             :                                NULL, 3, skey);
   12951                 :             : 
   12952         [ -  + ]:         208 :     if (!HeapTupleIsValid(childtup = systable_getnext(pscan)))
   12953         [ #  # ]:           0 :         ereport(ERROR,
   12954                 :             :                 errcode(ERRCODE_UNDEFINED_OBJECT),
   12955                 :             :                 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   12956                 :             :                        cmdcon->conname, get_rel_name(conrelid)));
   12957                 :             : 
   12958                 :         208 :     ATExecAlterCheckConstrEnforceability(wqueue, cmdcon, conrel, childtup,
   12959                 :             :                                          recurse, recursing, changing_conids,
   12960                 :             :                                          lockmode);
   12961                 :             : 
   12962                 :         208 :     systable_endscan(pscan);
   12963                 :         208 : }
   12964                 :             : 
   12965                 :             : /*
   12966                 :             :  * When setting an inherited CHECK constraint to NOT ENFORCED, look for a
   12967                 :             :  * matching parent constraint that remains ENFORCED and is not part of the same
   12968                 :             :  * ALTER.
   12969                 :             :  */
   12970                 :             : static bool
   12971                 :         268 : ATCheckCheckConstrHasEnforcedParent(Relation conrel, Relation rel,
   12972                 :             :                                     HeapTuple contuple,
   12973                 :             :                                     List *changing_conids,
   12974                 :             :                                     Oid *enforced_parentoid)
   12975                 :             : {
   12976                 :             :     Form_pg_constraint currcon;
   12977                 :             :     Relation    inhrel;
   12978                 :             :     SysScanDesc scan;
   12979                 :             :     ScanKeyData skey;
   12980                 :             :     HeapTuple   inheritsTuple;
   12981                 :             : 
   12982                 :             :     /* Since this function recurses, it could be driven to stack overflow */
   12983                 :         268 :     check_stack_depth();
   12984                 :             : 
   12985                 :         268 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   12986                 :             :     Assert(currcon->contype == CONSTRAINT_CHECK);
   12987                 :             : 
   12988         [ +  + ]:         268 :     if (currcon->coninhcount <= 0)
   12989                 :         124 :         return false;
   12990                 :             : 
   12991                 :         144 :     inhrel = table_open(InheritsRelationId, AccessShareLock);
   12992                 :             : 
   12993                 :         144 :     ScanKeyInit(&skey,
   12994                 :             :                 Anum_pg_inherits_inhrelid,
   12995                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   12996                 :             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   12997                 :         144 :     scan = systable_beginscan(inhrel, InheritsRelidSeqnoIndexId,
   12998                 :             :                               true, NULL, 1, &skey);
   12999                 :             : 
   13000         [ +  + ]:         304 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   13001                 :             :     {
   13002                 :             :         Oid         parentoid;
   13003                 :         200 :         Relation    parentrel = NULL;
   13004                 :             :         SysScanDesc pscan;
   13005                 :             :         ScanKeyData pkey[3];
   13006                 :             :         HeapTuple   parenttup;
   13007                 :             : 
   13008                 :         200 :         parentoid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent;
   13009                 :             : 
   13010                 :         200 :         ScanKeyInit(&pkey[0],
   13011                 :             :                     Anum_pg_constraint_conrelid,
   13012                 :             :                     BTEqualStrategyNumber, F_OIDEQ,
   13013                 :             :                     ObjectIdGetDatum(parentoid));
   13014                 :         200 :         ScanKeyInit(&pkey[1],
   13015                 :             :                     Anum_pg_constraint_contypid,
   13016                 :             :                     BTEqualStrategyNumber, F_OIDEQ,
   13017                 :             :                     ObjectIdGetDatum(InvalidOid));
   13018                 :         200 :         ScanKeyInit(&pkey[2],
   13019                 :             :                     Anum_pg_constraint_conname,
   13020                 :             :                     BTEqualStrategyNumber, F_NAMEEQ,
   13021                 :         200 :                     NameGetDatum(&currcon->conname));
   13022                 :             : 
   13023                 :         200 :         pscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   13024                 :             :                                    true, NULL, 3, pkey);
   13025                 :             : 
   13026                 :             :         /*
   13027                 :             :          * ConstraintRelidTypidNameIndexId is unique on (conrelid, contypid,
   13028                 :             :          * conname), so this loop body executes at most once per parent.
   13029                 :             :          */
   13030         [ +  + ]:         360 :         while (HeapTupleIsValid(parenttup = systable_getnext(pscan)))
   13031                 :             :         {
   13032                 :             :             Form_pg_constraint parentcon;
   13033                 :             : 
   13034                 :         200 :             parentcon = (Form_pg_constraint) GETSTRUCT(parenttup);
   13035                 :             : 
   13036         [ +  - ]:         200 :             if (parentcon->contype != CONSTRAINT_CHECK ||
   13037         [ +  - ]:         200 :                 parentcon->connoinherit ||
   13038         [ +  + ]:         200 :                 !parentcon->conenforced)
   13039                 :          44 :                 continue;
   13040                 :             : 
   13041         [ -  + ]:         156 :             if (!constraints_equivalent(parenttup, contuple,
   13042                 :             :                                         RelationGetDescr(conrel)))
   13043         [ #  # ]:           0 :                 elog(ERROR, "child table \"%s\" has different definition for check constraint \"%s\"",
   13044                 :             :                      RelationGetRelationName(rel),
   13045                 :             :                      NameStr(parentcon->conname));
   13046                 :             : 
   13047                 :             :             /*
   13048                 :             :              * A parent listed in changing_conids is being changed by the same
   13049                 :             :              * ALTER, but it may not have been updated yet.  For regular
   13050                 :             :              * inheritance, recurse upward to check whether an equivalent
   13051                 :             :              * enforced parent outside the ALTER will make it remain enforced.
   13052                 :             :              * Partitions cannot have multiple parents, so they do not need
   13053                 :             :              * this check.
   13054                 :             :              */
   13055   [ +  +  +  + ]:         300 :             if (!rel->rd_rel->relispartition &&
   13056                 :         144 :                 list_member_oid(changing_conids, parentcon->oid))
   13057                 :             :             {
   13058                 :         124 :                 Oid         parent_enforced_parentoid = InvalidOid;
   13059                 :             : 
   13060         [ +  - ]:         124 :                 if (parentrel == NULL)
   13061                 :         124 :                     parentrel = table_open(parentoid, NoLock);
   13062                 :             : 
   13063         [ +  + ]:         124 :                 if (!ATCheckCheckConstrHasEnforcedParent(conrel,
   13064                 :             :                                                          parentrel,
   13065                 :             :                                                          parenttup,
   13066                 :             :                                                          changing_conids,
   13067                 :             :                                                          &parent_enforced_parentoid))
   13068                 :         116 :                     continue;
   13069                 :             :             }
   13070                 :             : 
   13071                 :          40 :             *enforced_parentoid = parentoid;
   13072         [ +  + ]:          40 :             if (parentrel != NULL)
   13073                 :           8 :                 table_close(parentrel, NoLock);
   13074                 :          40 :             systable_endscan(pscan);
   13075                 :          40 :             systable_endscan(scan);
   13076                 :          40 :             table_close(inhrel, AccessShareLock);
   13077                 :          40 :             return true;
   13078                 :             :         }
   13079                 :             : 
   13080         [ +  + ]:         160 :         if (parentrel != NULL)
   13081                 :         116 :             table_close(parentrel, NoLock);
   13082                 :         160 :         systable_endscan(pscan);
   13083                 :             :     }
   13084                 :             : 
   13085                 :         104 :     systable_endscan(scan);
   13086                 :         104 :     table_close(inhrel, AccessShareLock);
   13087                 :             : 
   13088                 :         104 :     return false;
   13089                 :             : }
   13090                 :             : 
   13091                 :             : /*
   13092                 :             :  * Returns true if the constraint's deferrability is altered.
   13093                 :             :  *
   13094                 :             :  * *otherrelids is appended OIDs of relations containing affected triggers.
   13095                 :             :  *
   13096                 :             :  * Note that we must recurse even when the values are correct, in case
   13097                 :             :  * indirect descendants have had their constraints altered locally.
   13098                 :             :  * (This could be avoided if we forbade altering constraints in partitions
   13099                 :             :  * but existing releases don't do that.)
   13100                 :             :  */
   13101                 :             : static bool
   13102                 :         108 : ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
   13103                 :             :                                Relation conrel, Relation tgrel, Relation rel,
   13104                 :             :                                HeapTuple contuple, bool recurse,
   13105                 :             :                                List **otherrelids, LOCKMODE lockmode)
   13106                 :             : {
   13107                 :             :     Form_pg_constraint currcon;
   13108                 :             :     Oid         refrelid;
   13109                 :         108 :     bool        changed = false;
   13110                 :             : 
   13111                 :             :     /* since this function recurses, it could be driven to stack overflow */
   13112                 :         108 :     check_stack_depth();
   13113                 :             : 
   13114                 :             :     Assert(cmdcon->alterDeferrability);
   13115                 :             : 
   13116                 :         108 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   13117                 :         108 :     refrelid = currcon->confrelid;
   13118                 :             : 
   13119                 :             :     /* Should be foreign key constraint */
   13120                 :             :     Assert(currcon->contype == CONSTRAINT_FOREIGN);
   13121                 :             : 
   13122                 :             :     /*
   13123                 :             :      * If called to modify a constraint that's already in the desired state,
   13124                 :             :      * silently do nothing.
   13125                 :             :      */
   13126         [ +  + ]:         108 :     if (currcon->condeferrable != cmdcon->deferrable ||
   13127         [ +  - ]:           4 :         currcon->condeferred != cmdcon->initdeferred)
   13128                 :             :     {
   13129                 :         108 :         AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
   13130                 :         108 :         changed = true;
   13131                 :             : 
   13132                 :             :         /*
   13133                 :             :          * Now we need to update the multiple entries in pg_trigger that
   13134                 :             :          * implement the constraint.
   13135                 :             :          */
   13136                 :         108 :         AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
   13137                 :         108 :                                         cmdcon->deferrable,
   13138                 :         108 :                                         cmdcon->initdeferred, otherrelids);
   13139                 :             :     }
   13140                 :             : 
   13141                 :             :     /*
   13142                 :             :      * If the table at either end of the constraint is partitioned, we need to
   13143                 :             :      * handle every constraint that is a child of this one.
   13144                 :             :      */
   13145   [ +  -  +  - ]:         108 :     if (recurse && changed &&
   13146   [ +  +  +  + ]:         200 :         (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   13147                 :          92 :          get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
   13148                 :          28 :         AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
   13149                 :             :                                         contuple, recurse, otherrelids,
   13150                 :             :                                         lockmode);
   13151                 :             : 
   13152                 :         108 :     return changed;
   13153                 :             : }
   13154                 :             : 
   13155                 :             : /*
   13156                 :             :  * Returns true if the constraint's inheritability is altered.
   13157                 :             :  */
   13158                 :             : static bool
   13159                 :          40 : ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
   13160                 :             :                                 Relation conrel, Relation rel,
   13161                 :             :                                 HeapTuple contuple, LOCKMODE lockmode)
   13162                 :             : {
   13163                 :             :     Form_pg_constraint currcon;
   13164                 :             :     AttrNumber  colNum;
   13165                 :             :     char       *colName;
   13166                 :             :     List       *children;
   13167                 :             : 
   13168                 :             :     Assert(cmdcon->alterInheritability);
   13169                 :             : 
   13170                 :          40 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   13171                 :             : 
   13172                 :             :     /* The current implementation only works for NOT NULL constraints */
   13173                 :             :     Assert(currcon->contype == CONSTRAINT_NOTNULL);
   13174                 :             : 
   13175                 :             :     /*
   13176                 :             :      * If called to modify a constraint that's already in the desired state,
   13177                 :             :      * silently do nothing.
   13178                 :             :      */
   13179         [ -  + ]:          40 :     if (cmdcon->noinherit == currcon->connoinherit)
   13180                 :           0 :         return false;
   13181                 :             : 
   13182                 :          40 :     AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
   13183                 :          40 :     CommandCounterIncrement();
   13184                 :             : 
   13185                 :             :     /* Fetch the column number and name */
   13186                 :          40 :     colNum = extractNotNullColumn(contuple);
   13187                 :          40 :     colName = get_attname(currcon->conrelid, colNum, false);
   13188                 :             : 
   13189                 :             :     /*
   13190                 :             :      * Propagate the change to children.  For this subcommand type we don't
   13191                 :             :      * recursively affect children, just the immediate level.
   13192                 :             :      */
   13193                 :          40 :     children = find_inheritance_children(RelationGetRelid(rel),
   13194                 :             :                                          lockmode);
   13195   [ +  +  +  +  :         128 :     foreach_oid(childoid, children)
                   +  + ]
   13196                 :             :     {
   13197                 :             :         ObjectAddress addr;
   13198                 :             : 
   13199         [ +  + ]:          56 :         if (cmdcon->noinherit)
   13200                 :             :         {
   13201                 :             :             HeapTuple   childtup;
   13202                 :             :             Form_pg_constraint childcon;
   13203                 :             : 
   13204                 :          20 :             childtup = findNotNullConstraint(childoid, colName);
   13205         [ -  + ]:          20 :             if (!childtup)
   13206         [ #  # ]:           0 :                 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
   13207                 :             :                      colName, childoid);
   13208                 :          20 :             childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   13209                 :             :             Assert(childcon->coninhcount > 0);
   13210                 :          20 :             childcon->coninhcount--;
   13211                 :          20 :             childcon->conislocal = true;
   13212                 :          20 :             CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
   13213                 :          20 :             heap_freetuple(childtup);
   13214                 :             :         }
   13215                 :             :         else
   13216                 :             :         {
   13217                 :          36 :             Relation    childrel = table_open(childoid, NoLock);
   13218                 :             : 
   13219                 :          36 :             addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
   13220                 :             :                                     colName, true, true, lockmode);
   13221         [ +  - ]:          32 :             if (OidIsValid(addr.objectId))
   13222                 :          32 :                 CommandCounterIncrement();
   13223                 :          32 :             table_close(childrel, NoLock);
   13224                 :             :         }
   13225                 :             :     }
   13226                 :             : 
   13227                 :          36 :     return true;
   13228                 :             : }
   13229                 :             : 
   13230                 :             : /*
   13231                 :             :  * A subroutine of ATExecAlterConstrDeferrability that updated constraint
   13232                 :             :  * trigger's deferrability.
   13233                 :             :  *
   13234                 :             :  * The arguments to this function have the same meaning as the arguments to
   13235                 :             :  * ATExecAlterConstrDeferrability.
   13236                 :             :  */
   13237                 :             : static void
   13238                 :         108 : AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
   13239                 :             :                                 bool deferrable, bool initdeferred,
   13240                 :             :                                 List **otherrelids)
   13241                 :             : {
   13242                 :             :     HeapTuple   tgtuple;
   13243                 :             :     ScanKeyData tgkey;
   13244                 :             :     SysScanDesc tgscan;
   13245                 :             : 
   13246                 :         108 :     ScanKeyInit(&tgkey,
   13247                 :             :                 Anum_pg_trigger_tgconstraint,
   13248                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   13249                 :             :                 ObjectIdGetDatum(conoid));
   13250                 :         108 :     tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
   13251                 :             :                                 NULL, 1, &tgkey);
   13252         [ +  + ]:         420 :     while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
   13253                 :             :     {
   13254                 :         312 :         Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
   13255                 :             :         Form_pg_trigger copy_tg;
   13256                 :             :         HeapTuple   tgCopyTuple;
   13257                 :             : 
   13258                 :             :         /*
   13259                 :             :          * Remember OIDs of other relation(s) involved in FK constraint.
   13260                 :             :          * (Note: it's likely that we could skip forcing a relcache inval for
   13261                 :             :          * other rels that don't have a trigger whose properties change, but
   13262                 :             :          * let's be conservative.)
   13263                 :             :          */
   13264         [ +  + ]:         312 :         if (tgform->tgrelid != RelationGetRelid(rel))
   13265                 :         152 :             *otherrelids = list_append_unique_oid(*otherrelids,
   13266                 :             :                                                   tgform->tgrelid);
   13267                 :             : 
   13268                 :             :         /*
   13269                 :             :          * Update enable status and deferrability of RI_FKey_noaction_del,
   13270                 :             :          * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
   13271                 :             :          * triggers, but not others; see createForeignKeyActionTriggers and
   13272                 :             :          * CreateFKCheckTrigger.
   13273                 :             :          */
   13274         [ +  + ]:         312 :         if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
   13275         [ +  + ]:         248 :             tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
   13276         [ +  + ]:         172 :             tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
   13277         [ +  + ]:          92 :             tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
   13278                 :          12 :             continue;
   13279                 :             : 
   13280                 :         300 :         tgCopyTuple = heap_copytuple(tgtuple);
   13281                 :         300 :         copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
   13282                 :             : 
   13283                 :         300 :         copy_tg->tgdeferrable = deferrable;
   13284                 :         300 :         copy_tg->tginitdeferred = initdeferred;
   13285                 :         300 :         CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
   13286                 :             : 
   13287         [ -  + ]:         300 :         InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
   13288                 :             : 
   13289                 :         300 :         heap_freetuple(tgCopyTuple);
   13290                 :             :     }
   13291                 :             : 
   13292                 :         108 :     systable_endscan(tgscan);
   13293                 :         108 : }
   13294                 :             : 
   13295                 :             : /*
   13296                 :             :  * Invokes ATExecAlterFKConstrEnforceability for each foreign key constraint
   13297                 :             :  * that is a child of the specified constraint.
   13298                 :             :  *
   13299                 :             :  * Note that this doesn't handle recursion the normal way, viz. by scanning the
   13300                 :             :  * list of child relations and recursing; instead it uses the conparentid
   13301                 :             :  * relationships.  This may need to be reconsidered.
   13302                 :             :  *
   13303                 :             :  * The arguments to this function have the same meaning as the arguments to
   13304                 :             :  * ATExecAlterFKConstrEnforceability.
   13305                 :             :  */
   13306                 :             : static void
   13307                 :          32 : AlterFKConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
   13308                 :             :                                    Relation conrel, Relation tgrel,
   13309                 :             :                                    Oid fkrelid, Oid pkrelid,
   13310                 :             :                                    HeapTuple contuple, LOCKMODE lockmode,
   13311                 :             :                                    Oid ReferencedParentDelTrigger,
   13312                 :             :                                    Oid ReferencedParentUpdTrigger,
   13313                 :             :                                    Oid ReferencingParentInsTrigger,
   13314                 :             :                                    Oid ReferencingParentUpdTrigger)
   13315                 :             : {
   13316                 :             :     Form_pg_constraint currcon;
   13317                 :             :     Oid         conoid;
   13318                 :             :     ScanKeyData pkey;
   13319                 :             :     SysScanDesc pscan;
   13320                 :             :     HeapTuple   childtup;
   13321                 :             : 
   13322                 :          32 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   13323                 :          32 :     conoid = currcon->oid;
   13324                 :             : 
   13325                 :          32 :     ScanKeyInit(&pkey,
   13326                 :             :                 Anum_pg_constraint_conparentid,
   13327                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   13328                 :             :                 ObjectIdGetDatum(conoid));
   13329                 :             : 
   13330                 :          32 :     pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   13331                 :             :                                true, NULL, 1, &pkey);
   13332                 :             : 
   13333         [ +  + ]:         100 :     while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   13334                 :          68 :         ATExecAlterFKConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
   13335                 :             :                                           pkrelid, childtup, lockmode,
   13336                 :             :                                           ReferencedParentDelTrigger,
   13337                 :             :                                           ReferencedParentUpdTrigger,
   13338                 :             :                                           ReferencingParentInsTrigger,
   13339                 :             :                                           ReferencingParentUpdTrigger);
   13340                 :             : 
   13341                 :          32 :     systable_endscan(pscan);
   13342                 :          32 : }
   13343                 :             : 
   13344                 :             : /*
   13345                 :             :  * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
   13346                 :             :  * the specified constraint.
   13347                 :             :  *
   13348                 :             :  * Note that this doesn't handle recursion the normal way, viz. by scanning the
   13349                 :             :  * list of child relations and recursing; instead it uses the conparentid
   13350                 :             :  * relationships.  This may need to be reconsidered.
   13351                 :             :  *
   13352                 :             :  * The arguments to this function have the same meaning as the arguments to
   13353                 :             :  * ATExecAlterConstrDeferrability.
   13354                 :             :  */
   13355                 :             : static void
   13356                 :          28 : AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
   13357                 :             :                                 Relation conrel, Relation tgrel, Relation rel,
   13358                 :             :                                 HeapTuple contuple, bool recurse,
   13359                 :             :                                 List **otherrelids, LOCKMODE lockmode)
   13360                 :             : {
   13361                 :             :     Form_pg_constraint currcon;
   13362                 :             :     Oid         conoid;
   13363                 :             :     ScanKeyData pkey;
   13364                 :             :     SysScanDesc pscan;
   13365                 :             :     HeapTuple   childtup;
   13366                 :             : 
   13367                 :          28 :     currcon = (Form_pg_constraint) GETSTRUCT(contuple);
   13368                 :          28 :     conoid = currcon->oid;
   13369                 :             : 
   13370                 :          28 :     ScanKeyInit(&pkey,
   13371                 :             :                 Anum_pg_constraint_conparentid,
   13372                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   13373                 :             :                 ObjectIdGetDatum(conoid));
   13374                 :             : 
   13375                 :          28 :     pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   13376                 :             :                                true, NULL, 1, &pkey);
   13377                 :             : 
   13378         [ +  + ]:          72 :     while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   13379                 :             :     {
   13380                 :          44 :         Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   13381                 :             :         Relation    childrel;
   13382                 :             : 
   13383                 :          44 :         childrel = table_open(childcon->conrelid, lockmode);
   13384                 :             : 
   13385                 :          44 :         ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
   13386                 :             :                                        childtup, recurse, otherrelids, lockmode);
   13387                 :          44 :         table_close(childrel, NoLock);
   13388                 :             :     }
   13389                 :             : 
   13390                 :          28 :     systable_endscan(pscan);
   13391                 :          28 : }
   13392                 :             : 
   13393                 :             : /*
   13394                 :             :  * Update the constraint entry for the given ATAlterConstraint command, and
   13395                 :             :  * invoke the appropriate hooks.
   13396                 :             :  */
   13397                 :             : static void
   13398                 :         520 : AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel,
   13399                 :             :                                  HeapTuple contuple)
   13400                 :             : {
   13401                 :             :     HeapTuple   copyTuple;
   13402                 :             :     Form_pg_constraint copy_con;
   13403                 :             : 
   13404                 :             :     Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
   13405                 :             :            cmdcon->alterInheritability);
   13406                 :             : 
   13407                 :         520 :     copyTuple = heap_copytuple(contuple);
   13408                 :         520 :     copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   13409                 :             : 
   13410         [ +  + ]:         520 :     if (cmdcon->alterEnforceability)
   13411                 :             :     {
   13412                 :         372 :         copy_con->conenforced = cmdcon->is_enforced;
   13413                 :             : 
   13414                 :             :         /*
   13415                 :             :          * NB: The convalidated status is irrelevant when the constraint is
   13416                 :             :          * set to NOT ENFORCED, but for consistency, it should still be set
   13417                 :             :          * appropriately. Similarly, if the constraint is later changed to
   13418                 :             :          * ENFORCED, validation will be performed during phase 3, so it makes
   13419                 :             :          * sense to mark it as valid in that case.
   13420                 :             :          */
   13421                 :         372 :         copy_con->convalidated = cmdcon->is_enforced;
   13422                 :             :     }
   13423         [ +  + ]:         520 :     if (cmdcon->alterDeferrability)
   13424                 :             :     {
   13425                 :         112 :         copy_con->condeferrable = cmdcon->deferrable;
   13426                 :         112 :         copy_con->condeferred = cmdcon->initdeferred;
   13427                 :             :     }
   13428         [ +  + ]:         520 :     if (cmdcon->alterInheritability)
   13429                 :          40 :         copy_con->connoinherit = cmdcon->noinherit;
   13430                 :             : 
   13431                 :         520 :     CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   13432         [ -  + ]:         520 :     InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
   13433                 :             : 
   13434                 :             :     /* Make new constraint flags visible to others */
   13435                 :         520 :     CacheInvalidateRelcacheByRelid(copy_con->conrelid);
   13436                 :             : 
   13437                 :         520 :     heap_freetuple(copyTuple);
   13438                 :         520 : }
   13439                 :             : 
   13440                 :             : /*
   13441                 :             :  * ALTER TABLE VALIDATE CONSTRAINT
   13442                 :             :  *
   13443                 :             :  * XXX The reason we handle recursion here rather than at Phase 1 is because
   13444                 :             :  * there's no good way to skip recursing when handling foreign keys: there is
   13445                 :             :  * no need to lock children in that case, yet we wouldn't be able to avoid
   13446                 :             :  * doing so at that level.
   13447                 :             :  *
   13448                 :             :  * Return value is the address of the validated constraint.  If the constraint
   13449                 :             :  * was already validated, InvalidObjectAddress is returned.
   13450                 :             :  */
   13451                 :             : static ObjectAddress
   13452                 :         351 : ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
   13453                 :             :                          bool recurse, bool recursing, LOCKMODE lockmode)
   13454                 :             : {
   13455                 :             :     Relation    conrel;
   13456                 :             :     SysScanDesc scan;
   13457                 :             :     ScanKeyData skey[3];
   13458                 :             :     HeapTuple   tuple;
   13459                 :             :     Form_pg_constraint con;
   13460                 :             :     ObjectAddress address;
   13461                 :             : 
   13462                 :         351 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   13463                 :             : 
   13464                 :             :     /*
   13465                 :             :      * Find and check the target constraint
   13466                 :             :      */
   13467                 :         351 :     ScanKeyInit(&skey[0],
   13468                 :             :                 Anum_pg_constraint_conrelid,
   13469                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   13470                 :             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   13471                 :         351 :     ScanKeyInit(&skey[1],
   13472                 :             :                 Anum_pg_constraint_contypid,
   13473                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   13474                 :             :                 ObjectIdGetDatum(InvalidOid));
   13475                 :         351 :     ScanKeyInit(&skey[2],
   13476                 :             :                 Anum_pg_constraint_conname,
   13477                 :             :                 BTEqualStrategyNumber, F_NAMEEQ,
   13478                 :             :                 CStringGetDatum(constrName));
   13479                 :         351 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   13480                 :             :                               true, NULL, 3, skey);
   13481                 :             : 
   13482                 :             :     /* There can be at most one matching row */
   13483         [ -  + ]:         351 :     if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
   13484         [ #  # ]:           0 :         ereport(ERROR,
   13485                 :             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   13486                 :             :                  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   13487                 :             :                         constrName, RelationGetRelationName(rel))));
   13488                 :             : 
   13489                 :         351 :     con = (Form_pg_constraint) GETSTRUCT(tuple);
   13490         [ +  + ]:         351 :     if (con->contype != CONSTRAINT_FOREIGN &&
   13491         [ +  + ]:         174 :         con->contype != CONSTRAINT_CHECK &&
   13492         [ -  + ]:          78 :         con->contype != CONSTRAINT_NOTNULL)
   13493         [ #  # ]:           0 :         ereport(ERROR,
   13494                 :             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   13495                 :             :                 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
   13496                 :             :                        constrName, RelationGetRelationName(rel)),
   13497                 :             :                 errdetail("This operation is not supported for this type of constraint."));
   13498                 :             : 
   13499         [ +  + ]:         351 :     if (!con->conenforced)
   13500         [ +  - ]:           4 :         ereport(ERROR,
   13501                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   13502                 :             :                  errmsg("cannot validate NOT ENFORCED constraint")));
   13503                 :             : 
   13504         [ +  + ]:         347 :     if (!con->convalidated)
   13505                 :             :     {
   13506         [ +  + ]:         335 :         if (con->contype == CONSTRAINT_FOREIGN)
   13507                 :             :         {
   13508                 :         173 :             QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
   13509                 :             :                                         tuple, lockmode);
   13510                 :             :         }
   13511         [ +  + ]:         162 :         else if (con->contype == CONSTRAINT_CHECK)
   13512                 :             :         {
   13513                 :          84 :             QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
   13514                 :             :                                            tuple, recurse, recursing, lockmode);
   13515                 :             :         }
   13516         [ +  - ]:          78 :         else if (con->contype == CONSTRAINT_NOTNULL)
   13517                 :             :         {
   13518                 :          78 :             QueueNNConstraintValidation(wqueue, conrel, rel,
   13519                 :             :                                         tuple, recurse, recursing, lockmode);
   13520                 :             :         }
   13521                 :             : 
   13522                 :         335 :         ObjectAddressSet(address, ConstraintRelationId, con->oid);
   13523                 :             :     }
   13524                 :             :     else
   13525                 :          12 :         address = InvalidObjectAddress; /* already validated */
   13526                 :             : 
   13527                 :         347 :     systable_endscan(scan);
   13528                 :             : 
   13529                 :         347 :     table_close(conrel, RowExclusiveLock);
   13530                 :             : 
   13531                 :         347 :     return address;
   13532                 :             : }
   13533                 :             : 
   13534                 :             : /*
   13535                 :             :  * QueueFKConstraintValidation
   13536                 :             :  *
   13537                 :             :  * Add an entry to the wqueue to validate the given foreign key constraint in
   13538                 :             :  * Phase 3 and update the convalidated field in the pg_constraint catalog
   13539                 :             :  * for the specified relation and all its children.
   13540                 :             :  */
   13541                 :             : static void
   13542                 :         225 : QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel,
   13543                 :             :                             Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
   13544                 :             : {
   13545                 :             :     Form_pg_constraint con;
   13546                 :             :     AlteredTableInfo *tab;
   13547                 :             :     HeapTuple   copyTuple;
   13548                 :             :     Form_pg_constraint copy_con;
   13549                 :             : 
   13550                 :             :     /* since this function recurses, it could be driven to stack overflow */
   13551                 :         225 :     check_stack_depth();
   13552                 :             : 
   13553                 :         225 :     con = (Form_pg_constraint) GETSTRUCT(contuple);
   13554                 :             :     Assert(con->contype == CONSTRAINT_FOREIGN);
   13555                 :             :     Assert(!con->convalidated);
   13556                 :             : 
   13557                 :             :     /*
   13558                 :             :      * Add the validation to phase 3's queue; not needed for partitioned
   13559                 :             :      * tables themselves, only for their partitions.
   13560                 :             :      *
   13561                 :             :      * When the referenced table (pkrelid) is partitioned, the referencing
   13562                 :             :      * table (fkrel) has one pg_constraint row pointing to each partition
   13563                 :             :      * thereof.  These rows are there only to support action triggers and no
   13564                 :             :      * table scan is needed, therefore skip this for them as well.
   13565                 :             :      */
   13566         [ +  + ]:         225 :     if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
   13567         [ +  + ]:         193 :         con->confrelid == pkrelid)
   13568                 :             :     {
   13569                 :             :         NewConstraint *newcon;
   13570                 :             :         Constraint *fkconstraint;
   13571                 :             : 
   13572                 :             :         /* Queue validation for phase 3 */
   13573                 :         181 :         fkconstraint = makeNode(Constraint);
   13574                 :             :         /* for now this is all we need */
   13575                 :         181 :         fkconstraint->conname = pstrdup(NameStr(con->conname));
   13576                 :             : 
   13577                 :         181 :         newcon = palloc0_object(NewConstraint);
   13578                 :         181 :         newcon->name = fkconstraint->conname;
   13579                 :         181 :         newcon->contype = CONSTR_FOREIGN;
   13580                 :         181 :         newcon->refrelid = con->confrelid;
   13581                 :         181 :         newcon->refindid = con->conindid;
   13582                 :         181 :         newcon->conid = con->oid;
   13583                 :         181 :         newcon->qual = (Node *) fkconstraint;
   13584                 :             : 
   13585                 :             :         /* Find or create work queue entry for this table */
   13586                 :         181 :         tab = ATGetQueueEntry(wqueue, fkrel);
   13587                 :         181 :         tab->constraints = lappend(tab->constraints, newcon);
   13588                 :             :     }
   13589                 :             : 
   13590                 :             :     /*
   13591                 :             :      * If the table at either end of the constraint is partitioned, we need to
   13592                 :             :      * recurse and handle every unvalidated constraint that is a child of this
   13593                 :             :      * constraint.
   13594                 :             :      */
   13595   [ +  +  +  + ]:         418 :     if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
   13596                 :         193 :         get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
   13597                 :             :     {
   13598                 :             :         ScanKeyData pkey;
   13599                 :             :         SysScanDesc pscan;
   13600                 :             :         HeapTuple   childtup;
   13601                 :             : 
   13602                 :          52 :         ScanKeyInit(&pkey,
   13603                 :             :                     Anum_pg_constraint_conparentid,
   13604                 :             :                     BTEqualStrategyNumber, F_OIDEQ,
   13605                 :             :                     ObjectIdGetDatum(con->oid));
   13606                 :             : 
   13607                 :          52 :         pscan = systable_beginscan(conrel, ConstraintParentIndexId,
   13608                 :             :                                    true, NULL, 1, &pkey);
   13609                 :             : 
   13610         [ +  + ]:         104 :         while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
   13611                 :             :         {
   13612                 :             :             Form_pg_constraint childcon;
   13613                 :             :             Relation    childrel;
   13614                 :             : 
   13615                 :          52 :             childcon = (Form_pg_constraint) GETSTRUCT(childtup);
   13616                 :             : 
   13617                 :             :             /*
   13618                 :             :              * If the child constraint has already been validated, no further
   13619                 :             :              * action is required for it or its descendants, as they are all
   13620                 :             :              * valid.
   13621                 :             :              */
   13622         [ +  + ]:          52 :             if (childcon->convalidated)
   13623                 :          12 :                 continue;
   13624                 :             : 
   13625                 :          40 :             childrel = table_open(childcon->conrelid, lockmode);
   13626                 :             : 
   13627                 :             :             /*
   13628                 :             :              * NB: Note that pkrelid should be passed as-is during recursion,
   13629                 :             :              * as it is required to identify the root referenced table.
   13630                 :             :              */
   13631                 :          40 :             QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
   13632                 :             :                                         childtup, lockmode);
   13633                 :          40 :             table_close(childrel, NoLock);
   13634                 :             :         }
   13635                 :             : 
   13636                 :          52 :         systable_endscan(pscan);
   13637                 :             :     }
   13638                 :             : 
   13639                 :             :     /*
   13640                 :             :      * Now mark the pg_constraint row as validated (even if we didn't check,
   13641                 :             :      * notably the ones for partitions on the referenced side).
   13642                 :             :      *
   13643                 :             :      * We rely on transaction abort to roll back this change if phase 3
   13644                 :             :      * ultimately finds violating rows.  This is a bit ugly.
   13645                 :             :      */
   13646                 :         225 :     copyTuple = heap_copytuple(contuple);
   13647                 :         225 :     copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   13648                 :         225 :     copy_con->convalidated = true;
   13649                 :         225 :     CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   13650                 :             : 
   13651         [ -  + ]:         225 :     InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   13652                 :             : 
   13653                 :         225 :     heap_freetuple(copyTuple);
   13654                 :         225 : }
   13655                 :             : 
   13656                 :             : /*
   13657                 :             :  * QueueCheckConstraintValidation
   13658                 :             :  *
   13659                 :             :  * Add an entry to the wqueue to validate the given check constraint in Phase 3
   13660                 :             :  * and update the convalidated field in the pg_constraint catalog for the
   13661                 :             :  * specified relation and all its inheriting children.
   13662                 :             :  */
   13663                 :             : static void
   13664                 :          84 : QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
   13665                 :             :                                char *constrName, HeapTuple contuple,
   13666                 :             :                                bool recurse, bool recursing, LOCKMODE lockmode)
   13667                 :             : {
   13668                 :             :     Form_pg_constraint con;
   13669                 :             :     AlteredTableInfo *tab;
   13670                 :             :     HeapTuple   copyTuple;
   13671                 :             :     Form_pg_constraint copy_con;
   13672                 :             : 
   13673                 :          84 :     List       *children = NIL;
   13674                 :             :     ListCell   *child;
   13675                 :             :     NewConstraint *newcon;
   13676                 :             :     Datum       val;
   13677                 :             :     char       *conbin;
   13678                 :             : 
   13679                 :          84 :     con = (Form_pg_constraint) GETSTRUCT(contuple);
   13680                 :             :     Assert(con->contype == CONSTRAINT_CHECK);
   13681                 :             : 
   13682                 :             :     /*
   13683                 :             :      * If we're recursing, the parent has already done this, so skip it. Also,
   13684                 :             :      * if the constraint is a NO INHERIT constraint, we shouldn't try to look
   13685                 :             :      * for it in the children.
   13686                 :             :      */
   13687   [ +  +  +  + ]:          84 :     if (!recursing && !con->connoinherit)
   13688                 :          48 :         children = find_all_inheritors(RelationGetRelid(rel),
   13689                 :             :                                        lockmode, NULL);
   13690                 :             : 
   13691                 :             :     /*
   13692                 :             :      * For CHECK constraints, we must ensure that we only mark the constraint
   13693                 :             :      * as validated on the parent if it's already validated on the children.
   13694                 :             :      *
   13695                 :             :      * We recurse before validating on the parent, to reduce risk of
   13696                 :             :      * deadlocks.
   13697                 :             :      */
   13698   [ +  +  +  +  :         164 :     foreach(child, children)
                   +  + ]
   13699                 :             :     {
   13700                 :          80 :         Oid         childoid = lfirst_oid(child);
   13701                 :             :         Relation    childrel;
   13702                 :             : 
   13703         [ +  + ]:          80 :         if (childoid == RelationGetRelid(rel))
   13704                 :          48 :             continue;
   13705                 :             : 
   13706                 :             :         /*
   13707                 :             :          * If we are told not to recurse, there had better not be any child
   13708                 :             :          * tables, because we can't mark the constraint on the parent valid
   13709                 :             :          * unless it is valid for all child tables.
   13710                 :             :          */
   13711         [ -  + ]:          32 :         if (!recurse)
   13712         [ #  # ]:           0 :             ereport(ERROR,
   13713                 :             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13714                 :             :                      errmsg("constraint must be validated on child tables too")));
   13715                 :             : 
   13716                 :             :         /* find_all_inheritors already got lock */
   13717                 :          32 :         childrel = table_open(childoid, NoLock);
   13718                 :             : 
   13719                 :          32 :         ATExecValidateConstraint(wqueue, childrel, constrName, false,
   13720                 :             :                                  true, lockmode);
   13721                 :          32 :         table_close(childrel, NoLock);
   13722                 :             :     }
   13723                 :             : 
   13724                 :             :     /* Queue validation for phase 3 */
   13725                 :          84 :     newcon = palloc0_object(NewConstraint);
   13726                 :          84 :     newcon->name = constrName;
   13727                 :          84 :     newcon->contype = CONSTR_CHECK;
   13728                 :          84 :     newcon->refrelid = InvalidOid;
   13729                 :          84 :     newcon->refindid = InvalidOid;
   13730                 :          84 :     newcon->conid = con->oid;
   13731                 :             : 
   13732                 :          84 :     val = SysCacheGetAttrNotNull(CONSTROID, contuple,
   13733                 :             :                                  Anum_pg_constraint_conbin);
   13734                 :          84 :     conbin = TextDatumGetCString(val);
   13735                 :          84 :     newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
   13736                 :             : 
   13737                 :             :     /* Find or create work queue entry for this table */
   13738                 :          84 :     tab = ATGetQueueEntry(wqueue, rel);
   13739                 :          84 :     tab->constraints = lappend(tab->constraints, newcon);
   13740                 :             : 
   13741                 :             :     /*
   13742                 :             :      * Invalidate relcache so that others see the new validated constraint.
   13743                 :             :      */
   13744                 :          84 :     CacheInvalidateRelcache(rel);
   13745                 :             : 
   13746                 :             :     /*
   13747                 :             :      * Now update the catalog, while we have the door open.
   13748                 :             :      */
   13749                 :          84 :     copyTuple = heap_copytuple(contuple);
   13750                 :          84 :     copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   13751                 :          84 :     copy_con->convalidated = true;
   13752                 :          84 :     CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   13753                 :             : 
   13754         [ -  + ]:          84 :     InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   13755                 :             : 
   13756                 :          84 :     heap_freetuple(copyTuple);
   13757                 :          84 : }
   13758                 :             : 
   13759                 :             : /*
   13760                 :             :  * QueueNNConstraintValidation
   13761                 :             :  *
   13762                 :             :  * Add an entry to the wqueue to validate the given not-null constraint in
   13763                 :             :  * Phase 3 and update the convalidated field in the pg_constraint catalog for
   13764                 :             :  * the specified relation and all its inheriting children.
   13765                 :             :  */
   13766                 :             : static void
   13767                 :          78 : QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
   13768                 :             :                             HeapTuple contuple, bool recurse, bool recursing,
   13769                 :             :                             LOCKMODE lockmode)
   13770                 :             : {
   13771                 :             :     Form_pg_constraint con;
   13772                 :             :     AlteredTableInfo *tab;
   13773                 :             :     HeapTuple   copyTuple;
   13774                 :             :     Form_pg_constraint copy_con;
   13775                 :          78 :     List       *children = NIL;
   13776                 :             :     AttrNumber  attnum;
   13777                 :             :     char       *colname;
   13778                 :             : 
   13779                 :          78 :     con = (Form_pg_constraint) GETSTRUCT(contuple);
   13780                 :             :     Assert(con->contype == CONSTRAINT_NOTNULL);
   13781                 :             : 
   13782                 :          78 :     attnum = extractNotNullColumn(contuple);
   13783                 :             : 
   13784                 :             :     /*
   13785                 :             :      * If we're recursing, we've already done this for parent, so skip it.
   13786                 :             :      * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
   13787                 :             :      * look for it in the children.
   13788                 :             :      *
   13789                 :             :      * We recurse before validating on the parent, to reduce risk of
   13790                 :             :      * deadlocks.
   13791                 :             :      */
   13792   [ +  +  +  - ]:          78 :     if (!recursing && !con->connoinherit)
   13793                 :          54 :         children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
   13794                 :             : 
   13795                 :          78 :     colname = get_attname(RelationGetRelid(rel), attnum, false);
   13796   [ +  +  +  +  :         262 :     foreach_oid(childoid, children)
                   +  + ]
   13797                 :             :     {
   13798                 :             :         Relation    childrel;
   13799                 :             :         HeapTuple   contup;
   13800                 :             :         Form_pg_constraint childcon;
   13801                 :             :         char       *conname;
   13802                 :             : 
   13803         [ +  + ]:         106 :         if (childoid == RelationGetRelid(rel))
   13804                 :          54 :             continue;
   13805                 :             : 
   13806                 :             :         /*
   13807                 :             :          * If we are told not to recurse, there had better not be any child
   13808                 :             :          * tables, because we can't mark the constraint on the parent valid
   13809                 :             :          * unless it is valid for all child tables.
   13810                 :             :          */
   13811         [ -  + ]:          52 :         if (!recurse)
   13812         [ #  # ]:           0 :             ereport(ERROR,
   13813                 :             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   13814                 :             :                     errmsg("constraint must be validated on child tables too"));
   13815                 :             : 
   13816                 :             :         /*
   13817                 :             :          * The column on child might have a different attnum, so search by
   13818                 :             :          * column name.
   13819                 :             :          */
   13820                 :          52 :         contup = findNotNullConstraint(childoid, colname);
   13821         [ -  + ]:          52 :         if (!contup)
   13822         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
   13823                 :             :                  colname, get_rel_name(childoid));
   13824                 :          52 :         childcon = (Form_pg_constraint) GETSTRUCT(contup);
   13825         [ +  + ]:          52 :         if (childcon->convalidated)
   13826                 :          28 :             continue;
   13827                 :             : 
   13828                 :             :         /* find_all_inheritors already got lock */
   13829                 :          24 :         childrel = table_open(childoid, NoLock);
   13830                 :          24 :         conname = pstrdup(NameStr(childcon->conname));
   13831                 :             : 
   13832                 :             :         /* XXX improve ATExecValidateConstraint API to avoid double search */
   13833                 :          24 :         ATExecValidateConstraint(wqueue, childrel, conname,
   13834                 :             :                                  false, true, lockmode);
   13835                 :          24 :         table_close(childrel, NoLock);
   13836                 :             :     }
   13837                 :             : 
   13838                 :             :     /* Set attnotnull appropriately without queueing another validation */
   13839                 :          78 :     set_attnotnull(NULL, rel, attnum, false);
   13840                 :             : 
   13841                 :          78 :     tab = ATGetQueueEntry(wqueue, rel);
   13842                 :          78 :     tab->verify_new_notnull = true;
   13843                 :             : 
   13844                 :             :     /*
   13845                 :             :      * Invalidate relcache so that others see the new validated constraint.
   13846                 :             :      */
   13847                 :          78 :     CacheInvalidateRelcache(rel);
   13848                 :             : 
   13849                 :             :     /*
   13850                 :             :      * Now update the catalogs, while we have the door open.
   13851                 :             :      */
   13852                 :          78 :     copyTuple = heap_copytuple(contuple);
   13853                 :          78 :     copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   13854                 :          78 :     copy_con->convalidated = true;
   13855                 :          78 :     CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
   13856                 :             : 
   13857         [ -  + ]:          78 :     InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
   13858                 :             : 
   13859                 :          78 :     heap_freetuple(copyTuple);
   13860                 :          78 : }
   13861                 :             : 
   13862                 :             : /*
   13863                 :             :  * transformColumnNameList - transform list of column names
   13864                 :             :  *
   13865                 :             :  * Lookup each name and return its attnum and, optionally, type and collation
   13866                 :             :  * OIDs
   13867                 :             :  *
   13868                 :             :  * Note: the name of this function suggests that it's general-purpose,
   13869                 :             :  * but actually it's only used to look up names appearing in foreign-key
   13870                 :             :  * clauses.  The error messages would need work to use it in other cases,
   13871                 :             :  * and perhaps the validity checks as well.
   13872                 :             :  */
   13873                 :             : static int
   13874                 :        4597 : transformColumnNameList(Oid relId, List *colList,
   13875                 :             :                         int16 *attnums, Oid *atttypids, Oid *attcollids)
   13876                 :             : {
   13877                 :             :     ListCell   *l;
   13878                 :             :     int         attnum;
   13879                 :             : 
   13880                 :        4597 :     attnum = 0;
   13881   [ +  +  +  +  :        8353 :     foreach(l, colList)
                   +  + ]
   13882                 :             :     {
   13883                 :        3800 :         char       *attname = strVal(lfirst(l));
   13884                 :             :         HeapTuple   atttuple;
   13885                 :             :         Form_pg_attribute attform;
   13886                 :             : 
   13887                 :        3800 :         atttuple = SearchSysCacheAttName(relId, attname);
   13888         [ +  + ]:        3800 :         if (!HeapTupleIsValid(atttuple))
   13889         [ +  - ]:          36 :             ereport(ERROR,
   13890                 :             :                     (errcode(ERRCODE_UNDEFINED_COLUMN),
   13891                 :             :                      errmsg("column \"%s\" referenced in foreign key constraint does not exist",
   13892                 :             :                             attname)));
   13893                 :        3764 :         attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   13894         [ +  + ]:        3764 :         if (attform->attnum < 0)
   13895         [ +  - ]:           8 :             ereport(ERROR,
   13896                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   13897                 :             :                      errmsg("system columns cannot be used in foreign keys")));
   13898         [ -  + ]:        3756 :         if (attnum >= INDEX_MAX_KEYS)
   13899         [ #  # ]:           0 :             ereport(ERROR,
   13900                 :             :                     (errcode(ERRCODE_TOO_MANY_COLUMNS),
   13901                 :             :                      errmsg("cannot have more than %d keys in a foreign key",
   13902                 :             :                             INDEX_MAX_KEYS)));
   13903                 :        3756 :         attnums[attnum] = attform->attnum;
   13904         [ +  + ]:        3756 :         if (atttypids != NULL)
   13905                 :        3732 :             atttypids[attnum] = attform->atttypid;
   13906         [ +  + ]:        3756 :         if (attcollids != NULL)
   13907                 :        3732 :             attcollids[attnum] = attform->attcollation;
   13908                 :        3756 :         ReleaseSysCache(atttuple);
   13909                 :        3756 :         attnum++;
   13910                 :             :     }
   13911                 :             : 
   13912                 :        4553 :     return attnum;
   13913                 :             : }
   13914                 :             : 
   13915                 :             : /*
   13916                 :             :  * transformFkeyGetPrimaryKey -
   13917                 :             :  *
   13918                 :             :  *  Look up the names, attnums, types, and collations of the primary key attributes
   13919                 :             :  *  for the pkrel.  Also return the index OID and index opclasses of the
   13920                 :             :  *  index supporting the primary key.  Also return whether the index has
   13921                 :             :  *  WITHOUT OVERLAPS.
   13922                 :             :  *
   13923                 :             :  *  All parameters except pkrel are output parameters.  Also, the function
   13924                 :             :  *  return value is the number of attributes in the primary key.
   13925                 :             :  *
   13926                 :             :  *  Used when the column list in the REFERENCES specification is omitted.
   13927                 :             :  */
   13928                 :             : static int
   13929                 :         900 : transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
   13930                 :             :                            List **attnamelist,
   13931                 :             :                            int16 *attnums, Oid *atttypids, Oid *attcollids,
   13932                 :             :                            Oid *opclasses, bool *pk_has_without_overlaps)
   13933                 :             : {
   13934                 :             :     List       *indexoidlist;
   13935                 :             :     ListCell   *indexoidscan;
   13936                 :         900 :     HeapTuple   indexTuple = NULL;
   13937                 :         900 :     Form_pg_index indexStruct = NULL;
   13938                 :             :     Datum       indclassDatum;
   13939                 :             :     oidvector  *indclass;
   13940                 :             :     int         i;
   13941                 :             : 
   13942                 :             :     /*
   13943                 :             :      * Get the list of index OIDs for the table from the relcache, and look up
   13944                 :             :      * each one in the pg_index syscache until we find one marked primary key
   13945                 :             :      * (hopefully there isn't more than one such).  Insist it's valid, too.
   13946                 :             :      */
   13947                 :         900 :     *indexOid = InvalidOid;
   13948                 :             : 
   13949                 :         900 :     indexoidlist = RelationGetIndexList(pkrel);
   13950                 :             : 
   13951   [ +  -  +  -  :         904 :     foreach(indexoidscan, indexoidlist)
                   +  - ]
   13952                 :             :     {
   13953                 :         904 :         Oid         indexoid = lfirst_oid(indexoidscan);
   13954                 :             : 
   13955                 :         904 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   13956         [ -  + ]:         904 :         if (!HeapTupleIsValid(indexTuple))
   13957         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
   13958                 :         904 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   13959   [ +  +  +  - ]:         904 :         if (indexStruct->indisprimary && indexStruct->indisvalid)
   13960                 :             :         {
   13961                 :             :             /*
   13962                 :             :              * Refuse to use a deferrable primary key.  This is per SQL spec,
   13963                 :             :              * and there would be a lot of interesting semantic problems if we
   13964                 :             :              * tried to allow it.
   13965                 :             :              */
   13966         [ -  + ]:         900 :             if (!indexStruct->indimmediate)
   13967         [ #  # ]:           0 :                 ereport(ERROR,
   13968                 :             :                         (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   13969                 :             :                          errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
   13970                 :             :                                 RelationGetRelationName(pkrel))));
   13971                 :             : 
   13972                 :         900 :             *indexOid = indexoid;
   13973                 :         900 :             break;
   13974                 :             :         }
   13975                 :           4 :         ReleaseSysCache(indexTuple);
   13976                 :             :     }
   13977                 :             : 
   13978                 :         900 :     list_free(indexoidlist);
   13979                 :             : 
   13980                 :             :     /*
   13981                 :             :      * Check that we found it
   13982                 :             :      */
   13983         [ -  + ]:         900 :     if (!OidIsValid(*indexOid))
   13984         [ #  # ]:           0 :         ereport(ERROR,
   13985                 :             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   13986                 :             :                  errmsg("there is no primary key for referenced table \"%s\"",
   13987                 :             :                         RelationGetRelationName(pkrel))));
   13988                 :             : 
   13989                 :             :     /* Must get indclass the hard way */
   13990                 :         900 :     indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   13991                 :             :                                            Anum_pg_index_indclass);
   13992                 :         900 :     indclass = (oidvector *) DatumGetPointer(indclassDatum);
   13993                 :             : 
   13994                 :             :     /*
   13995                 :             :      * Now build the list of PK attributes from the indkey definition (we
   13996                 :             :      * assume a primary key cannot have expressional elements)
   13997                 :             :      */
   13998                 :         900 :     *attnamelist = NIL;
   13999         [ +  + ]:        2116 :     for (i = 0; i < indexStruct->indnkeyatts; i++)
   14000                 :             :     {
   14001                 :        1216 :         int         pkattno = indexStruct->indkey.values[i];
   14002                 :             : 
   14003                 :        1216 :         attnums[i] = pkattno;
   14004                 :        1216 :         atttypids[i] = attnumTypeId(pkrel, pkattno);
   14005                 :        1216 :         attcollids[i] = attnumCollationId(pkrel, pkattno);
   14006                 :        1216 :         opclasses[i] = indclass->values[i];
   14007                 :        1216 :         *attnamelist = lappend(*attnamelist,
   14008                 :        1216 :                                makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
   14009                 :             :     }
   14010                 :             : 
   14011                 :         900 :     *pk_has_without_overlaps = indexStruct->indisexclusion;
   14012                 :             : 
   14013                 :         900 :     ReleaseSysCache(indexTuple);
   14014                 :             : 
   14015                 :         900 :     return i;
   14016                 :             : }
   14017                 :             : 
   14018                 :             : /*
   14019                 :             :  * transformFkeyCheckAttrs -
   14020                 :             :  *
   14021                 :             :  *  Validate that the 'attnums' columns in the 'pkrel' relation are valid to
   14022                 :             :  *  reference as part of a foreign key constraint.
   14023                 :             :  *
   14024                 :             :  *  Returns the OID of the unique index supporting the constraint and
   14025                 :             :  *  populates the caller-provided 'opclasses' array with the opclasses
   14026                 :             :  *  associated with the index columns.  Also sets whether the index
   14027                 :             :  *  uses WITHOUT OVERLAPS.
   14028                 :             :  *
   14029                 :             :  *  Raises an ERROR on validation failure.
   14030                 :             :  */
   14031                 :             : static Oid
   14032                 :         879 : transformFkeyCheckAttrs(Relation pkrel,
   14033                 :             :                         int numattrs, int16 *attnums,
   14034                 :             :                         bool with_period, Oid *opclasses,
   14035                 :             :                         bool *pk_has_without_overlaps)
   14036                 :             : {
   14037                 :         879 :     Oid         indexoid = InvalidOid;
   14038                 :         879 :     bool        found = false;
   14039                 :         879 :     bool        found_deferrable = false;
   14040                 :             :     List       *indexoidlist;
   14041                 :             :     ListCell   *indexoidscan;
   14042                 :             :     int         i,
   14043                 :             :                 j;
   14044                 :             : 
   14045                 :             :     /*
   14046                 :             :      * Reject duplicate appearances of columns in the referenced-columns list.
   14047                 :             :      * Such a case is forbidden by the SQL standard, and even if we thought it
   14048                 :             :      * useful to allow it, there would be ambiguity about how to match the
   14049                 :             :      * list to unique indexes (in particular, it'd be unclear which index
   14050                 :             :      * opclass goes with which FK column).
   14051                 :             :      */
   14052         [ +  + ]:        2051 :     for (i = 0; i < numattrs; i++)
   14053                 :             :     {
   14054         [ +  + ]:        1547 :         for (j = i + 1; j < numattrs; j++)
   14055                 :             :         {
   14056         [ +  + ]:         375 :             if (attnums[i] == attnums[j])
   14057         [ +  - ]:          16 :                 ereport(ERROR,
   14058                 :             :                         (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   14059                 :             :                          errmsg("foreign key referenced-columns list must not contain duplicates")));
   14060                 :             :         }
   14061                 :             :     }
   14062                 :             : 
   14063                 :             :     /*
   14064                 :             :      * Get the list of index OIDs for the table from the relcache, and look up
   14065                 :             :      * each one in the pg_index syscache, and match unique indexes to the list
   14066                 :             :      * of attnums we are given.
   14067                 :             :      */
   14068                 :         863 :     indexoidlist = RelationGetIndexList(pkrel);
   14069                 :             : 
   14070   [ +  -  +  +  :         982 :     foreach(indexoidscan, indexoidlist)
                   +  + ]
   14071                 :             :     {
   14072                 :             :         HeapTuple   indexTuple;
   14073                 :             :         Form_pg_index indexStruct;
   14074                 :             : 
   14075                 :         974 :         indexoid = lfirst_oid(indexoidscan);
   14076                 :         974 :         indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
   14077         [ -  + ]:         974 :         if (!HeapTupleIsValid(indexTuple))
   14078         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for index %u", indexoid);
   14079                 :         974 :         indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
   14080                 :             : 
   14081                 :             :         /*
   14082                 :             :          * Must have the right number of columns; must be unique (or if
   14083                 :             :          * temporal then exclusion instead) and not a partial index; forget it
   14084                 :             :          * if there are any expressions, too. Invalid indexes are out as well.
   14085                 :             :          */
   14086   [ +  +  +  +  :         974 :         if (indexStruct->indnkeyatts == numattrs &&
                   +  + ]
   14087                 :         893 :             (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
   14088   [ +  -  +  - ]:        1786 :             indexStruct->indisvalid &&
   14089         [ +  - ]:        1786 :             heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
   14090                 :         893 :             heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
   14091                 :             :         {
   14092                 :             :             Datum       indclassDatum;
   14093                 :             :             oidvector  *indclass;
   14094                 :             : 
   14095                 :             :             /* Must get indclass the hard way */
   14096                 :         893 :             indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
   14097                 :             :                                                    Anum_pg_index_indclass);
   14098                 :         893 :             indclass = (oidvector *) DatumGetPointer(indclassDatum);
   14099                 :             : 
   14100                 :             :             /*
   14101                 :             :              * The given attnum list may match the index columns in any order.
   14102                 :             :              * Check for a match, and extract the appropriate opclasses while
   14103                 :             :              * we're at it.
   14104                 :             :              *
   14105                 :             :              * We know that attnums[] is duplicate-free per the test at the
   14106                 :             :              * start of this function, and we checked above that the number of
   14107                 :             :              * index columns agrees, so if we find a match for each attnums[]
   14108                 :             :              * entry then we must have a one-to-one match in some order.
   14109                 :             :              */
   14110         [ +  + ]:        2057 :             for (i = 0; i < numattrs; i++)
   14111                 :             :             {
   14112                 :        1202 :                 found = false;
   14113         [ +  + ]:        1599 :                 for (j = 0; j < numattrs; j++)
   14114                 :             :                 {
   14115         [ +  + ]:        1561 :                     if (attnums[i] == indexStruct->indkey.values[j])
   14116                 :             :                     {
   14117                 :        1164 :                         opclasses[i] = indclass->values[j];
   14118                 :        1164 :                         found = true;
   14119                 :        1164 :                         break;
   14120                 :             :                     }
   14121                 :             :                 }
   14122         [ +  + ]:        1202 :                 if (!found)
   14123                 :          38 :                     break;
   14124                 :             :             }
   14125                 :             :             /* The last attribute in the index must be the PERIOD FK part */
   14126   [ +  +  +  + ]:         893 :             if (found && with_period)
   14127                 :             :             {
   14128                 :          80 :                 int16       periodattnum = attnums[numattrs - 1];
   14129                 :             : 
   14130                 :          80 :                 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
   14131                 :             :             }
   14132                 :             : 
   14133                 :             :             /*
   14134                 :             :              * Refuse to use a deferrable unique/primary key.  This is per SQL
   14135                 :             :              * spec, and there would be a lot of interesting semantic problems
   14136                 :             :              * if we tried to allow it.
   14137                 :             :              */
   14138   [ +  +  -  + ]:         893 :             if (found && !indexStruct->indimmediate)
   14139                 :             :             {
   14140                 :             :                 /*
   14141                 :             :                  * Remember that we found an otherwise matching index, so that
   14142                 :             :                  * we can generate a more appropriate error message.
   14143                 :             :                  */
   14144                 :           0 :                 found_deferrable = true;
   14145                 :           0 :                 found = false;
   14146                 :             :             }
   14147                 :             : 
   14148                 :             :             /* We need to know whether the index has WITHOUT OVERLAPS */
   14149         [ +  + ]:         893 :             if (found)
   14150                 :         855 :                 *pk_has_without_overlaps = indexStruct->indisexclusion;
   14151                 :             :         }
   14152                 :         974 :         ReleaseSysCache(indexTuple);
   14153         [ +  + ]:         974 :         if (found)
   14154                 :         855 :             break;
   14155                 :             :     }
   14156                 :             : 
   14157         [ +  + ]:         863 :     if (!found)
   14158                 :             :     {
   14159         [ -  + ]:           8 :         if (found_deferrable)
   14160         [ #  # ]:           0 :             ereport(ERROR,
   14161                 :             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   14162                 :             :                      errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
   14163                 :             :                             RelationGetRelationName(pkrel))));
   14164                 :             :         else
   14165         [ +  - ]:           8 :             ereport(ERROR,
   14166                 :             :                     (errcode(ERRCODE_INVALID_FOREIGN_KEY),
   14167                 :             :                      errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
   14168                 :             :                             RelationGetRelationName(pkrel))));
   14169                 :             :     }
   14170                 :             : 
   14171                 :         855 :     list_free(indexoidlist);
   14172                 :             : 
   14173                 :         855 :     return indexoid;
   14174                 :             : }
   14175                 :             : 
   14176                 :             : /*
   14177                 :             :  * findFkeyCast -
   14178                 :             :  *
   14179                 :             :  *  Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
   14180                 :             :  *  Caller has equal regard for binary coercibility and for an exact match.
   14181                 :             :  */
   14182                 :             : static CoercionPathType
   14183                 :           8 : findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
   14184                 :             : {
   14185                 :             :     CoercionPathType ret;
   14186                 :             : 
   14187         [ +  - ]:           8 :     if (targetTypeId == sourceTypeId)
   14188                 :             :     {
   14189                 :           8 :         ret = COERCION_PATH_RELABELTYPE;
   14190                 :           8 :         *funcid = InvalidOid;
   14191                 :             :     }
   14192                 :             :     else
   14193                 :             :     {
   14194                 :           0 :         ret = find_coercion_pathway(targetTypeId, sourceTypeId,
   14195                 :             :                                     COERCION_IMPLICIT, funcid);
   14196         [ #  # ]:           0 :         if (ret == COERCION_PATH_NONE)
   14197                 :             :             /* A previously-relied-upon cast is now gone. */
   14198         [ #  # ]:           0 :             elog(ERROR, "could not find cast from %u to %u",
   14199                 :             :                  sourceTypeId, targetTypeId);
   14200                 :             :     }
   14201                 :             : 
   14202                 :           8 :     return ret;
   14203                 :             : }
   14204                 :             : 
   14205                 :             : /*
   14206                 :             :  * Permissions checks on the referenced table for ADD FOREIGN KEY
   14207                 :             :  *
   14208                 :             :  * Note: we have already checked that the user owns the referencing table,
   14209                 :             :  * else we'd have failed much earlier; no additional checks are needed for it.
   14210                 :             :  */
   14211                 :             : static void
   14212                 :        1731 : checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
   14213                 :             : {
   14214                 :        1731 :     Oid         roleid = GetUserId();
   14215                 :             :     AclResult   aclresult;
   14216                 :             :     int         i;
   14217                 :             : 
   14218                 :             :     /* Okay if we have relation-level REFERENCES permission */
   14219                 :        1731 :     aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
   14220                 :             :                                   ACL_REFERENCES);
   14221         [ +  - ]:        1731 :     if (aclresult == ACLCHECK_OK)
   14222                 :        1731 :         return;
   14223                 :             :     /* Else we must have REFERENCES on each column */
   14224         [ #  # ]:           0 :     for (i = 0; i < natts; i++)
   14225                 :             :     {
   14226                 :           0 :         aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
   14227                 :             :                                           roleid, ACL_REFERENCES);
   14228         [ #  # ]:           0 :         if (aclresult != ACLCHECK_OK)
   14229                 :           0 :             aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
   14230                 :           0 :                            RelationGetRelationName(rel));
   14231                 :             :     }
   14232                 :             : }
   14233                 :             : 
   14234                 :             : /*
   14235                 :             :  * Scan the existing rows in a table to verify they meet a proposed FK
   14236                 :             :  * constraint.
   14237                 :             :  *
   14238                 :             :  * Caller must have opened and locked both relations appropriately.
   14239                 :             :  */
   14240                 :             : static void
   14241                 :         832 : validateForeignKeyConstraint(char *conname,
   14242                 :             :                              Relation rel,
   14243                 :             :                              Relation pkrel,
   14244                 :             :                              Oid pkindOid,
   14245                 :             :                              Oid constraintOid,
   14246                 :             :                              bool hasperiod)
   14247                 :             : {
   14248                 :             :     TupleTableSlot *slot;
   14249                 :             :     TableScanDesc scan;
   14250                 :         832 :     Trigger     trig = {0};
   14251                 :             :     Snapshot    snapshot;
   14252                 :             :     MemoryContext oldcxt;
   14253                 :             :     MemoryContext perTupCxt;
   14254                 :             : 
   14255         [ -  + ]:         832 :     ereport(DEBUG1,
   14256                 :             :             (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
   14257                 :             : 
   14258                 :             :     /*
   14259                 :             :      * Build a trigger call structure; we'll need it either way.
   14260                 :             :      */
   14261                 :         832 :     trig.tgoid = InvalidOid;
   14262                 :         832 :     trig.tgname = conname;
   14263                 :         832 :     trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   14264                 :         832 :     trig.tgisinternal = true;
   14265                 :         832 :     trig.tgconstrrelid = RelationGetRelid(pkrel);
   14266                 :         832 :     trig.tgconstrindid = pkindOid;
   14267                 :         832 :     trig.tgconstraint = constraintOid;
   14268                 :         832 :     trig.tgdeferrable = false;
   14269                 :         832 :     trig.tginitdeferred = false;
   14270                 :             :     /* we needn't fill in remaining fields */
   14271                 :             : 
   14272                 :             :     /*
   14273                 :             :      * See if we can do it with a single LEFT JOIN query.  A false result
   14274                 :             :      * indicates we must proceed with the fire-the-trigger method. We can't do
   14275                 :             :      * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
   14276                 :             :      * left joins.
   14277                 :             :      */
   14278   [ +  +  +  + ]:         832 :     if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
   14279                 :         702 :         return;
   14280                 :             : 
   14281                 :             :     /*
   14282                 :             :      * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
   14283                 :             :      * if that tuple had just been inserted.  If any of those fail, it should
   14284                 :             :      * ereport(ERROR) and that's that.
   14285                 :             :      */
   14286                 :          71 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
   14287                 :          71 :     slot = table_slot_create(rel, NULL);
   14288                 :          71 :     scan = table_beginscan(rel, snapshot, 0, NULL,
   14289                 :             :                            SO_NONE);
   14290                 :          71 :     perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   14291                 :             :                                       "validateForeignKeyConstraint",
   14292                 :             :                                       ALLOCSET_SMALL_SIZES);
   14293                 :          71 :     oldcxt = MemoryContextSwitchTo(perTupCxt);
   14294                 :             : 
   14295         [ +  + ]:         127 :     while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
   14296                 :             :     {
   14297                 :          68 :         LOCAL_FCINFO(fcinfo, 0);
   14298                 :          68 :         TriggerData trigdata = {0};
   14299                 :             : 
   14300         [ -  + ]:          68 :         CHECK_FOR_INTERRUPTS();
   14301                 :             : 
   14302                 :             :         /*
   14303                 :             :          * Make a call to the trigger function
   14304                 :             :          *
   14305                 :             :          * No parameters are passed, but we do set a context
   14306                 :             :          */
   14307   [ +  -  +  -  :         340 :         MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
          +  -  +  -  +  
                      + ]
   14308                 :             : 
   14309                 :             :         /*
   14310                 :             :          * We assume RI_FKey_check_ins won't look at flinfo...
   14311                 :             :          */
   14312                 :          68 :         trigdata.type = T_TriggerData;
   14313                 :          68 :         trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
   14314                 :          68 :         trigdata.tg_relation = rel;
   14315                 :          68 :         trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
   14316                 :          68 :         trigdata.tg_trigslot = slot;
   14317                 :          68 :         trigdata.tg_trigger = &trig;
   14318                 :             : 
   14319                 :          68 :         fcinfo->context = (Node *) &trigdata;
   14320                 :             : 
   14321                 :          68 :         RI_FKey_check_ins(fcinfo);
   14322                 :             : 
   14323                 :          56 :         MemoryContextReset(perTupCxt);
   14324                 :             :     }
   14325                 :             : 
   14326                 :          59 :     MemoryContextSwitchTo(oldcxt);
   14327                 :          59 :     MemoryContextDelete(perTupCxt);
   14328                 :          59 :     table_endscan(scan);
   14329                 :          59 :     UnregisterSnapshot(snapshot);
   14330                 :          59 :     ExecDropSingleTupleTableSlot(slot);
   14331                 :             : }
   14332                 :             : 
   14333                 :             : /*
   14334                 :             :  * CreateFKCheckTrigger
   14335                 :             :  *      Creates the insert (on_insert=true) or update "check" trigger that
   14336                 :             :  *      implements a given foreign key
   14337                 :             :  *
   14338                 :             :  * Returns the OID of the so created trigger.
   14339                 :             :  */
   14340                 :             : static Oid
   14341                 :        4180 : CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
   14342                 :             :                      Oid constraintOid, Oid indexOid, Oid parentTrigOid,
   14343                 :             :                      bool on_insert)
   14344                 :             : {
   14345                 :             :     ObjectAddress trigAddress;
   14346                 :             :     CreateTrigStmt *fk_trigger;
   14347                 :             : 
   14348                 :             :     /*
   14349                 :             :      * Note: for a self-referential FK (referencing and referenced tables are
   14350                 :             :      * the same), it is important that the ON UPDATE action fires before the
   14351                 :             :      * CHECK action, since both triggers will fire on the same row during an
   14352                 :             :      * UPDATE event; otherwise the CHECK trigger will be checking a non-final
   14353                 :             :      * state of the row.  Triggers fire in name order, so we ensure this by
   14354                 :             :      * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
   14355                 :             :      * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
   14356                 :             :      */
   14357                 :        4180 :     fk_trigger = makeNode(CreateTrigStmt);
   14358                 :        4180 :     fk_trigger->replace = false;
   14359                 :        4180 :     fk_trigger->isconstraint = true;
   14360                 :        4180 :     fk_trigger->trigname = "RI_ConstraintTrigger_c";
   14361                 :        4180 :     fk_trigger->relation = NULL;
   14362                 :             : 
   14363                 :             :     /* Either ON INSERT or ON UPDATE */
   14364         [ +  + ]:        4180 :     if (on_insert)
   14365                 :             :     {
   14366                 :        2090 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
   14367                 :        2090 :         fk_trigger->events = TRIGGER_TYPE_INSERT;
   14368                 :             :     }
   14369                 :             :     else
   14370                 :             :     {
   14371                 :        2090 :         fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
   14372                 :        2090 :         fk_trigger->events = TRIGGER_TYPE_UPDATE;
   14373                 :             :     }
   14374                 :             : 
   14375                 :        4180 :     fk_trigger->args = NIL;
   14376                 :        4180 :     fk_trigger->row = true;
   14377                 :        4180 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   14378                 :        4180 :     fk_trigger->columns = NIL;
   14379                 :        4180 :     fk_trigger->whenClause = NULL;
   14380                 :        4180 :     fk_trigger->transitionRels = NIL;
   14381                 :        4180 :     fk_trigger->deferrable = fkconstraint->deferrable;
   14382                 :        4180 :     fk_trigger->initdeferred = fkconstraint->initdeferred;
   14383                 :        4180 :     fk_trigger->constrrel = NULL;
   14384                 :             : 
   14385                 :        4180 :     trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
   14386                 :             :                                 constraintOid, indexOid, InvalidOid,
   14387                 :             :                                 parentTrigOid, NULL, true, false);
   14388                 :             : 
   14389                 :             :     /* Make changes-so-far visible */
   14390                 :        4180 :     CommandCounterIncrement();
   14391                 :             : 
   14392                 :        4180 :     return trigAddress.objectId;
   14393                 :             : }
   14394                 :             : 
   14395                 :             : /*
   14396                 :             :  * createForeignKeyActionTriggers
   14397                 :             :  *      Create the referenced-side "action" triggers that implement a foreign
   14398                 :             :  *      key.
   14399                 :             :  *
   14400                 :             :  * Returns the OIDs of the so created triggers in *deleteTrigOid and
   14401                 :             :  * *updateTrigOid.
   14402                 :             :  */
   14403                 :             : static void
   14404                 :        2377 : createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
   14405                 :             :                                Oid constraintOid, Oid indexOid,
   14406                 :             :                                Oid parentDelTrigger, Oid parentUpdTrigger,
   14407                 :             :                                Oid *deleteTrigOid, Oid *updateTrigOid)
   14408                 :             : {
   14409                 :             :     CreateTrigStmt *fk_trigger;
   14410                 :             :     ObjectAddress trigAddress;
   14411                 :             : 
   14412                 :             :     /*
   14413                 :             :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   14414                 :             :      * DELETE action on the referenced table.
   14415                 :             :      */
   14416                 :        2377 :     fk_trigger = makeNode(CreateTrigStmt);
   14417                 :        2377 :     fk_trigger->replace = false;
   14418                 :        2377 :     fk_trigger->isconstraint = true;
   14419                 :        2377 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
   14420                 :        2377 :     fk_trigger->relation = NULL;
   14421                 :        2377 :     fk_trigger->args = NIL;
   14422                 :        2377 :     fk_trigger->row = true;
   14423                 :        2377 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   14424                 :        2377 :     fk_trigger->events = TRIGGER_TYPE_DELETE;
   14425                 :        2377 :     fk_trigger->columns = NIL;
   14426                 :        2377 :     fk_trigger->whenClause = NULL;
   14427                 :        2377 :     fk_trigger->transitionRels = NIL;
   14428                 :        2377 :     fk_trigger->constrrel = NULL;
   14429                 :             : 
   14430   [ +  +  +  +  :        2377 :     switch (fkconstraint->fk_del_action)
                   +  - ]
   14431                 :             :     {
   14432                 :        1948 :         case FKCONSTR_ACTION_NOACTION:
   14433                 :        1948 :             fk_trigger->deferrable = fkconstraint->deferrable;
   14434                 :        1948 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
   14435                 :        1948 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
   14436                 :        1948 :             break;
   14437                 :          20 :         case FKCONSTR_ACTION_RESTRICT:
   14438                 :          20 :             fk_trigger->deferrable = false;
   14439                 :          20 :             fk_trigger->initdeferred = false;
   14440                 :          20 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
   14441                 :          20 :             break;
   14442                 :         306 :         case FKCONSTR_ACTION_CASCADE:
   14443                 :         306 :             fk_trigger->deferrable = false;
   14444                 :         306 :             fk_trigger->initdeferred = false;
   14445                 :         306 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
   14446                 :         306 :             break;
   14447                 :          63 :         case FKCONSTR_ACTION_SETNULL:
   14448                 :          63 :             fk_trigger->deferrable = false;
   14449                 :          63 :             fk_trigger->initdeferred = false;
   14450                 :          63 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
   14451                 :          63 :             break;
   14452                 :          40 :         case FKCONSTR_ACTION_SETDEFAULT:
   14453                 :          40 :             fk_trigger->deferrable = false;
   14454                 :          40 :             fk_trigger->initdeferred = false;
   14455                 :          40 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
   14456                 :          40 :             break;
   14457                 :           0 :         default:
   14458         [ #  # ]:           0 :             elog(ERROR, "unrecognized FK action type: %d",
   14459                 :             :                  (int) fkconstraint->fk_del_action);
   14460                 :             :             break;
   14461                 :             :     }
   14462                 :             : 
   14463                 :        2377 :     trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
   14464                 :             :                                 constraintOid, indexOid, InvalidOid,
   14465                 :             :                                 parentDelTrigger, NULL, true, false);
   14466         [ +  - ]:        2377 :     if (deleteTrigOid)
   14467                 :        2377 :         *deleteTrigOid = trigAddress.objectId;
   14468                 :             : 
   14469                 :             :     /* Make changes-so-far visible */
   14470                 :        2377 :     CommandCounterIncrement();
   14471                 :             : 
   14472                 :             :     /*
   14473                 :             :      * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
   14474                 :             :      * UPDATE action on the referenced table.
   14475                 :             :      */
   14476                 :        2377 :     fk_trigger = makeNode(CreateTrigStmt);
   14477                 :        2377 :     fk_trigger->replace = false;
   14478                 :        2377 :     fk_trigger->isconstraint = true;
   14479                 :        2377 :     fk_trigger->trigname = "RI_ConstraintTrigger_a";
   14480                 :        2377 :     fk_trigger->relation = NULL;
   14481                 :        2377 :     fk_trigger->args = NIL;
   14482                 :        2377 :     fk_trigger->row = true;
   14483                 :        2377 :     fk_trigger->timing = TRIGGER_TYPE_AFTER;
   14484                 :        2377 :     fk_trigger->events = TRIGGER_TYPE_UPDATE;
   14485                 :        2377 :     fk_trigger->columns = NIL;
   14486                 :        2377 :     fk_trigger->whenClause = NULL;
   14487                 :        2377 :     fk_trigger->transitionRels = NIL;
   14488                 :        2377 :     fk_trigger->constrrel = NULL;
   14489                 :             : 
   14490   [ +  +  +  +  :        2377 :     switch (fkconstraint->fk_upd_action)
                   +  - ]
   14491                 :             :     {
   14492                 :        2073 :         case FKCONSTR_ACTION_NOACTION:
   14493                 :        2073 :             fk_trigger->deferrable = fkconstraint->deferrable;
   14494                 :        2073 :             fk_trigger->initdeferred = fkconstraint->initdeferred;
   14495                 :        2073 :             fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
   14496                 :        2073 :             break;
   14497                 :          24 :         case FKCONSTR_ACTION_RESTRICT:
   14498                 :          24 :             fk_trigger->deferrable = false;
   14499                 :          24 :             fk_trigger->initdeferred = false;
   14500                 :          24 :             fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
   14501                 :          24 :             break;
   14502                 :         211 :         case FKCONSTR_ACTION_CASCADE:
   14503                 :         211 :             fk_trigger->deferrable = false;
   14504                 :         211 :             fk_trigger->initdeferred = false;
   14505                 :         211 :             fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
   14506                 :         211 :             break;
   14507                 :          41 :         case FKCONSTR_ACTION_SETNULL:
   14508                 :          41 :             fk_trigger->deferrable = false;
   14509                 :          41 :             fk_trigger->initdeferred = false;
   14510                 :          41 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
   14511                 :          41 :             break;
   14512                 :          28 :         case FKCONSTR_ACTION_SETDEFAULT:
   14513                 :          28 :             fk_trigger->deferrable = false;
   14514                 :          28 :             fk_trigger->initdeferred = false;
   14515                 :          28 :             fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
   14516                 :          28 :             break;
   14517                 :           0 :         default:
   14518         [ #  # ]:           0 :             elog(ERROR, "unrecognized FK action type: %d",
   14519                 :             :                  (int) fkconstraint->fk_upd_action);
   14520                 :             :             break;
   14521                 :             :     }
   14522                 :             : 
   14523                 :        2377 :     trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
   14524                 :             :                                 constraintOid, indexOid, InvalidOid,
   14525                 :             :                                 parentUpdTrigger, NULL, true, false);
   14526         [ +  - ]:        2377 :     if (updateTrigOid)
   14527                 :        2377 :         *updateTrigOid = trigAddress.objectId;
   14528                 :        2377 : }
   14529                 :             : 
   14530                 :             : /*
   14531                 :             :  * createForeignKeyCheckTriggers
   14532                 :             :  *      Create the referencing-side "check" triggers that implement a foreign
   14533                 :             :  *      key.
   14534                 :             :  *
   14535                 :             :  * Returns the OIDs of the so created triggers in *insertTrigOid and
   14536                 :             :  * *updateTrigOid.
   14537                 :             :  */
   14538                 :             : static void
   14539                 :        2090 : createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
   14540                 :             :                               Constraint *fkconstraint, Oid constraintOid,
   14541                 :             :                               Oid indexOid,
   14542                 :             :                               Oid parentInsTrigger, Oid parentUpdTrigger,
   14543                 :             :                               Oid *insertTrigOid, Oid *updateTrigOid)
   14544                 :             : {
   14545                 :        2090 :     *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   14546                 :             :                                           constraintOid, indexOid,
   14547                 :             :                                           parentInsTrigger, true);
   14548                 :        2090 :     *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
   14549                 :             :                                           constraintOid, indexOid,
   14550                 :             :                                           parentUpdTrigger, false);
   14551                 :        2090 : }
   14552                 :             : 
   14553                 :             : /*
   14554                 :             :  * ALTER TABLE DROP CONSTRAINT
   14555                 :             :  *
   14556                 :             :  * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
   14557                 :             :  */
   14558                 :             : static void
   14559                 :         578 : ATExecDropConstraint(Relation rel, const char *constrName,
   14560                 :             :                      DropBehavior behavior, bool recurse,
   14561                 :             :                      bool missing_ok, LOCKMODE lockmode)
   14562                 :             : {
   14563                 :             :     Relation    conrel;
   14564                 :             :     SysScanDesc scan;
   14565                 :             :     ScanKeyData skey[3];
   14566                 :             :     HeapTuple   tuple;
   14567                 :         578 :     bool        found = false;
   14568                 :             : 
   14569                 :         578 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   14570                 :             : 
   14571                 :             :     /*
   14572                 :             :      * Find and drop the target constraint
   14573                 :             :      */
   14574                 :         578 :     ScanKeyInit(&skey[0],
   14575                 :             :                 Anum_pg_constraint_conrelid,
   14576                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   14577                 :             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   14578                 :         578 :     ScanKeyInit(&skey[1],
   14579                 :             :                 Anum_pg_constraint_contypid,
   14580                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   14581                 :             :                 ObjectIdGetDatum(InvalidOid));
   14582                 :         578 :     ScanKeyInit(&skey[2],
   14583                 :             :                 Anum_pg_constraint_conname,
   14584                 :             :                 BTEqualStrategyNumber, F_NAMEEQ,
   14585                 :             :                 CStringGetDatum(constrName));
   14586                 :         578 :     scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   14587                 :             :                               true, NULL, 3, skey);
   14588                 :             : 
   14589                 :             :     /* There can be at most one matching row */
   14590         [ +  + ]:         578 :     if (HeapTupleIsValid(tuple = systable_getnext(scan)))
   14591                 :             :     {
   14592                 :         554 :         dropconstraint_internal(rel, tuple, behavior, recurse, false,
   14593                 :             :                                 missing_ok, lockmode);
   14594                 :         430 :         found = true;
   14595                 :             :     }
   14596                 :             : 
   14597                 :         454 :     systable_endscan(scan);
   14598                 :             : 
   14599         [ +  + ]:         454 :     if (!found)
   14600                 :             :     {
   14601         [ +  + ]:          24 :         if (!missing_ok)
   14602         [ +  - ]:          16 :             ereport(ERROR,
   14603                 :             :                     errcode(ERRCODE_UNDEFINED_OBJECT),
   14604                 :             :                     errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   14605                 :             :                            constrName, RelationGetRelationName(rel)));
   14606                 :             :         else
   14607         [ +  - ]:           8 :             ereport(NOTICE,
   14608                 :             :                     errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
   14609                 :             :                            constrName, RelationGetRelationName(rel)));
   14610                 :             :     }
   14611                 :             : 
   14612                 :         438 :     table_close(conrel, RowExclusiveLock);
   14613                 :         438 : }
   14614                 :             : 
   14615                 :             : /*
   14616                 :             :  * Remove a constraint, using its pg_constraint tuple
   14617                 :             :  *
   14618                 :             :  * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
   14619                 :             :  * DROP NOT NULL.
   14620                 :             :  *
   14621                 :             :  * Returns the address of the constraint being removed.
   14622                 :             :  */
   14623                 :             : static ObjectAddress
   14624                 :         858 : dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
   14625                 :             :                         bool recurse, bool recursing, bool missing_ok,
   14626                 :             :                         LOCKMODE lockmode)
   14627                 :             : {
   14628                 :             :     Relation    conrel;
   14629                 :             :     Form_pg_constraint con;
   14630                 :             :     ObjectAddress conobj;
   14631                 :             :     List       *children;
   14632                 :         858 :     bool        is_no_inherit_constraint = false;
   14633                 :             :     char       *constrName;
   14634                 :         858 :     char       *colname = NULL;
   14635                 :             : 
   14636                 :             :     /* Guard against stack overflow due to overly deep inheritance tree. */
   14637                 :         858 :     check_stack_depth();
   14638                 :             : 
   14639                 :             :     /* At top level, permission check was done in ATPrepCmd, else do it */
   14640         [ +  + ]:         858 :     if (recursing)
   14641                 :         159 :         ATSimplePermissions(AT_DropConstraint, rel,
   14642                 :             :                             ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   14643                 :             : 
   14644                 :         854 :     conrel = table_open(ConstraintRelationId, RowExclusiveLock);
   14645                 :             : 
   14646                 :         854 :     con = (Form_pg_constraint) GETSTRUCT(constraintTup);
   14647                 :         854 :     constrName = NameStr(con->conname);
   14648                 :             : 
   14649                 :             :     /* Don't allow drop of inherited constraints */
   14650   [ +  +  +  + ]:         854 :     if (con->coninhcount > 0 && !recursing)
   14651         [ +  - ]:         104 :         ereport(ERROR,
   14652                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14653                 :             :                  errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
   14654                 :             :                         constrName, RelationGetRelationName(rel))));
   14655                 :             : 
   14656                 :             :     /*
   14657                 :             :      * Reset pg_constraint.attnotnull, if this is a not-null constraint.
   14658                 :             :      *
   14659                 :             :      * While doing that, we're in a good position to disallow dropping a not-
   14660                 :             :      * null constraint underneath a primary key, a replica identity index, or
   14661                 :             :      * a generated identity column.
   14662                 :             :      */
   14663         [ +  + ]:         750 :     if (con->contype == CONSTRAINT_NOTNULL)
   14664                 :             :     {
   14665                 :         207 :         Relation    attrel = table_open(AttributeRelationId, RowExclusiveLock);
   14666                 :         207 :         AttrNumber  attnum = extractNotNullColumn(constraintTup);
   14667                 :             :         Bitmapset  *pkattrs;
   14668                 :             :         Bitmapset  *irattrs;
   14669                 :             :         HeapTuple   atttup;
   14670                 :             :         Form_pg_attribute attForm;
   14671                 :             : 
   14672                 :             :         /* save column name for recursion step */
   14673                 :         207 :         colname = get_attname(RelationGetRelid(rel), attnum, false);
   14674                 :             : 
   14675                 :             :         /*
   14676                 :             :          * Disallow if it's in the primary key.  For partitioned tables we
   14677                 :             :          * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
   14678                 :             :          * return NULL if the primary key is invalid; but we still need to
   14679                 :             :          * protect not-null constraints under such a constraint, so check the
   14680                 :             :          * slow way.
   14681                 :             :          */
   14682                 :         207 :         pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
   14683                 :             : 
   14684         [ +  + ]:         207 :         if (pkattrs == NULL &&
   14685         [ +  + ]:         187 :             rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   14686                 :             :         {
   14687                 :          12 :             Oid         pkindex = RelationGetPrimaryKeyIndex(rel, true);
   14688                 :             : 
   14689         [ -  + ]:          12 :             if (OidIsValid(pkindex))
   14690                 :             :             {
   14691                 :           0 :                 Relation    pk = relation_open(pkindex, AccessShareLock);
   14692                 :             : 
   14693                 :           0 :                 pkattrs = NULL;
   14694         [ #  # ]:           0 :                 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
   14695                 :           0 :                     pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
   14696                 :             : 
   14697                 :           0 :                 relation_close(pk, AccessShareLock);
   14698                 :             :             }
   14699                 :             :         }
   14700                 :             : 
   14701   [ +  +  +  + ]:         227 :         if (pkattrs &&
   14702                 :          20 :             bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
   14703         [ +  - ]:          16 :             ereport(ERROR,
   14704                 :             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14705                 :             :                     errmsg("column \"%s\" is in a primary key",
   14706                 :             :                            get_attname(RelationGetRelid(rel), attnum, false)));
   14707                 :             : 
   14708                 :             :         /* Disallow if it's in the replica identity */
   14709                 :         191 :         irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
   14710         [ +  + ]:         191 :         if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
   14711         [ +  - ]:           8 :             ereport(ERROR,
   14712                 :             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14713                 :             :                     errmsg("column \"%s\" is in index used as replica identity",
   14714                 :             :                            get_attname(RelationGetRelid(rel), attnum, false)));
   14715                 :             : 
   14716                 :             :         /* Disallow if it's a GENERATED AS IDENTITY column */
   14717                 :         183 :         atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
   14718         [ -  + ]:         183 :         if (!HeapTupleIsValid(atttup))
   14719         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for attribute %d of relation %u",
   14720                 :             :                  attnum, RelationGetRelid(rel));
   14721                 :         183 :         attForm = (Form_pg_attribute) GETSTRUCT(atttup);
   14722         [ -  + ]:         183 :         if (attForm->attidentity != '\0')
   14723         [ #  # ]:           0 :             ereport(ERROR,
   14724                 :             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   14725                 :             :                     errmsg("column \"%s\" of relation \"%s\" is an identity column",
   14726                 :             :                            get_attname(RelationGetRelid(rel), attnum,
   14727                 :             :                                        false),
   14728                 :             :                            RelationGetRelationName(rel)));
   14729                 :             : 
   14730                 :             :         /* All good -- reset attnotnull if needed */
   14731         [ +  - ]:         183 :         if (attForm->attnotnull)
   14732                 :             :         {
   14733                 :         183 :             attForm->attnotnull = false;
   14734                 :         183 :             CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
   14735                 :             :         }
   14736                 :             : 
   14737                 :         183 :         table_close(attrel, RowExclusiveLock);
   14738                 :             :     }
   14739                 :             : 
   14740                 :         726 :     is_no_inherit_constraint = con->connoinherit;
   14741                 :             : 
   14742                 :             :     /*
   14743                 :             :      * If it's a foreign-key constraint, we'd better lock the referenced table
   14744                 :             :      * and check that that's not in use, just as we've already done for the
   14745                 :             :      * constrained table (else we might, eg, be dropping a trigger that has
   14746                 :             :      * unfired events).  But we can/must skip that in the self-referential
   14747                 :             :      * case.
   14748                 :             :      */
   14749         [ +  + ]:         726 :     if (con->contype == CONSTRAINT_FOREIGN &&
   14750         [ +  - ]:         112 :         con->confrelid != RelationGetRelid(rel))
   14751                 :             :     {
   14752                 :             :         Relation    frel;
   14753                 :             : 
   14754                 :             :         /* Must match lock taken by RemoveTriggerById: */
   14755                 :         112 :         frel = table_open(con->confrelid, AccessExclusiveLock);
   14756                 :         112 :         CheckAlterTableIsSafe(frel);
   14757                 :         108 :         table_close(frel, NoLock);
   14758                 :             :     }
   14759                 :             : 
   14760                 :             :     /*
   14761                 :             :      * Perform the actual constraint deletion
   14762                 :             :      */
   14763                 :         722 :     ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
   14764                 :         722 :     performDeletion(&conobj, behavior, 0);
   14765                 :             : 
   14766                 :             :     /*
   14767                 :             :      * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
   14768                 :             :      * are dropped via the dependency mechanism, so we're done here.
   14769                 :             :      */
   14770         [ +  + ]:         698 :     if (con->contype != CONSTRAINT_CHECK &&
   14771         [ +  + ]:         421 :         con->contype != CONSTRAINT_NOTNULL &&
   14772         [ +  + ]:         238 :         rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   14773                 :             :     {
   14774                 :          52 :         table_close(conrel, RowExclusiveLock);
   14775                 :          52 :         return conobj;
   14776                 :             :     }
   14777                 :             : 
   14778                 :             :     /*
   14779                 :             :      * Propagate to children as appropriate.  Unlike most other ALTER
   14780                 :             :      * routines, we have to do this one level of recursion at a time; we can't
   14781                 :             :      * use find_all_inheritors to do it in one pass.
   14782                 :             :      */
   14783         [ +  + ]:         646 :     if (!is_no_inherit_constraint)
   14784                 :         452 :         children = find_inheritance_children(RelationGetRelid(rel), lockmode);
   14785                 :             :     else
   14786                 :         194 :         children = NIL;
   14787                 :             : 
   14788   [ +  +  +  +  :        1567 :     foreach_oid(childrelid, children)
                   +  + ]
   14789                 :             :     {
   14790                 :             :         Relation    childrel;
   14791                 :             :         HeapTuple   tuple;
   14792                 :             :         Form_pg_constraint childcon;
   14793                 :             : 
   14794                 :             :         /* find_inheritance_children already got lock */
   14795                 :         283 :         childrel = table_open(childrelid, NoLock);
   14796                 :         283 :         CheckAlterTableIsSafe(childrel);
   14797                 :             : 
   14798                 :             :         /*
   14799                 :             :          * We search for not-null constraints by column name, and others by
   14800                 :             :          * constraint name.
   14801                 :             :          */
   14802         [ +  + ]:         283 :         if (con->contype == CONSTRAINT_NOTNULL)
   14803                 :             :         {
   14804                 :          98 :             tuple = findNotNullConstraint(childrelid, colname);
   14805         [ -  + ]:          98 :             if (!HeapTupleIsValid(tuple))
   14806         [ #  # ]:           0 :                 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
   14807                 :             :                      colname, RelationGetRelid(childrel));
   14808                 :             :         }
   14809                 :             :         else
   14810                 :             :         {
   14811                 :             :             SysScanDesc scan;
   14812                 :             :             ScanKeyData skey[3];
   14813                 :             : 
   14814                 :         185 :             ScanKeyInit(&skey[0],
   14815                 :             :                         Anum_pg_constraint_conrelid,
   14816                 :             :                         BTEqualStrategyNumber, F_OIDEQ,
   14817                 :             :                         ObjectIdGetDatum(childrelid));
   14818                 :         185 :             ScanKeyInit(&skey[1],
   14819                 :             :                         Anum_pg_constraint_contypid,
   14820                 :             :                         BTEqualStrategyNumber, F_OIDEQ,
   14821                 :             :                         ObjectIdGetDatum(InvalidOid));
   14822                 :         185 :             ScanKeyInit(&skey[2],
   14823                 :             :                         Anum_pg_constraint_conname,
   14824                 :             :                         BTEqualStrategyNumber, F_NAMEEQ,
   14825                 :             :                         CStringGetDatum(constrName));
   14826                 :         185 :             scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
   14827                 :             :                                       true, NULL, 3, skey);
   14828                 :             :             /* There can only be one, so no need to loop */
   14829                 :         185 :             tuple = systable_getnext(scan);
   14830         [ -  + ]:         185 :             if (!HeapTupleIsValid(tuple))
   14831         [ #  # ]:           0 :                 ereport(ERROR,
   14832                 :             :                         (errcode(ERRCODE_UNDEFINED_OBJECT),
   14833                 :             :                          errmsg("constraint \"%s\" of relation \"%s\" does not exist",
   14834                 :             :                                 constrName,
   14835                 :             :                                 RelationGetRelationName(childrel))));
   14836                 :         185 :             tuple = heap_copytuple(tuple);
   14837                 :         185 :             systable_endscan(scan);
   14838                 :             :         }
   14839                 :             : 
   14840                 :         283 :         childcon = (Form_pg_constraint) GETSTRUCT(tuple);
   14841                 :             : 
   14842                 :             :         /* Right now only CHECK and not-null constraints can be inherited */
   14843         [ +  + ]:         283 :         if (childcon->contype != CONSTRAINT_CHECK &&
   14844         [ -  + ]:          98 :             childcon->contype != CONSTRAINT_NOTNULL)
   14845         [ #  # ]:           0 :             elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
   14846                 :             : 
   14847         [ -  + ]:         283 :         if (childcon->coninhcount <= 0) /* shouldn't happen */
   14848         [ #  # ]:           0 :             elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   14849                 :             :                  childrelid, NameStr(childcon->conname));
   14850                 :             : 
   14851         [ +  + ]:         283 :         if (recurse)
   14852                 :             :         {
   14853                 :             :             /*
   14854                 :             :              * If the child constraint has other definition sources, just
   14855                 :             :              * decrement its inheritance count; if not, recurse to delete it.
   14856                 :             :              */
   14857   [ +  +  +  + ]:         215 :             if (childcon->coninhcount == 1 && !childcon->conislocal)
   14858                 :             :             {
   14859                 :             :                 /* Time to delete this child constraint, too */
   14860                 :         159 :                 dropconstraint_internal(childrel, tuple, behavior,
   14861                 :             :                                         recurse, true, missing_ok,
   14862                 :             :                                         lockmode);
   14863                 :             :             }
   14864                 :             :             else
   14865                 :             :             {
   14866                 :             :                 /* Child constraint must survive my deletion */
   14867                 :          56 :                 childcon->coninhcount--;
   14868                 :          56 :                 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
   14869                 :             : 
   14870                 :             :                 /* Make update visible */
   14871                 :          56 :                 CommandCounterIncrement();
   14872                 :             :             }
   14873                 :             :         }
   14874                 :             :         else
   14875                 :             :         {
   14876                 :             :             /*
   14877                 :             :              * If we were told to drop ONLY in this table (no recursion) and
   14878                 :             :              * there are no further parents for this constraint, we need to
   14879                 :             :              * mark the inheritors' constraints as locally defined rather than
   14880                 :             :              * inherited.
   14881                 :             :              */
   14882                 :          68 :             childcon->coninhcount--;
   14883         [ +  - ]:          68 :             if (childcon->coninhcount == 0)
   14884                 :          68 :                 childcon->conislocal = true;
   14885                 :             : 
   14886                 :          68 :             CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
   14887                 :             : 
   14888                 :             :             /* Make update visible */
   14889                 :          68 :             CommandCounterIncrement();
   14890                 :             :         }
   14891                 :             : 
   14892                 :         279 :         heap_freetuple(tuple);
   14893                 :             : 
   14894                 :         279 :         table_close(childrel, NoLock);
   14895                 :             :     }
   14896                 :             : 
   14897                 :         642 :     table_close(conrel, RowExclusiveLock);
   14898                 :             : 
   14899                 :         642 :     return conobj;
   14900                 :             : }
   14901                 :             : 
   14902                 :             : /*
   14903                 :             :  * ALTER COLUMN TYPE
   14904                 :             :  *
   14905                 :             :  * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
   14906                 :             :  * TYPE during phase 1 --- the AlterTableCmd passed in here is already
   14907                 :             :  * transformed (and must be, because we rely on some transformed fields).
   14908                 :             :  *
   14909                 :             :  * The point of this is that the execution of all ALTER COLUMN TYPEs for a
   14910                 :             :  * table will be done "in parallel" during phase 3, so all the USING
   14911                 :             :  * expressions should be parsed assuming the original column types.  Also,
   14912                 :             :  * this allows a USING expression to refer to a field that will be dropped.
   14913                 :             :  *
   14914                 :             :  * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
   14915                 :             :  * the first two execution steps in phase 2; they must not see the effects
   14916                 :             :  * of any other subcommand types, since the USING expressions are parsed
   14917                 :             :  * against the unmodified table's state.
   14918                 :             :  */
   14919                 :             : static void
   14920                 :         959 : ATPrepAlterColumnType(List **wqueue,
   14921                 :             :                       AlteredTableInfo *tab, Relation rel,
   14922                 :             :                       bool recurse, bool recursing,
   14923                 :             :                       AlterTableCmd *cmd, LOCKMODE lockmode,
   14924                 :             :                       AlterTableUtilityContext *context)
   14925                 :             : {
   14926                 :         959 :     char       *colName = cmd->name;
   14927                 :         959 :     ColumnDef  *def = (ColumnDef *) cmd->def;
   14928                 :         959 :     TypeName   *typeName = def->typeName;
   14929                 :         959 :     Node       *transform = def->cooked_default;
   14930                 :             :     HeapTuple   tuple;
   14931                 :             :     Form_pg_attribute attTup;
   14932                 :             :     AttrNumber  attnum;
   14933                 :             :     Oid         targettype;
   14934                 :             :     int32       targettypmod;
   14935                 :             :     Oid         targetcollid;
   14936                 :             :     NewColumnValue *newval;
   14937                 :         959 :     ParseState *pstate = make_parsestate(NULL);
   14938                 :             :     AclResult   aclresult;
   14939                 :             :     bool        is_expr;
   14940                 :             : 
   14941                 :         959 :     pstate->p_sourcetext = context->queryString;
   14942                 :             : 
   14943   [ +  +  +  + ]:         959 :     if (rel->rd_rel->reloftype && !recursing)
   14944         [ +  - ]:           4 :         ereport(ERROR,
   14945                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   14946                 :             :                  errmsg("cannot alter column type of typed table"),
   14947                 :             :                  parser_errposition(pstate, def->location)));
   14948                 :             : 
   14949                 :             :     /* lookup the attribute so we can check inheritance status */
   14950                 :         955 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   14951         [ -  + ]:         955 :     if (!HeapTupleIsValid(tuple))
   14952         [ #  # ]:           0 :         ereport(ERROR,
   14953                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   14954                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   14955                 :             :                         colName, RelationGetRelationName(rel)),
   14956                 :             :                  parser_errposition(pstate, def->location)));
   14957                 :         955 :     attTup = (Form_pg_attribute) GETSTRUCT(tuple);
   14958                 :         955 :     attnum = attTup->attnum;
   14959                 :             : 
   14960                 :             :     /* Can't alter a system attribute */
   14961         [ +  + ]:         955 :     if (attnum <= 0)
   14962         [ +  - ]:           4 :         ereport(ERROR,
   14963                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   14964                 :             :                  errmsg("cannot alter system column \"%s\"", colName),
   14965                 :             :                  parser_errposition(pstate, def->location)));
   14966                 :             : 
   14967                 :             :     /*
   14968                 :             :      * Cannot specify USING when altering type of a generated column, because
   14969                 :             :      * that would violate the generation expression.
   14970                 :             :      */
   14971   [ +  +  +  + ]:         951 :     if (attTup->attgenerated && def->cooked_default)
   14972         [ +  - ]:           8 :         ereport(ERROR,
   14973                 :             :                 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
   14974                 :             :                  errmsg("cannot specify USING when altering type of generated column"),
   14975                 :             :                  errdetail("Column \"%s\" is a generated column.", colName),
   14976                 :             :                  parser_errposition(pstate, def->location)));
   14977                 :             : 
   14978                 :             :     /*
   14979                 :             :      * Don't alter inherited columns.  At outer level, there had better not be
   14980                 :             :      * any inherited definition; when recursing, we assume this was checked at
   14981                 :             :      * the parent level (see below).
   14982                 :             :      */
   14983   [ +  +  +  + ]:         943 :     if (attTup->attinhcount > 0 && !recursing)
   14984         [ +  - ]:           4 :         ereport(ERROR,
   14985                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14986                 :             :                  errmsg("cannot alter inherited column \"%s\"", colName),
   14987                 :             :                  parser_errposition(pstate, def->location)));
   14988                 :             : 
   14989                 :             :     /* Don't alter columns used in the partition key */
   14990         [ +  + ]:         939 :     if (has_partition_attrs(rel,
   14991                 :             :                             bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
   14992                 :             :                             &is_expr))
   14993         [ +  - ]:          12 :         ereport(ERROR,
   14994                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   14995                 :             :                  errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
   14996                 :             :                         colName, RelationGetRelationName(rel)),
   14997                 :             :                  parser_errposition(pstate, def->location)));
   14998                 :             : 
   14999                 :             :     /* Look up the target type */
   15000                 :         927 :     typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
   15001                 :             : 
   15002                 :         923 :     aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
   15003         [ +  + ]:         923 :     if (aclresult != ACLCHECK_OK)
   15004                 :           8 :         aclcheck_error_type(aclresult, targettype);
   15005                 :             : 
   15006                 :             :     /* And the collation */
   15007                 :         915 :     targetcollid = GetColumnDefCollation(pstate, def, targettype);
   15008                 :             : 
   15009                 :             :     /* make sure datatype is legal for a column */
   15010                 :        1822 :     CheckAttributeType(colName, targettype, targetcollid,
   15011                 :         911 :                        list_make1_oid(rel->rd_rel->reltype),
   15012         [ +  + ]:         911 :                        (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
   15013                 :             : 
   15014         [ +  + ]:         903 :     if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
   15015                 :             :     {
   15016                 :             :         /* do nothing */
   15017                 :             :     }
   15018         [ +  + ]:         879 :     else if (tab->relkind == RELKIND_RELATION ||
   15019         [ +  + ]:         137 :              tab->relkind == RELKIND_PARTITIONED_TABLE)
   15020                 :             :     {
   15021                 :             :         /*
   15022                 :             :          * Set up an expression to transform the old data value to the new
   15023                 :             :          * type. If a USING option was given, use the expression as
   15024                 :             :          * transformed by transformAlterTableStmt, else just take the old
   15025                 :             :          * value and try to coerce it.  We do this first so that type
   15026                 :             :          * incompatibility can be detected before we waste effort, and because
   15027                 :             :          * we need the expression to be parsed against the original table row
   15028                 :             :          * type.
   15029                 :             :          */
   15030         [ +  + ]:         786 :         if (!transform)
   15031                 :             :         {
   15032                 :         635 :             transform = (Node *) makeVar(1, attnum,
   15033                 :             :                                          attTup->atttypid, attTup->atttypmod,
   15034                 :             :                                          attTup->attcollation,
   15035                 :             :                                          0);
   15036                 :             :         }
   15037                 :             : 
   15038                 :         786 :         transform = coerce_to_target_type(pstate,
   15039                 :             :                                           transform, exprType(transform),
   15040                 :             :                                           targettype, targettypmod,
   15041                 :             :                                           COERCION_ASSIGNMENT,
   15042                 :             :                                           COERCE_IMPLICIT_CAST,
   15043                 :             :                                           -1);
   15044         [ +  + ]:         786 :         if (transform == NULL)
   15045                 :             :         {
   15046                 :             :             /* error text depends on whether USING was specified or not */
   15047         [ +  + ]:          15 :             if (def->cooked_default != NULL)
   15048         [ +  - ]:           4 :                 ereport(ERROR,
   15049                 :             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15050                 :             :                          errmsg("result of USING clause for column \"%s\""
   15051                 :             :                                 " cannot be cast automatically to type %s",
   15052                 :             :                                 colName, format_type_be(targettype)),
   15053                 :             :                          errhint("You might need to add an explicit cast.")));
   15054                 :             :             else
   15055   [ +  -  +  - ]:          11 :                 ereport(ERROR,
   15056                 :             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15057                 :             :                          errmsg("column \"%s\" cannot be cast automatically to type %s",
   15058                 :             :                                 colName, format_type_be(targettype)),
   15059                 :             :                          !attTup->attgenerated ?
   15060                 :             :                 /* translator: USING is SQL, don't translate it */
   15061                 :             :                          errhint("You might need to specify \"USING %s::%s\".",
   15062                 :             :                                  quote_identifier(colName),
   15063                 :             :                                  format_type_with_typemod(targettype,
   15064                 :             :                                                           targettypmod)) : 0));
   15065                 :             :         }
   15066                 :             : 
   15067                 :             :         /* Fix collations after all else */
   15068                 :         771 :         assign_expr_collations(pstate, transform);
   15069                 :             : 
   15070                 :             :         /* Expand virtual generated columns in the expr. */
   15071                 :         771 :         transform = expand_generated_columns_in_expr(transform, rel, 1);
   15072                 :             : 
   15073                 :             :         /* Plan the expr now so we can accurately assess the need to rewrite. */
   15074                 :         771 :         transform = (Node *) expression_planner((Expr *) transform);
   15075                 :             : 
   15076                 :             :         /*
   15077                 :             :          * Add a work queue item to make ATRewriteTable update the column
   15078                 :             :          * contents.
   15079                 :             :          */
   15080                 :         771 :         newval = palloc0_object(NewColumnValue);
   15081                 :         771 :         newval->attnum = attnum;
   15082                 :         771 :         newval->expr = (Expr *) transform;
   15083                 :         771 :         newval->is_generated = false;
   15084                 :             : 
   15085                 :         771 :         tab->newvals = lappend(tab->newvals, newval);
   15086         [ +  + ]:        1410 :         if (ATColumnChangeRequiresRewrite(transform, attnum))
   15087                 :         639 :             tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
   15088                 :             :     }
   15089         [ +  + ]:          93 :     else if (transform)
   15090         [ +  - ]:           8 :         ereport(ERROR,
   15091                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   15092                 :             :                  errmsg("\"%s\" is not a table",
   15093                 :             :                         RelationGetRelationName(rel))));
   15094                 :             : 
   15095   [ +  +  +  -  :         880 :     if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
          +  -  +  -  -  
                +  +  + ]
   15096                 :             :     {
   15097                 :             :         /*
   15098                 :             :          * For relations or columns without storage, do this check now.
   15099                 :             :          * Regular tables will check it later when the table is being
   15100                 :             :          * rewritten.
   15101                 :             :          */
   15102                 :         153 :         find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
   15103                 :             :     }
   15104                 :             : 
   15105                 :         848 :     ReleaseSysCache(tuple);
   15106                 :             : 
   15107                 :             :     /*
   15108                 :             :      * Recurse manually by queueing a new command for each child, if
   15109                 :             :      * necessary. We cannot apply ATSimpleRecursion here because we need to
   15110                 :             :      * remap attribute numbers in the USING expression, if any.
   15111                 :             :      *
   15112                 :             :      * If we are told not to recurse, there had better not be any child
   15113                 :             :      * tables; else the alter would put them out of step.
   15114                 :             :      */
   15115         [ +  + ]:         848 :     if (recurse)
   15116                 :             :     {
   15117                 :         673 :         Oid         relid = RelationGetRelid(rel);
   15118                 :             :         List       *child_oids,
   15119                 :             :                    *child_numparents;
   15120                 :             :         ListCell   *lo,
   15121                 :             :                    *li;
   15122                 :             : 
   15123                 :         673 :         child_oids = find_all_inheritors(relid, lockmode,
   15124                 :             :                                          &child_numparents);
   15125                 :             : 
   15126                 :             :         /*
   15127                 :             :          * find_all_inheritors does the recursive search of the inheritance
   15128                 :             :          * hierarchy, so all we have to do is process all of the relids in the
   15129                 :             :          * list that it returns.
   15130                 :             :          */
   15131   [ +  -  +  +  :        1484 :         forboth(lo, child_oids, li, child_numparents)
          +  -  +  +  +  
             +  +  -  +  
                      + ]
   15132                 :             :         {
   15133                 :         827 :             Oid         childrelid = lfirst_oid(lo);
   15134                 :         827 :             int         numparents = lfirst_int(li);
   15135                 :             :             Relation    childrel;
   15136                 :             :             HeapTuple   childtuple;
   15137                 :             :             Form_pg_attribute childattTup;
   15138                 :             : 
   15139         [ +  + ]:         827 :             if (childrelid == relid)
   15140                 :         673 :                 continue;
   15141                 :             : 
   15142                 :             :             /* find_all_inheritors already got lock */
   15143                 :         154 :             childrel = relation_open(childrelid, NoLock);
   15144                 :         154 :             CheckAlterTableIsSafe(childrel);
   15145                 :             : 
   15146                 :             :             /*
   15147                 :             :              * Verify that the child doesn't have any inherited definitions of
   15148                 :             :              * this column that came from outside this inheritance hierarchy.
   15149                 :             :              * (renameatt makes a similar test, though in a different way
   15150                 :             :              * because of its different recursion mechanism.)
   15151                 :             :              */
   15152                 :         154 :             childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
   15153                 :             :                                                colName);
   15154         [ -  + ]:         154 :             if (!HeapTupleIsValid(childtuple))
   15155         [ #  # ]:           0 :                 ereport(ERROR,
   15156                 :             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   15157                 :             :                          errmsg("column \"%s\" of relation \"%s\" does not exist",
   15158                 :             :                                 colName, RelationGetRelationName(childrel))));
   15159                 :         154 :             childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
   15160                 :             : 
   15161         [ +  + ]:         154 :             if (childattTup->attinhcount > numparents)
   15162         [ +  - ]:           4 :                 ereport(ERROR,
   15163                 :             :                         (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   15164                 :             :                          errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
   15165                 :             :                                 colName, RelationGetRelationName(childrel))));
   15166                 :             : 
   15167                 :         150 :             ReleaseSysCache(childtuple);
   15168                 :             : 
   15169                 :             :             /*
   15170                 :             :              * Remap the attribute numbers.  If no USING expression was
   15171                 :             :              * specified, there is no need for this step.
   15172                 :             :              */
   15173         [ +  + ]:         150 :             if (def->cooked_default)
   15174                 :             :             {
   15175                 :             :                 AttrMap    *attmap;
   15176                 :             :                 bool        found_whole_row;
   15177                 :             : 
   15178                 :             :                 /* create a copy to scribble on */
   15179                 :          52 :                 cmd = copyObject(cmd);
   15180                 :             : 
   15181                 :          52 :                 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
   15182                 :             :                                                RelationGetDescr(rel),
   15183                 :             :                                                false);
   15184                 :         104 :                 ((ColumnDef *) cmd->def)->cooked_default =
   15185                 :          52 :                     map_variable_attnos(def->cooked_default,
   15186                 :             :                                         1, 0,
   15187                 :             :                                         attmap,
   15188                 :             :                                         InvalidOid, &found_whole_row);
   15189         [ +  + ]:          52 :                 if (found_whole_row)
   15190         [ +  - ]:           4 :                     ereport(ERROR,
   15191                 :             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15192                 :             :                              errmsg("cannot convert whole-row table reference"),
   15193                 :             :                              errdetail("USING expression contains a whole-row table reference.")));
   15194                 :          48 :                 pfree(attmap);
   15195                 :             :             }
   15196                 :         146 :             ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
   15197                 :         138 :             relation_close(childrel, NoLock);
   15198                 :             :         }
   15199                 :             :     }
   15200   [ +  +  -  + ]:         212 :     else if (!recursing &&
   15201                 :          37 :              find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
   15202         [ #  # ]:           0 :         ereport(ERROR,
   15203                 :             :                 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   15204                 :             :                  errmsg("type of inherited column \"%s\" must be changed in child tables too",
   15205                 :             :                         colName)));
   15206                 :             : 
   15207         [ +  + ]:         832 :     if (tab->relkind == RELKIND_COMPOSITE_TYPE)
   15208                 :          37 :         ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
   15209                 :         828 : }
   15210                 :             : 
   15211                 :             : /*
   15212                 :             :  * When the data type of a column is changed, a rewrite might not be required
   15213                 :             :  * if the new type is sufficiently identical to the old one, and the USING
   15214                 :             :  * clause isn't trying to insert some other value.  It's safe to skip the
   15215                 :             :  * rewrite in these cases:
   15216                 :             :  *
   15217                 :             :  * - the old type is binary coercible to the new type
   15218                 :             :  * - the new type is an unconstrained domain over the old type
   15219                 :             :  * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
   15220                 :             :  *
   15221                 :             :  * In the case of a constrained domain, we could get by with scanning the
   15222                 :             :  * table and checking the constraint rather than actually rewriting it, but we
   15223                 :             :  * don't currently try to do that.
   15224                 :             :  */
   15225                 :             : static bool
   15226                 :         771 : ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
   15227                 :             : {
   15228                 :             :     Assert(expr != NULL);
   15229                 :             : 
   15230                 :             :     for (;;)
   15231                 :             :     {
   15232                 :             :         /* only one varno, so no need to check that */
   15233   [ +  +  +  - ]:         847 :         if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
   15234                 :         132 :             return false;
   15235         [ +  + ]:         715 :         else if (IsA(expr, RelabelType))
   15236                 :          68 :             expr = (Node *) ((RelabelType *) expr)->arg;
   15237         [ +  + ]:         647 :         else if (IsA(expr, CoerceToDomain))
   15238                 :             :         {
   15239                 :           4 :             CoerceToDomain *d = (CoerceToDomain *) expr;
   15240                 :             : 
   15241         [ +  - ]:           4 :             if (DomainHasConstraints(d->resulttype, NULL))
   15242                 :           4 :                 return true;
   15243                 :           0 :             expr = (Node *) d->arg;
   15244                 :             :         }
   15245         [ +  + ]:         643 :         else if (IsA(expr, FuncExpr))
   15246                 :             :         {
   15247                 :         510 :             FuncExpr   *f = (FuncExpr *) expr;
   15248                 :             : 
   15249         [ +  + ]:         510 :             switch (f->funcid)
   15250                 :             :             {
   15251                 :          12 :                 case F_TIMESTAMPTZ_TIMESTAMP:
   15252                 :             :                 case F_TIMESTAMP_TIMESTAMPTZ:
   15253         [ +  + ]:          12 :                     if (TimestampTimestampTzRequiresRewrite())
   15254                 :           4 :                         return true;
   15255                 :             :                     else
   15256                 :           8 :                         expr = linitial(f->args);
   15257                 :           8 :                     break;
   15258                 :         498 :                 default:
   15259                 :         498 :                     return true;
   15260                 :             :             }
   15261                 :             :         }
   15262                 :             :         else
   15263                 :         133 :             return true;
   15264                 :             :     }
   15265                 :             : }
   15266                 :             : 
   15267                 :             : /*
   15268                 :             :  * ALTER COLUMN .. SET DATA TYPE
   15269                 :             :  *
   15270                 :             :  * Return the address of the modified column.
   15271                 :             :  */
   15272                 :             : static ObjectAddress
   15273                 :         804 : ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
   15274                 :             :                       AlterTableCmd *cmd, LOCKMODE lockmode)
   15275                 :             : {
   15276                 :         804 :     char       *colName = cmd->name;
   15277                 :         804 :     ColumnDef  *def = (ColumnDef *) cmd->def;
   15278                 :         804 :     TypeName   *typeName = def->typeName;
   15279                 :             :     HeapTuple   heapTup;
   15280                 :             :     Form_pg_attribute attTup,
   15281                 :             :                 attOldTup;
   15282                 :             :     AttrNumber  attnum;
   15283                 :             :     HeapTuple   typeTuple;
   15284                 :             :     Form_pg_type tform;
   15285                 :             :     Oid         targettype;
   15286                 :             :     int32       targettypmod;
   15287                 :             :     Oid         targetcollid;
   15288                 :             :     Node       *defaultexpr;
   15289                 :             :     Relation    attrelation;
   15290                 :             :     Relation    depRel;
   15291                 :             :     ScanKeyData key[3];
   15292                 :             :     SysScanDesc scan;
   15293                 :             :     HeapTuple   depTup;
   15294                 :             :     ObjectAddress address;
   15295                 :             : 
   15296                 :             :     /*
   15297                 :             :      * Clear all the missing values if we're rewriting the table, since this
   15298                 :             :      * renders them pointless.
   15299                 :             :      */
   15300         [ +  + ]:         804 :     if (tab->rewrite)
   15301                 :             :     {
   15302                 :             :         Relation    newrel;
   15303                 :             : 
   15304                 :         599 :         newrel = table_open(RelationGetRelid(rel), NoLock);
   15305                 :         599 :         RelationClearMissing(newrel);
   15306                 :         599 :         relation_close(newrel, NoLock);
   15307                 :             :         /* make sure we don't conflict with later attribute modifications */
   15308                 :         599 :         CommandCounterIncrement();
   15309                 :             :     }
   15310                 :             : 
   15311                 :         804 :     attrelation = table_open(AttributeRelationId, RowExclusiveLock);
   15312                 :             : 
   15313                 :             :     /* Look up the target column */
   15314                 :         804 :     heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
   15315         [ -  + ]:         804 :     if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
   15316         [ #  # ]:           0 :         ereport(ERROR,
   15317                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   15318                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   15319                 :             :                         colName, RelationGetRelationName(rel))));
   15320                 :         804 :     attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   15321                 :         804 :     attnum = attTup->attnum;
   15322                 :         804 :     attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
   15323                 :             : 
   15324                 :             :     /* Check for multiple ALTER TYPE on same column --- can't cope */
   15325         [ +  - ]:         804 :     if (attTup->atttypid != attOldTup->atttypid ||
   15326         [ -  + ]:         804 :         attTup->atttypmod != attOldTup->atttypmod)
   15327         [ #  # ]:           0 :         ereport(ERROR,
   15328                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15329                 :             :                  errmsg("cannot alter type of column \"%s\" twice",
   15330                 :             :                         colName)));
   15331                 :             : 
   15332                 :             :     /* Look up the target type (should not fail, since prep found it) */
   15333                 :         804 :     typeTuple = typenameType(NULL, typeName, &targettypmod);
   15334                 :         804 :     tform = (Form_pg_type) GETSTRUCT(typeTuple);
   15335                 :         804 :     targettype = tform->oid;
   15336                 :             :     /* And the collation */
   15337                 :         804 :     targetcollid = GetColumnDefCollation(NULL, def, targettype);
   15338                 :             : 
   15339                 :             :     /*
   15340                 :             :      * If there is a default expression for the column, get it and ensure we
   15341                 :             :      * can coerce it to the new datatype.  (We must do this before changing
   15342                 :             :      * the column type, because build_column_default itself will try to
   15343                 :             :      * coerce, and will not issue the error message we want if it fails.)
   15344                 :             :      *
   15345                 :             :      * We remove any implicit coercion steps at the top level of the old
   15346                 :             :      * default expression; this has been agreed to satisfy the principle of
   15347                 :             :      * least surprise.  (The conversion to the new column type should act like
   15348                 :             :      * it started from what the user sees as the stored expression, and the
   15349                 :             :      * implicit coercions aren't going to be shown.)
   15350                 :             :      */
   15351         [ +  + ]:         804 :     if (attTup->atthasdef)
   15352                 :             :     {
   15353                 :          68 :         defaultexpr = build_column_default(rel, attnum);
   15354                 :             :         Assert(defaultexpr);
   15355                 :          68 :         defaultexpr = strip_implicit_coercions(defaultexpr);
   15356                 :          68 :         defaultexpr = coerce_to_target_type(NULL,   /* no UNKNOWN params */
   15357                 :             :                                             defaultexpr, exprType(defaultexpr),
   15358                 :             :                                             targettype, targettypmod,
   15359                 :             :                                             COERCION_ASSIGNMENT,
   15360                 :             :                                             COERCE_IMPLICIT_CAST,
   15361                 :             :                                             -1);
   15362         [ +  + ]:          68 :         if (defaultexpr == NULL)
   15363                 :             :         {
   15364         [ -  + ]:           4 :             if (attTup->attgenerated)
   15365         [ #  # ]:           0 :                 ereport(ERROR,
   15366                 :             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15367                 :             :                          errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
   15368                 :             :                                 colName, format_type_be(targettype))));
   15369                 :             :             else
   15370         [ +  - ]:           4 :                 ereport(ERROR,
   15371                 :             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   15372                 :             :                          errmsg("default for column \"%s\" cannot be cast automatically to type %s",
   15373                 :             :                                 colName, format_type_be(targettype))));
   15374                 :             :         }
   15375                 :             :     }
   15376                 :             :     else
   15377                 :         736 :         defaultexpr = NULL;
   15378                 :             : 
   15379                 :             :     /*
   15380                 :             :      * Find everything that depends on the column (constraints, indexes, etc),
   15381                 :             :      * and record enough information to let us recreate the objects.
   15382                 :             :      *
   15383                 :             :      * The actual recreation does not happen here, but only after we have
   15384                 :             :      * performed all the individual ALTER TYPE operations.  We have to save
   15385                 :             :      * the info before executing ALTER TYPE, though, else the deparser will
   15386                 :             :      * get confused.
   15387                 :             :      */
   15388                 :         800 :     RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
   15389                 :             : 
   15390                 :             :     /*
   15391                 :             :      * Now scan for dependencies of this column on other things.  The only
   15392                 :             :      * things we should find are the dependency on the column datatype and
   15393                 :             :      * possibly a collation dependency.  Those can be removed.
   15394                 :             :      */
   15395                 :         776 :     depRel = table_open(DependRelationId, RowExclusiveLock);
   15396                 :             : 
   15397                 :         776 :     ScanKeyInit(&key[0],
   15398                 :             :                 Anum_pg_depend_classid,
   15399                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   15400                 :             :                 ObjectIdGetDatum(RelationRelationId));
   15401                 :         776 :     ScanKeyInit(&key[1],
   15402                 :             :                 Anum_pg_depend_objid,
   15403                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   15404                 :             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   15405                 :         776 :     ScanKeyInit(&key[2],
   15406                 :             :                 Anum_pg_depend_objsubid,
   15407                 :             :                 BTEqualStrategyNumber, F_INT4EQ,
   15408                 :             :                 Int32GetDatum((int32) attnum));
   15409                 :             : 
   15410                 :         776 :     scan = systable_beginscan(depRel, DependDependerIndexId, true,
   15411                 :             :                               NULL, 3, key);
   15412                 :             : 
   15413         [ +  + ]:         778 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   15414                 :             :     {
   15415                 :           2 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   15416                 :             :         ObjectAddress foundObject;
   15417                 :             : 
   15418                 :           2 :         foundObject.classId = foundDep->refclassid;
   15419                 :           2 :         foundObject.objectId = foundDep->refobjid;
   15420                 :           2 :         foundObject.objectSubId = foundDep->refobjsubid;
   15421                 :             : 
   15422         [ -  + ]:           2 :         if (foundDep->deptype != DEPENDENCY_NORMAL)
   15423         [ #  # ]:           0 :             elog(ERROR, "found unexpected dependency type '%c'",
   15424                 :             :                  foundDep->deptype);
   15425         [ +  - ]:           2 :         if (!(foundDep->refclassid == TypeRelationId &&
   15426         [ -  + ]:           2 :               foundDep->refobjid == attTup->atttypid) &&
   15427         [ #  # ]:           0 :             !(foundDep->refclassid == CollationRelationId &&
   15428         [ #  # ]:           0 :               foundDep->refobjid == attTup->attcollation))
   15429         [ #  # ]:           0 :             elog(ERROR, "found unexpected dependency for column: %s",
   15430                 :             :                  getObjectDescription(&foundObject, false));
   15431                 :             : 
   15432                 :           2 :         CatalogTupleDelete(depRel, &depTup->t_self);
   15433                 :             :     }
   15434                 :             : 
   15435                 :         776 :     systable_endscan(scan);
   15436                 :             : 
   15437                 :         776 :     table_close(depRel, RowExclusiveLock);
   15438                 :             : 
   15439                 :             :     /*
   15440                 :             :      * Here we go --- change the recorded column type and collation.  (Note
   15441                 :             :      * heapTup is a copy of the syscache entry, so okay to scribble on.) First
   15442                 :             :      * fix up the missing value if any.
   15443                 :             :      */
   15444         [ +  + ]:         776 :     if (attTup->atthasmissing)
   15445                 :             :     {
   15446                 :             :         Datum       missingval;
   15447                 :             :         bool        missingNull;
   15448                 :             : 
   15449                 :             :         /* if rewrite is true the missing value should already be cleared */
   15450                 :             :         Assert(tab->rewrite == 0);
   15451                 :             : 
   15452                 :             :         /* Get the missing value datum */
   15453                 :           4 :         missingval = heap_getattr(heapTup,
   15454                 :             :                                   Anum_pg_attribute_attmissingval,
   15455                 :             :                                   attrelation->rd_att,
   15456                 :             :                                   &missingNull);
   15457                 :             : 
   15458                 :             :         /* if it's a null array there is nothing to do */
   15459                 :             : 
   15460         [ +  - ]:           4 :         if (!missingNull)
   15461                 :             :         {
   15462                 :             :             /*
   15463                 :             :              * Get the datum out of the array and repack it in a new array
   15464                 :             :              * built with the new type data. We assume that since the table
   15465                 :             :              * doesn't need rewriting, the actual Datum doesn't need to be
   15466                 :             :              * changed, only the array metadata.
   15467                 :             :              */
   15468                 :             : 
   15469                 :           4 :             int         one = 1;
   15470                 :             :             bool        isNull;
   15471                 :           4 :             Datum       valuesAtt[Natts_pg_attribute] = {0};
   15472                 :           4 :             bool        nullsAtt[Natts_pg_attribute] = {0};
   15473                 :           4 :             bool        replacesAtt[Natts_pg_attribute] = {0};
   15474                 :             :             HeapTuple   newTup;
   15475                 :             : 
   15476                 :           8 :             missingval = array_get_element(missingval,
   15477                 :             :                                            1,
   15478                 :             :                                            &one,
   15479                 :             :                                            0,
   15480                 :           4 :                                            attTup->attlen,
   15481                 :           4 :                                            attTup->attbyval,
   15482                 :           4 :                                            attTup->attalign,
   15483                 :             :                                            &isNull);
   15484                 :           4 :             missingval = PointerGetDatum(construct_array(&missingval,
   15485                 :             :                                                          1,
   15486                 :             :                                                          targettype,
   15487                 :             :                                                          tform->typlen,
   15488                 :             :                                                          tform->typbyval,
   15489                 :             :                                                          tform->typalign));
   15490                 :             : 
   15491                 :           4 :             valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
   15492                 :           4 :             replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
   15493                 :           4 :             nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
   15494                 :             : 
   15495                 :           4 :             newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
   15496                 :             :                                        valuesAtt, nullsAtt, replacesAtt);
   15497                 :           4 :             heap_freetuple(heapTup);
   15498                 :           4 :             heapTup = newTup;
   15499                 :           4 :             attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
   15500                 :             :         }
   15501                 :             :     }
   15502                 :             : 
   15503                 :         776 :     attTup->atttypid = targettype;
   15504                 :         776 :     attTup->atttypmod = targettypmod;
   15505                 :         776 :     attTup->attcollation = targetcollid;
   15506         [ -  + ]:         776 :     if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
   15507         [ #  # ]:           0 :         ereport(ERROR,
   15508                 :             :                 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   15509                 :             :                 errmsg("too many array dimensions"));
   15510                 :         776 :     attTup->attndims = list_length(typeName->arrayBounds);
   15511                 :         776 :     attTup->attlen = tform->typlen;
   15512                 :         776 :     attTup->attbyval = tform->typbyval;
   15513                 :         776 :     attTup->attalign = tform->typalign;
   15514                 :         776 :     attTup->attstorage = tform->typstorage;
   15515                 :         776 :     attTup->attcompression = InvalidCompressionMethod;
   15516                 :             : 
   15517                 :         776 :     ReleaseSysCache(typeTuple);
   15518                 :             : 
   15519                 :         776 :     CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
   15520                 :             : 
   15521                 :         776 :     table_close(attrelation, RowExclusiveLock);
   15522                 :             : 
   15523                 :             :     /* Install dependencies on new datatype and collation */
   15524                 :         776 :     add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
   15525                 :         776 :     add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
   15526                 :             : 
   15527                 :             :     /*
   15528                 :             :      * Drop any pg_statistic entry for the column, since it's now wrong type
   15529                 :             :      */
   15530                 :         776 :     RemoveStatistics(RelationGetRelid(rel), attnum);
   15531                 :             : 
   15532         [ -  + ]:         776 :     InvokeObjectPostAlterHook(RelationRelationId,
   15533                 :             :                               RelationGetRelid(rel), attnum);
   15534                 :             : 
   15535                 :             :     /*
   15536                 :             :      * Update the default, if present, by brute force --- remove and re-add
   15537                 :             :      * the default.  Probably unsafe to take shortcuts, since the new version
   15538                 :             :      * may well have additional dependencies.  (It's okay to do this now,
   15539                 :             :      * rather than after other ALTER TYPE commands, since the default won't
   15540                 :             :      * depend on other column types.)
   15541                 :             :      */
   15542         [ +  + ]:         776 :     if (defaultexpr)
   15543                 :             :     {
   15544                 :             :         /*
   15545                 :             :          * If it's a GENERATED default, drop its dependency records, in
   15546                 :             :          * particular its INTERNAL dependency on the column, which would
   15547                 :             :          * otherwise cause dependency.c to refuse to perform the deletion.
   15548                 :             :          */
   15549         [ +  + ]:          64 :         if (attTup->attgenerated)
   15550                 :             :         {
   15551                 :          28 :             Oid         attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
   15552                 :             : 
   15553         [ -  + ]:          28 :             if (!OidIsValid(attrdefoid))
   15554         [ #  # ]:           0 :                 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
   15555                 :             :                      RelationGetRelid(rel), attnum);
   15556                 :          28 :             (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
   15557                 :             :         }
   15558                 :             : 
   15559                 :             :         /*
   15560                 :             :          * Make updates-so-far visible, particularly the new pg_attribute row
   15561                 :             :          * which will be updated again.
   15562                 :             :          */
   15563                 :          64 :         CommandCounterIncrement();
   15564                 :             : 
   15565                 :             :         /*
   15566                 :             :          * We use RESTRICT here for safety, but at present we do not expect
   15567                 :             :          * anything to depend on the default.
   15568                 :             :          */
   15569                 :          64 :         RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
   15570                 :             :                           true);
   15571                 :             : 
   15572                 :          64 :         (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
   15573                 :             :     }
   15574                 :             : 
   15575                 :         776 :     ObjectAddressSubSet(address, RelationRelationId,
   15576                 :             :                         RelationGetRelid(rel), attnum);
   15577                 :             : 
   15578                 :             :     /* Cleanup */
   15579                 :         776 :     heap_freetuple(heapTup);
   15580                 :             : 
   15581                 :         776 :     return address;
   15582                 :             : }
   15583                 :             : 
   15584                 :             : /*
   15585                 :             :  * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
   15586                 :             :  * that depends on the column (constraints, indexes, etc), and record enough
   15587                 :             :  * information to let us recreate the objects.
   15588                 :             :  */
   15589                 :             : static void
   15590                 :         981 : RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
   15591                 :             :                                   Relation rel, AttrNumber attnum, const char *colName)
   15592                 :             : {
   15593                 :             :     Relation    depRel;
   15594                 :             :     ScanKeyData key[3];
   15595                 :             :     SysScanDesc scan;
   15596                 :             :     HeapTuple   depTup;
   15597                 :             : 
   15598                 :             :     Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
   15599                 :             : 
   15600                 :         981 :     depRel = table_open(DependRelationId, RowExclusiveLock);
   15601                 :             : 
   15602                 :         981 :     ScanKeyInit(&key[0],
   15603                 :             :                 Anum_pg_depend_refclassid,
   15604                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   15605                 :             :                 ObjectIdGetDatum(RelationRelationId));
   15606                 :         981 :     ScanKeyInit(&key[1],
   15607                 :             :                 Anum_pg_depend_refobjid,
   15608                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   15609                 :             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   15610                 :         981 :     ScanKeyInit(&key[2],
   15611                 :             :                 Anum_pg_depend_refobjsubid,
   15612                 :             :                 BTEqualStrategyNumber, F_INT4EQ,
   15613                 :             :                 Int32GetDatum((int32) attnum));
   15614                 :             : 
   15615                 :         981 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   15616                 :             :                               NULL, 3, key);
   15617                 :             : 
   15618         [ +  + ]:        2003 :     while (HeapTupleIsValid(depTup = systable_getnext(scan)))
   15619                 :             :     {
   15620                 :        1046 :         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
   15621                 :             :         ObjectAddress foundObject;
   15622                 :             : 
   15623                 :        1046 :         foundObject.classId = foundDep->classid;
   15624                 :        1046 :         foundObject.objectId = foundDep->objid;
   15625                 :        1046 :         foundObject.objectSubId = foundDep->objsubid;
   15626                 :             : 
   15627   [ +  +  -  +  :        1046 :         switch (foundObject.classId)
          -  -  +  +  -  
                      - ]
   15628                 :             :         {
   15629                 :         192 :             case RelationRelationId:
   15630                 :             :                 {
   15631                 :         192 :                     char        relKind = get_rel_relkind(foundObject.objectId);
   15632                 :             : 
   15633   [ +  +  +  + ]:         192 :                     if (relKind == RELKIND_INDEX ||
   15634                 :             :                         relKind == RELKIND_PARTITIONED_INDEX)
   15635                 :             :                     {
   15636                 :             :                         Assert(foundObject.objectSubId == 0);
   15637                 :         167 :                         RememberIndexForRebuilding(foundObject.objectId, tab);
   15638                 :             :                     }
   15639         [ -  + ]:          25 :                     else if (relKind == RELKIND_SEQUENCE)
   15640                 :             :                     {
   15641                 :             :                         /*
   15642                 :             :                          * This must be a SERIAL column's sequence.  We need
   15643                 :             :                          * not do anything to it.
   15644                 :             :                          */
   15645                 :             :                         Assert(foundObject.objectSubId == 0);
   15646                 :             :                     }
   15647                 :             :                     else
   15648                 :             :                     {
   15649                 :             :                         /* Not expecting any other direct dependencies... */
   15650         [ #  # ]:           0 :                         elog(ERROR, "unexpected object depending on column: %s",
   15651                 :             :                              getObjectDescription(&foundObject, false));
   15652                 :             :                     }
   15653                 :         192 :                     break;
   15654                 :             :                 }
   15655                 :             : 
   15656                 :         528 :             case ConstraintRelationId:
   15657                 :             :                 Assert(foundObject.objectSubId == 0);
   15658                 :         528 :                 RememberConstraintForRebuilding(foundObject.objectId, tab);
   15659                 :         528 :                 break;
   15660                 :             : 
   15661                 :           0 :             case ProcedureRelationId:
   15662                 :             : 
   15663                 :             :                 /*
   15664                 :             :                  * A new-style SQL function can depend on a column, if that
   15665                 :             :                  * column is referenced in the parsed function body.  Ideally
   15666                 :             :                  * we'd automatically update the function by deparsing and
   15667                 :             :                  * reparsing it, but that's risky and might well fail anyhow.
   15668                 :             :                  * FIXME someday.
   15669                 :             :                  *
   15670                 :             :                  * This is only a problem for AT_AlterColumnType, not
   15671                 :             :                  * AT_SetExpression.
   15672                 :             :                  */
   15673         [ #  # ]:           0 :                 if (subtype == AT_AlterColumnType)
   15674         [ #  # ]:           0 :                     ereport(ERROR,
   15675                 :             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15676                 :             :                              errmsg("cannot alter type of a column used by a function or procedure"),
   15677                 :             :                              errdetail("%s depends on column \"%s\".",
   15678                 :             :                                        getObjectDescription(&foundObject, false),
   15679                 :             :                                        colName)));
   15680                 :           0 :                 break;
   15681                 :             : 
   15682                 :           8 :             case RewriteRelationId:
   15683                 :             : 
   15684                 :             :                 /*
   15685                 :             :                  * View/rule bodies have pretty much the same issues as
   15686                 :             :                  * function bodies.  FIXME someday.
   15687                 :             :                  */
   15688         [ +  - ]:           8 :                 if (subtype == AT_AlterColumnType)
   15689         [ +  - ]:           8 :                     ereport(ERROR,
   15690                 :             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15691                 :             :                              errmsg("cannot alter type of a column used by a view or rule"),
   15692                 :             :                              errdetail("%s depends on column \"%s\".",
   15693                 :             :                                        getObjectDescription(&foundObject, false),
   15694                 :             :                                        colName)));
   15695                 :           0 :                 break;
   15696                 :             : 
   15697                 :           0 :             case TriggerRelationId:
   15698                 :             : 
   15699                 :             :                 /*
   15700                 :             :                  * A trigger can depend on a column because the column is
   15701                 :             :                  * specified as an update target, or because the column is
   15702                 :             :                  * used in the trigger's WHEN condition.  The first case would
   15703                 :             :                  * not require any extra work, but the second case would
   15704                 :             :                  * require updating the WHEN expression, which has the same
   15705                 :             :                  * issues as above.  Since we can't easily tell which case
   15706                 :             :                  * applies, we punt for both.  FIXME someday.
   15707                 :             :                  */
   15708         [ #  # ]:           0 :                 if (subtype == AT_AlterColumnType)
   15709         [ #  # ]:           0 :                     ereport(ERROR,
   15710                 :             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15711                 :             :                              errmsg("cannot alter type of a column used in a trigger definition"),
   15712                 :             :                              errdetail("%s depends on column \"%s\".",
   15713                 :             :                                        getObjectDescription(&foundObject, false),
   15714                 :             :                                        colName)));
   15715                 :           0 :                 break;
   15716                 :             : 
   15717                 :           0 :             case PolicyRelationId:
   15718                 :             : 
   15719                 :             :                 /*
   15720                 :             :                  * A policy can depend on a column because the column is
   15721                 :             :                  * specified in the policy's USING or WITH CHECK qual
   15722                 :             :                  * expressions.  It might be possible to rewrite and recheck
   15723                 :             :                  * the policy expression, but punt for now.  It's certainly
   15724                 :             :                  * easy enough to remove and recreate the policy; still, FIXME
   15725                 :             :                  * someday.
   15726                 :             :                  */
   15727         [ #  # ]:           0 :                 if (subtype == AT_AlterColumnType)
   15728         [ #  # ]:           0 :                     ereport(ERROR,
   15729                 :             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15730                 :             :                              errmsg("cannot alter type of a column used in a policy definition"),
   15731                 :             :                              errdetail("%s depends on column \"%s\".",
   15732                 :             :                                        getObjectDescription(&foundObject, false),
   15733                 :             :                                        colName)));
   15734                 :           0 :                 break;
   15735                 :             : 
   15736                 :         261 :             case AttrDefaultRelationId:
   15737                 :             :                 {
   15738                 :         261 :                     ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
   15739                 :             : 
   15740         [ +  - ]:         506 :                     if (col.objectId == RelationGetRelid(rel) &&
   15741         [ +  + ]:         261 :                         col.objectSubId == attnum)
   15742                 :             :                     {
   15743                 :             :                         /*
   15744                 :             :                          * Ignore the column's own default expression.  The
   15745                 :             :                          * caller deals with it.
   15746                 :             :                          */
   15747                 :             :                     }
   15748                 :             :                     else
   15749                 :             :                     {
   15750                 :             :                         /*
   15751                 :             :                          * This must be a reference from the expression of a
   15752                 :             :                          * generated column elsewhere in the same table.
   15753                 :             :                          * Changing the type/generated expression of a column
   15754                 :             :                          * that is used by a generated column is not allowed
   15755                 :             :                          * by SQL standard, so just punt for now.  It might be
   15756                 :             :                          * doable with some thinking and effort.
   15757                 :             :                          */
   15758         [ +  - ]:          16 :                         if (subtype == AT_AlterColumnType)
   15759         [ +  - ]:          16 :                             ereport(ERROR,
   15760                 :             :                                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15761                 :             :                                      errmsg("cannot alter type of a column used by a generated column"),
   15762                 :             :                                      errdetail("Column \"%s\" is used by generated column \"%s\".",
   15763                 :             :                                                colName,
   15764                 :             :                                                get_attname(col.objectId,
   15765                 :             :                                                            col.objectSubId,
   15766                 :             :                                                            false))));
   15767                 :             :                     }
   15768                 :         245 :                     break;
   15769                 :             :                 }
   15770                 :             : 
   15771                 :          57 :             case StatisticExtRelationId:
   15772                 :             : 
   15773                 :             :                 /*
   15774                 :             :                  * Give the extended-stats machinery a chance to fix anything
   15775                 :             :                  * that this column type change would break.
   15776                 :             :                  */
   15777                 :          57 :                 RememberStatisticsForRebuilding(foundObject.objectId, tab);
   15778                 :          57 :                 break;
   15779                 :             : 
   15780                 :           0 :             case PublicationRelRelationId:
   15781                 :             : 
   15782                 :             :                 /*
   15783                 :             :                  * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
   15784                 :             :                  * clause.  Same issues as above.  FIXME someday.
   15785                 :             :                  */
   15786         [ #  # ]:           0 :                 if (subtype == AT_AlterColumnType)
   15787         [ #  # ]:           0 :                     ereport(ERROR,
   15788                 :             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   15789                 :             :                              errmsg("cannot alter type of a column used by a publication WHERE clause"),
   15790                 :             :                              errdetail("%s depends on column \"%s\".",
   15791                 :             :                                        getObjectDescription(&foundObject, false),
   15792                 :             :                                        colName)));
   15793                 :           0 :                 break;
   15794                 :             : 
   15795                 :           0 :             default:
   15796                 :             : 
   15797                 :             :                 /*
   15798                 :             :                  * We don't expect any other sorts of objects to depend on a
   15799                 :             :                  * column.
   15800                 :             :                  */
   15801         [ #  # ]:           0 :                 elog(ERROR, "unexpected object depending on column: %s",
   15802                 :             :                      getObjectDescription(&foundObject, false));
   15803                 :             :                 break;
   15804                 :             :         }
   15805                 :             :     }
   15806                 :             : 
   15807                 :         957 :     systable_endscan(scan);
   15808                 :         957 :     table_close(depRel, NoLock);
   15809                 :         957 : }
   15810                 :             : 
   15811                 :             : /*
   15812                 :             :  * Record information about dependencies between objects with whole-row Var
   15813                 :             :  * references (indexes, check constraints, etc.) and the relation.
   15814                 :             :  *
   15815                 :             :  * See also RememberAllDependentForRebuilding, which handles non-whole-row Var
   15816                 :             :  * references.
   15817                 :             :  *
   15818                 :             :  * This function currently applies only to ALTER COLUMN SET EXPRESSION.
   15819                 :             :  */
   15820                 :             : static void
   15821                 :         181 : RememberWholeRowDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel)
   15822                 :             : {
   15823                 :             :     ScanKeyData skey;
   15824                 :             :     Relation    pg_constraint;
   15825                 :             :     Relation    pg_index;
   15826                 :             :     SysScanDesc conscan;
   15827                 :             :     SysScanDesc indscan;
   15828                 :             :     HeapTuple   constrTuple;
   15829                 :             :     HeapTuple   indexTuple;
   15830                 :             :     bool        isnull;
   15831                 :             : 
   15832                 :             :     Assert(subtype == AT_SetExpression);
   15833                 :             : 
   15834                 :             :     /*
   15835                 :             :      * Check CHECK constraints with whole-row references first.
   15836                 :             :      */
   15837         [ +  - ]:         181 :     if (RelationGetDescr(rel)->constr &&
   15838         [ +  + ]:         181 :         RelationGetDescr(rel)->constr->num_check > 0)
   15839                 :             :     {
   15840                 :          28 :         pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   15841                 :             : 
   15842                 :          28 :         ScanKeyInit(&skey,
   15843                 :             :                     Anum_pg_constraint_conrelid,
   15844                 :             :                     BTEqualStrategyNumber, F_OIDEQ,
   15845                 :             :                     ObjectIdGetDatum(RelationGetRelid(rel)));
   15846                 :             : 
   15847                 :          28 :         conscan = systable_beginscan(pg_constraint,
   15848                 :             :                                      ConstraintRelidTypidNameIndexId,
   15849                 :             :                                      true,
   15850                 :             :                                      NULL,
   15851                 :             :                                      1,
   15852                 :             :                                      &skey);
   15853                 :             : 
   15854         [ +  + ]:          96 :         while (HeapTupleIsValid(constrTuple = systable_getnext(conscan)))
   15855                 :             :         {
   15856                 :          68 :             Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(constrTuple);
   15857                 :             :             Datum       exprDatum;
   15858                 :             : 
   15859         [ +  + ]:          68 :             if (conform->contype != CONSTRAINT_CHECK)
   15860                 :          40 :                 continue;
   15861                 :             : 
   15862                 :          28 :             exprDatum = heap_getattr(constrTuple,
   15863                 :             :                                      Anum_pg_constraint_conbin,
   15864                 :             :                                      RelationGetDescr(pg_constraint),
   15865                 :             :                                      &isnull);
   15866         [ -  + ]:          28 :             if (isnull)
   15867         [ #  # ]:           0 :                 elog(ERROR, "null conbin for relation \"%s\"",
   15868                 :             :                      RelationGetRelationName(rel));
   15869                 :             :             else
   15870                 :             :             {
   15871                 :             :                 char       *exprString;
   15872                 :             :                 Node       *expr;
   15873                 :          28 :                 Bitmapset  *expr_attrs = NULL;
   15874                 :             : 
   15875                 :          28 :                 exprString = TextDatumGetCString(exprDatum);
   15876                 :          28 :                 expr = stringToNode(exprString);
   15877                 :          28 :                 pfree(exprString);
   15878                 :             : 
   15879                 :             :                 /* Find all attributes referenced */
   15880                 :          28 :                 pull_varattnos(expr, 1, &expr_attrs);
   15881                 :             : 
   15882                 :             :                 /*
   15883                 :             :                  * If the CHECK constraint contains whole-row reference then
   15884                 :             :                  * remember it.
   15885                 :             :                  */
   15886         [ +  + ]:          28 :                 if (bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber, expr_attrs))
   15887                 :             :                 {
   15888                 :           8 :                     RememberConstraintForRebuilding(conform->oid, tab);
   15889                 :             :                 }
   15890                 :             :             }
   15891                 :             :         }
   15892                 :          28 :         systable_endscan(conscan);
   15893                 :          28 :         table_close(pg_constraint, AccessShareLock);
   15894                 :             :     }
   15895                 :             : 
   15896                 :             :     /*
   15897                 :             :      * Now check indexes with whole-row references. Prepare to scan pg_index
   15898                 :             :      * for entries having indrelid matching this relation.
   15899                 :             :      */
   15900                 :         181 :     ScanKeyInit(&skey,
   15901                 :             :                 Anum_pg_index_indrelid,
   15902                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   15903                 :             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   15904                 :             : 
   15905                 :         181 :     pg_index = table_open(IndexRelationId, AccessShareLock);
   15906                 :             : 
   15907                 :         181 :     indscan = systable_beginscan(pg_index,
   15908                 :             :                                  IndexIndrelidIndexId,
   15909                 :             :                                  true,
   15910                 :             :                                  NULL,
   15911                 :             :                                  1,
   15912                 :             :                                  &skey);
   15913                 :             : 
   15914         [ +  + ]:         261 :     while (HeapTupleIsValid(indexTuple = systable_getnext(indscan)))
   15915                 :             :     {
   15916                 :          80 :         Form_pg_index index = (Form_pg_index) GETSTRUCT(indexTuple);
   15917                 :             :         Datum       exprDatum;
   15918                 :             : 
   15919                 :          80 :         exprDatum = heap_getattr(indexTuple,
   15920                 :             :                                  Anum_pg_index_indexprs,
   15921                 :             :                                  RelationGetDescr(pg_index),
   15922                 :             :                                  &isnull);
   15923         [ +  + ]:          80 :         if (!isnull)
   15924                 :             :         {
   15925                 :             :             char       *exprString;
   15926                 :             :             Node       *expr;
   15927                 :          12 :             Bitmapset  *expr_attrs = NULL;
   15928                 :             : 
   15929                 :          12 :             exprString = TextDatumGetCString(exprDatum);
   15930                 :          12 :             expr = stringToNode(exprString);
   15931                 :          12 :             pfree(exprString);
   15932                 :             : 
   15933                 :             :             /* Find all attributes referenced */
   15934                 :          12 :             pull_varattnos(expr, 1, &expr_attrs);
   15935                 :             : 
   15936                 :             :             /*
   15937                 :             :              * If the index expression contains a whole-row reference then
   15938                 :             :              * remember it.
   15939                 :             :              */
   15940         [ +  + ]:          12 :             if (bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber, expr_attrs))
   15941                 :             :             {
   15942                 :           8 :                 RememberIndexForRebuilding(index->indexrelid, tab);
   15943                 :           8 :                 continue;
   15944                 :             :             }
   15945                 :             :         }
   15946                 :             : 
   15947                 :          72 :         exprDatum = heap_getattr(indexTuple,
   15948                 :             :                                  Anum_pg_index_indpred,
   15949                 :             :                                  RelationGetDescr(pg_index),
   15950                 :             :                                  &isnull);
   15951         [ +  + ]:          72 :         if (!isnull)
   15952                 :             :         {
   15953                 :             :             char       *exprString;
   15954                 :             :             Node       *expr;
   15955                 :          20 :             Bitmapset  *expr_attrs = NULL;
   15956                 :             : 
   15957                 :          20 :             exprString = TextDatumGetCString(exprDatum);
   15958                 :          20 :             expr = stringToNode(exprString);
   15959                 :          20 :             pfree(exprString);
   15960                 :             : 
   15961                 :             :             /* Find all attributes referenced */
   15962                 :          20 :             pull_varattnos(expr, 1, &expr_attrs);
   15963                 :             : 
   15964                 :             :             /*
   15965                 :             :              * If the index predicate expression contains a whole-row
   15966                 :             :              * reference then remember it.
   15967                 :             :              */
   15968         [ +  + ]:          20 :             if (bms_is_member(InvalidAttrNumber - FirstLowInvalidHeapAttributeNumber, expr_attrs))
   15969                 :             :             {
   15970                 :          16 :                 RememberIndexForRebuilding(index->indexrelid, tab);
   15971                 :             :             }
   15972                 :             :         }
   15973                 :             :     }
   15974                 :             : 
   15975                 :         181 :     systable_endscan(indscan);
   15976                 :         181 :     table_close(pg_index, AccessShareLock);
   15977                 :         181 : }
   15978                 :             : 
   15979                 :             : /*
   15980                 :             :  * Subroutine for ATExecAlterColumnType: remember that a replica identity
   15981                 :             :  * needs to be reset.
   15982                 :             :  */
   15983                 :             : static void
   15984                 :         330 : RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
   15985                 :             : {
   15986         [ +  + ]:         330 :     if (!get_index_isreplident(indoid))
   15987                 :         318 :         return;
   15988                 :             : 
   15989         [ -  + ]:          12 :     if (tab->replicaIdentityIndex)
   15990         [ #  # ]:           0 :         elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
   15991                 :             : 
   15992                 :          12 :     tab->replicaIdentityIndex = get_rel_name(indoid);
   15993                 :             : }
   15994                 :             : 
   15995                 :             : /*
   15996                 :             :  * Subroutine for ATExecAlterColumnType: remember any clustered index.
   15997                 :             :  */
   15998                 :             : static void
   15999                 :         330 : RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
   16000                 :             : {
   16001         [ +  + ]:         330 :     if (!get_index_isclustered(indoid))
   16002                 :         318 :         return;
   16003                 :             : 
   16004         [ -  + ]:          12 :     if (tab->clusterOnIndex)
   16005         [ #  # ]:           0 :         elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
   16006                 :             : 
   16007                 :          12 :     tab->clusterOnIndex = get_rel_name(indoid);
   16008                 :             : }
   16009                 :             : 
   16010                 :             : /*
   16011                 :             :  * Subroutine for ATExecAlterColumnType: remember that a constraint needs
   16012                 :             :  * to be rebuilt (which we might already know).
   16013                 :             :  */
   16014                 :             : static void
   16015                 :         544 : RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
   16016                 :             : {
   16017                 :             :     /*
   16018                 :             :      * This de-duplication check is critical for two independent reasons: we
   16019                 :             :      * mustn't try to recreate the same constraint twice, and if a constraint
   16020                 :             :      * depends on more than one column whose type is to be altered, we must
   16021                 :             :      * capture its definition string before applying any of the column type
   16022                 :             :      * changes.  ruleutils.c will get confused if we ask again later.
   16023                 :             :      */
   16024         [ +  + ]:         544 :     if (!list_member_oid(tab->changedConstraintOids, conoid))
   16025                 :             :     {
   16026                 :             :         /* OK, capture the constraint's existing definition string */
   16027                 :         468 :         char       *defstring = pg_get_constraintdef_command(conoid);
   16028                 :             :         Oid         indoid;
   16029                 :             : 
   16030                 :             :         /*
   16031                 :             :          * It is critical to create not-null constraints ahead of primary key
   16032                 :             :          * indexes; otherwise, the not-null constraint would be created by the
   16033                 :             :          * primary key, and the constraint name would be wrong.
   16034                 :             :          */
   16035         [ +  + ]:         468 :         if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
   16036                 :             :         {
   16037                 :         163 :             tab->changedConstraintOids = lcons_oid(conoid,
   16038                 :             :                                                    tab->changedConstraintOids);
   16039                 :         163 :             tab->changedConstraintDefs = lcons(defstring,
   16040                 :             :                                                tab->changedConstraintDefs);
   16041                 :             :         }
   16042                 :             :         else
   16043                 :             :         {
   16044                 :             : 
   16045                 :         305 :             tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
   16046                 :             :                                                      conoid);
   16047                 :         305 :             tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
   16048                 :             :                                                  defstring);
   16049                 :             :         }
   16050                 :             : 
   16051                 :             :         /*
   16052                 :             :          * For the index of a constraint, if any, remember if it is used for
   16053                 :             :          * the table's replica identity or if it is a clustered index, so that
   16054                 :             :          * ATPostAlterTypeCleanup() can queue up commands necessary to restore
   16055                 :             :          * those properties.
   16056                 :             :          */
   16057                 :         468 :         indoid = get_constraint_index(conoid);
   16058         [ +  + ]:         468 :         if (OidIsValid(indoid))
   16059                 :             :         {
   16060                 :         152 :             RememberReplicaIdentityForRebuilding(indoid, tab);
   16061                 :         152 :             RememberClusterOnForRebuilding(indoid, tab);
   16062                 :             :         }
   16063                 :             :     }
   16064                 :         544 : }
   16065                 :             : 
   16066                 :             : /*
   16067                 :             :  * Subroutine for ATExecAlterColumnType: remember that an index needs
   16068                 :             :  * to be rebuilt (which we might already know).
   16069                 :             :  */
   16070                 :             : static void
   16071                 :         191 : RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
   16072                 :             : {
   16073                 :             :     /*
   16074                 :             :      * This de-duplication check is critical for two independent reasons: we
   16075                 :             :      * mustn't try to recreate the same index twice, and if an index depends
   16076                 :             :      * on more than one column whose type is to be altered, we must capture
   16077                 :             :      * its definition string before applying any of the column type changes.
   16078                 :             :      * ruleutils.c will get confused if we ask again later.
   16079                 :             :      */
   16080         [ +  + ]:         191 :     if (!list_member_oid(tab->changedIndexOids, indoid))
   16081                 :             :     {
   16082                 :             :         /*
   16083                 :             :          * Before adding it as an index-to-rebuild, we'd better see if it
   16084                 :             :          * belongs to a constraint, and if so rebuild the constraint instead.
   16085                 :             :          * Typically this check fails, because constraint indexes normally
   16086                 :             :          * have only dependencies on their constraint.  But it's possible for
   16087                 :             :          * such an index to also have direct dependencies on table columns,
   16088                 :             :          * for example with a partial exclusion constraint.
   16089                 :             :          */
   16090                 :         186 :         Oid         conoid = get_index_constraint(indoid);
   16091                 :             : 
   16092         [ +  + ]:         186 :         if (OidIsValid(conoid))
   16093                 :             :         {
   16094                 :           8 :             RememberConstraintForRebuilding(conoid, tab);
   16095                 :             :         }
   16096                 :             :         else
   16097                 :             :         {
   16098                 :             :             /* OK, capture the index's existing definition string */
   16099                 :         178 :             char       *defstring = pg_get_indexdef_string(indoid);
   16100                 :             : 
   16101                 :         178 :             tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
   16102                 :             :                                                 indoid);
   16103                 :         178 :             tab->changedIndexDefs = lappend(tab->changedIndexDefs,
   16104                 :             :                                             defstring);
   16105                 :             : 
   16106                 :             :             /*
   16107                 :             :              * Remember if this index is used for the table's replica identity
   16108                 :             :              * or if it is a clustered index, so that ATPostAlterTypeCleanup()
   16109                 :             :              * can queue up commands necessary to restore those properties.
   16110                 :             :              */
   16111                 :         178 :             RememberReplicaIdentityForRebuilding(indoid, tab);
   16112                 :         178 :             RememberClusterOnForRebuilding(indoid, tab);
   16113                 :             :         }
   16114                 :             :     }
   16115                 :         191 : }
   16116                 :             : 
   16117                 :             : /*
   16118                 :             :  * Subroutine for ATExecAlterColumnType: remember that a statistics object
   16119                 :             :  * needs to be rebuilt (which we might already know).
   16120                 :             :  */
   16121                 :             : static void
   16122                 :          57 : RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
   16123                 :             : {
   16124                 :             :     /*
   16125                 :             :      * This de-duplication check is critical for two independent reasons: we
   16126                 :             :      * mustn't try to recreate the same statistics object twice, and if the
   16127                 :             :      * statistics object depends on more than one column whose type is to be
   16128                 :             :      * altered, we must capture its definition string before applying any of
   16129                 :             :      * the type changes. ruleutils.c will get confused if we ask again later.
   16130                 :             :      */
   16131         [ +  - ]:          57 :     if (!list_member_oid(tab->changedStatisticsOids, stxoid))
   16132                 :             :     {
   16133                 :             :         /* OK, capture the statistics object's existing definition string */
   16134                 :          57 :         char       *defstring = pg_get_statisticsobjdef_string(stxoid);
   16135                 :             :         HeapTuple   tup;
   16136                 :             :         Form_pg_statistic_ext statext;
   16137                 :             : 
   16138                 :          57 :         tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxoid));
   16139                 :             : 
   16140         [ -  + ]:          57 :         if (!HeapTupleIsValid(tup)) /* should not happen */
   16141         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for statistics object %u", stxoid);
   16142                 :             : 
   16143                 :          57 :         statext = (Form_pg_statistic_ext) GETSTRUCT(tup);
   16144                 :             : 
   16145                 :          57 :         tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
   16146                 :             :                                                  stxoid);
   16147                 :          57 :         tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
   16148                 :             :                                              defstring);
   16149                 :             : 
   16150                 :          57 :         tab->changedStatisticsOwners = lappend_oid(tab->changedStatisticsOwners,
   16151                 :             :                                                    statext->stxowner);
   16152                 :             : 
   16153                 :          57 :         ReleaseSysCache(tup);
   16154                 :             :     }
   16155                 :          57 : }
   16156                 :             : 
   16157                 :             : /*
   16158                 :             :  * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
   16159                 :             :  * operations for a particular relation.  We have to drop and recreate all the
   16160                 :             :  * indexes and constraints that depend on the altered columns.  We do the
   16161                 :             :  * actual dropping here, but re-creation is managed by adding work queue
   16162                 :             :  * entries to do those steps later.
   16163                 :             :  */
   16164                 :             : static void
   16165                 :         925 : ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
   16166                 :             : {
   16167                 :             :     ObjectAddress obj;
   16168                 :             :     ObjectAddresses *objects;
   16169                 :             :     ListCell   *def_item;
   16170                 :             :     ListCell   *oid_item;
   16171                 :             :     ListCell   *owner_item;
   16172                 :             : 
   16173                 :             :     /*
   16174                 :             :      * Collect all the constraints and indexes to drop so we can process them
   16175                 :             :      * in a single call.  That way we don't have to worry about dependencies
   16176                 :             :      * among them.
   16177                 :             :      */
   16178                 :         925 :     objects = new_object_addresses();
   16179                 :             : 
   16180                 :             :     /*
   16181                 :             :      * Re-parse the index and constraint definitions, and attach them to the
   16182                 :             :      * appropriate work queue entries.  We do this before dropping because in
   16183                 :             :      * the case of a constraint on another table, we might not yet have
   16184                 :             :      * exclusive lock on the table the constraint is attached to, and we need
   16185                 :             :      * to get that before reparsing/dropping.  (That's possible at least for
   16186                 :             :      * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
   16187                 :             :      * requires a dependency on the target table's composite type in the other
   16188                 :             :      * table's constraint expressions.)
   16189                 :             :      *
   16190                 :             :      * We can't rely on the output of deparsing to tell us which relation to
   16191                 :             :      * operate on, because concurrent activity might have made the name
   16192                 :             :      * resolve differently.  Instead, we've got to use the OID of the
   16193                 :             :      * constraint or index we're processing to figure out which relation to
   16194                 :             :      * operate on.
   16195                 :             :      */
   16196   [ +  +  +  +  :        1393 :     forboth(oid_item, tab->changedConstraintOids,
          +  +  +  +  +  
             +  +  -  +  
                      + ]
   16197                 :             :             def_item, tab->changedConstraintDefs)
   16198                 :             :     {
   16199                 :         468 :         Oid         oldId = lfirst_oid(oid_item);
   16200                 :             :         HeapTuple   tup;
   16201                 :             :         Form_pg_constraint con;
   16202                 :             :         Oid         relid;
   16203                 :             :         Oid         confrelid;
   16204                 :             :         bool        conislocal;
   16205                 :             : 
   16206                 :         468 :         tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   16207         [ -  + ]:         468 :         if (!HeapTupleIsValid(tup)) /* should not happen */
   16208         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for constraint %u", oldId);
   16209                 :         468 :         con = (Form_pg_constraint) GETSTRUCT(tup);
   16210         [ +  + ]:         468 :         if (OidIsValid(con->conrelid))
   16211                 :         455 :             relid = con->conrelid;
   16212                 :             :         else
   16213                 :             :         {
   16214                 :             :             /* must be a domain constraint */
   16215                 :          13 :             relid = get_typ_typrelid(getBaseType(con->contypid));
   16216         [ -  + ]:          13 :             if (!OidIsValid(relid))
   16217         [ #  # ]:           0 :                 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
   16218                 :             :         }
   16219                 :         468 :         confrelid = con->confrelid;
   16220                 :         468 :         conislocal = con->conislocal;
   16221                 :         468 :         ReleaseSysCache(tup);
   16222                 :             : 
   16223                 :         468 :         ObjectAddressSet(obj, ConstraintRelationId, oldId);
   16224                 :         468 :         add_exact_object_address(&obj, objects);
   16225                 :             : 
   16226                 :             :         /*
   16227                 :             :          * If the constraint is inherited (only), we don't want to inject a
   16228                 :             :          * new definition here; it'll get recreated when
   16229                 :             :          * ATAddCheckNNConstraint recurses from adding the parent table's
   16230                 :             :          * constraint.  But we had to carry the info this far so that we can
   16231                 :             :          * drop the constraint below.
   16232                 :             :          */
   16233         [ +  + ]:         468 :         if (!conislocal)
   16234                 :          34 :             continue;
   16235                 :             : 
   16236                 :             :         /*
   16237                 :             :          * When rebuilding another table's constraint that references the
   16238                 :             :          * table we're modifying, we might not yet have any lock on the other
   16239                 :             :          * table, so get one now.  We'll need AccessExclusiveLock for the DROP
   16240                 :             :          * CONSTRAINT step, so there's no value in asking for anything weaker.
   16241                 :             :          */
   16242         [ +  + ]:         434 :         if (relid != tab->relid)
   16243                 :          36 :             LockRelationOid(relid, AccessExclusiveLock);
   16244                 :             : 
   16245                 :         434 :         ATPostAlterTypeParse(oldId, relid, confrelid, InvalidOid,
   16246                 :         434 :                              (char *) lfirst(def_item),
   16247                 :         434 :                              wqueue, lockmode, tab->rewrite);
   16248                 :             :     }
   16249   [ +  +  +  +  :        1103 :     forboth(oid_item, tab->changedIndexOids,
          +  +  +  +  +  
             +  +  -  +  
                      + ]
   16250                 :             :             def_item, tab->changedIndexDefs)
   16251                 :             :     {
   16252                 :         178 :         Oid         oldId = lfirst_oid(oid_item);
   16253                 :             :         Oid         relid;
   16254                 :             : 
   16255                 :         178 :         relid = IndexGetRelation(oldId, false);
   16256                 :             : 
   16257                 :             :         /*
   16258                 :             :          * As above, make sure we have lock on the index's table if it's not
   16259                 :             :          * the same table.
   16260                 :             :          */
   16261         [ +  + ]:         178 :         if (relid != tab->relid)
   16262                 :          12 :             LockRelationOid(relid, AccessExclusiveLock);
   16263                 :             : 
   16264                 :         178 :         ATPostAlterTypeParse(oldId, relid, InvalidOid, InvalidOid,
   16265                 :         178 :                              (char *) lfirst(def_item),
   16266                 :         178 :                              wqueue, lockmode, tab->rewrite);
   16267                 :             : 
   16268                 :         178 :         ObjectAddressSet(obj, RelationRelationId, oldId);
   16269                 :         178 :         add_exact_object_address(&obj, objects);
   16270                 :             :     }
   16271                 :             : 
   16272                 :             :     /* add dependencies for new statistics */
   16273   [ +  +  +  +  :         982 :     forthree(oid_item, tab->changedStatisticsOids,
          +  +  +  +  +  
          +  +  +  +  +  
          +  -  +  -  +  
                      + ]
   16274                 :             :              def_item, tab->changedStatisticsDefs,
   16275                 :             :              owner_item, tab->changedStatisticsOwners)
   16276                 :             :     {
   16277                 :          57 :         Oid         oldId = lfirst_oid(oid_item);
   16278                 :             :         Oid         relid;
   16279                 :             : 
   16280                 :          57 :         relid = StatisticsGetRelation(oldId, false);
   16281                 :             : 
   16282                 :             :         /*
   16283                 :             :          * As above, make sure we have lock on the statistics object's table
   16284                 :             :          * if it's not the same table.  However, we take
   16285                 :             :          * ShareUpdateExclusiveLock here, aligning with the lock level used in
   16286                 :             :          * CreateStatistics and RemoveStatisticsById.
   16287                 :             :          *
   16288                 :             :          * CAUTION: this should be done after all cases that grab
   16289                 :             :          * AccessExclusiveLock, else we risk causing deadlock due to needing
   16290                 :             :          * to promote our table lock.
   16291                 :             :          */
   16292         [ +  + ]:          57 :         if (relid != tab->relid)
   16293                 :          12 :             LockRelationOid(relid, ShareUpdateExclusiveLock);
   16294                 :             : 
   16295                 :          57 :         ATPostAlterTypeParse(oldId, relid, InvalidOid, lfirst_oid(owner_item),
   16296                 :          57 :                              (char *) lfirst(def_item),
   16297                 :          57 :                              wqueue, lockmode, tab->rewrite);
   16298                 :             : 
   16299                 :          57 :         ObjectAddressSet(obj, StatisticExtRelationId, oldId);
   16300                 :          57 :         add_exact_object_address(&obj, objects);
   16301                 :             :     }
   16302                 :             : 
   16303                 :             :     /*
   16304                 :             :      * Queue up command to restore replica identity index marking
   16305                 :             :      */
   16306         [ +  + ]:         925 :     if (tab->replicaIdentityIndex)
   16307                 :             :     {
   16308                 :          12 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   16309                 :          12 :         ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
   16310                 :             : 
   16311                 :          12 :         subcmd->identity_type = REPLICA_IDENTITY_INDEX;
   16312                 :          12 :         subcmd->name = tab->replicaIdentityIndex;
   16313                 :          12 :         cmd->subtype = AT_ReplicaIdentity;
   16314                 :          12 :         cmd->def = (Node *) subcmd;
   16315                 :             : 
   16316                 :             :         /* do it after indexes and constraints */
   16317                 :          12 :         tab->subcmds[AT_PASS_OLD_CONSTR] =
   16318                 :          12 :             lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   16319                 :             :     }
   16320                 :             : 
   16321                 :             :     /*
   16322                 :             :      * Queue up command to restore marking of index used for cluster.
   16323                 :             :      */
   16324         [ +  + ]:         925 :     if (tab->clusterOnIndex)
   16325                 :             :     {
   16326                 :          12 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   16327                 :             : 
   16328                 :          12 :         cmd->subtype = AT_ClusterOn;
   16329                 :          12 :         cmd->name = tab->clusterOnIndex;
   16330                 :             : 
   16331                 :             :         /* do it after indexes and constraints */
   16332                 :          12 :         tab->subcmds[AT_PASS_OLD_CONSTR] =
   16333                 :          12 :             lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   16334                 :             :     }
   16335                 :             : 
   16336                 :             :     /*
   16337                 :             :      * It should be okay to use DROP_RESTRICT here, since nothing else should
   16338                 :             :      * be depending on these objects.
   16339                 :             :      */
   16340                 :         925 :     performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   16341                 :             : 
   16342                 :         925 :     free_object_addresses(objects);
   16343                 :             : 
   16344                 :             :     /*
   16345                 :             :      * The objects will get recreated during subsequent passes over the work
   16346                 :             :      * queue.
   16347                 :             :      */
   16348                 :         925 : }
   16349                 :             : 
   16350                 :             : /*
   16351                 :             :  * Parse the previously-saved definition string for a constraint, index or
   16352                 :             :  * statistics object against the newly-established column data type(s), and
   16353                 :             :  * queue up the resulting command parsetrees for execution.
   16354                 :             :  *
   16355                 :             :  * This might fail if, for example, you have a WHERE clause that uses an
   16356                 :             :  * operator that's not available for the new column type.
   16357                 :             :  */
   16358                 :             : static void
   16359                 :         669 : ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, Oid ownerId,
   16360                 :             :                      char *cmd, List **wqueue, LOCKMODE lockmode,
   16361                 :             :                      bool rewrite)
   16362                 :             : {
   16363                 :             :     List       *raw_parsetree_list;
   16364                 :             :     List       *querytree_list;
   16365                 :             :     ListCell   *list_item;
   16366                 :             :     Relation    rel;
   16367                 :             : 
   16368                 :             :     /*
   16369                 :             :      * We expect that we will get only ALTER TABLE and CREATE INDEX
   16370                 :             :      * statements. Hence, there is no need to pass them through
   16371                 :             :      * parse_analyze_*() or the rewriter, but instead we need to pass them
   16372                 :             :      * through parse_utilcmd.c to make them ready for execution.
   16373                 :             :      */
   16374                 :         669 :     raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
   16375                 :         669 :     querytree_list = NIL;
   16376   [ +  -  +  +  :        1338 :     foreach(list_item, raw_parsetree_list)
                   +  + ]
   16377                 :             :     {
   16378                 :         669 :         RawStmt    *rs = lfirst_node(RawStmt, list_item);
   16379                 :         669 :         Node       *stmt = rs->stmt;
   16380                 :             : 
   16381         [ +  + ]:         669 :         if (IsA(stmt, IndexStmt))
   16382                 :         178 :             querytree_list = lappend(querytree_list,
   16383                 :         178 :                                      transformIndexStmt(oldRelId,
   16384                 :             :                                                         (IndexStmt *) stmt,
   16385                 :             :                                                         cmd));
   16386         [ +  + ]:         491 :         else if (IsA(stmt, AlterTableStmt))
   16387                 :             :         {
   16388                 :             :             List       *beforeStmts;
   16389                 :             :             List       *afterStmts;
   16390                 :             : 
   16391                 :         421 :             stmt = (Node *) transformAlterTableStmt(oldRelId,
   16392                 :             :                                                     (AlterTableStmt *) stmt,
   16393                 :             :                                                     cmd,
   16394                 :             :                                                     &beforeStmts,
   16395                 :             :                                                     &afterStmts);
   16396                 :         421 :             querytree_list = list_concat(querytree_list, beforeStmts);
   16397                 :         421 :             querytree_list = lappend(querytree_list, stmt);
   16398                 :         421 :             querytree_list = list_concat(querytree_list, afterStmts);
   16399                 :             :         }
   16400         [ +  + ]:          70 :         else if (IsA(stmt, CreateStatsStmt))
   16401                 :             :         {
   16402                 :             :             CreateStatsStmt *csstmt;
   16403                 :             : 
   16404                 :          57 :             csstmt = transformStatsStmt(oldRelId, (CreateStatsStmt *) stmt, cmd);
   16405                 :          57 :             csstmt->owner = ownerId;
   16406                 :             : 
   16407                 :          57 :             querytree_list = lappend(querytree_list, csstmt);
   16408                 :             :         }
   16409                 :             :         else
   16410                 :          13 :             querytree_list = lappend(querytree_list, stmt);
   16411                 :             :     }
   16412                 :             : 
   16413                 :             :     /* Caller should already have acquired whatever lock we need. */
   16414                 :         669 :     rel = relation_open(oldRelId, NoLock);
   16415                 :             : 
   16416                 :             :     /*
   16417                 :             :      * Attach each generated command to the proper place in the work queue.
   16418                 :             :      * Note this could result in creation of entirely new work-queue entries.
   16419                 :             :      *
   16420                 :             :      * Also note that we have to tweak the command subtypes, because it turns
   16421                 :             :      * out that re-creation of indexes and constraints has to act a bit
   16422                 :             :      * differently from initial creation.
   16423                 :             :      */
   16424   [ +  -  +  +  :        1338 :     foreach(list_item, querytree_list)
                   +  + ]
   16425                 :             :     {
   16426                 :         669 :         Node       *stm = (Node *) lfirst(list_item);
   16427                 :             :         AlteredTableInfo *tab;
   16428                 :             : 
   16429                 :         669 :         tab = ATGetQueueEntry(wqueue, rel);
   16430                 :             : 
   16431         [ +  + ]:         669 :         if (IsA(stm, IndexStmt))
   16432                 :             :         {
   16433                 :         178 :             IndexStmt  *stmt = (IndexStmt *) stm;
   16434                 :             :             AlterTableCmd *newcmd;
   16435                 :             : 
   16436         [ +  + ]:         178 :             if (!rewrite)
   16437                 :          53 :                 TryReuseIndex(oldId, stmt);
   16438                 :         178 :             stmt->reset_default_tblspc = true;
   16439                 :             :             /* keep the index's comment */
   16440                 :         178 :             stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
   16441                 :             : 
   16442                 :         178 :             newcmd = makeNode(AlterTableCmd);
   16443                 :         178 :             newcmd->subtype = AT_ReAddIndex;
   16444                 :         178 :             newcmd->def = (Node *) stmt;
   16445                 :         178 :             tab->subcmds[AT_PASS_OLD_INDEX] =
   16446                 :         178 :                 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
   16447                 :             :         }
   16448         [ +  + ]:         491 :         else if (IsA(stm, AlterTableStmt))
   16449                 :             :         {
   16450                 :         421 :             AlterTableStmt *stmt = (AlterTableStmt *) stm;
   16451                 :             :             ListCell   *lcmd;
   16452                 :             : 
   16453   [ +  -  +  +  :         842 :             foreach(lcmd, stmt->cmds)
                   +  + ]
   16454                 :             :             {
   16455                 :         421 :                 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
   16456                 :             : 
   16457         [ +  + ]:         421 :                 if (cmd->subtype == AT_AddIndex)
   16458                 :             :                 {
   16459                 :             :                     IndexStmt  *indstmt;
   16460                 :             :                     Oid         indoid;
   16461                 :             : 
   16462                 :         152 :                     indstmt = castNode(IndexStmt, cmd->def);
   16463                 :         152 :                     indoid = get_constraint_index(oldId);
   16464                 :             : 
   16465         [ +  + ]:         152 :                     if (!rewrite)
   16466                 :          32 :                         TryReuseIndex(indoid, indstmt);
   16467                 :             :                     /* keep any comment on the index */
   16468                 :         152 :                     indstmt->idxcomment = GetComment(indoid,
   16469                 :             :                                                      RelationRelationId, 0);
   16470                 :         152 :                     indstmt->reset_default_tblspc = true;
   16471                 :             : 
   16472                 :         152 :                     cmd->subtype = AT_ReAddIndex;
   16473                 :         152 :                     tab->subcmds[AT_PASS_OLD_INDEX] =
   16474                 :         152 :                         lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
   16475                 :             : 
   16476                 :             :                     /* recreate any comment on the constraint */
   16477                 :         152 :                     RebuildConstraintComment(tab,
   16478                 :             :                                              AT_PASS_OLD_INDEX,
   16479                 :             :                                              oldId,
   16480                 :             :                                              rel,
   16481                 :             :                                              NIL,
   16482                 :         152 :                                              indstmt->idxname);
   16483                 :             :                 }
   16484         [ +  - ]:         269 :                 else if (cmd->subtype == AT_AddConstraint)
   16485                 :             :                 {
   16486                 :         269 :                     Constraint *con = castNode(Constraint, cmd->def);
   16487                 :             : 
   16488                 :         269 :                     con->old_pktable_oid = refRelId;
   16489                 :             :                     /* rewriting neither side of a FK */
   16490         [ +  + ]:         269 :                     if (con->contype == CONSTR_FOREIGN &&
   16491   [ +  +  +  - ]:          48 :                         !rewrite && tab->rewrite == 0)
   16492                 :           4 :                         TryReuseForeignKey(oldId, con);
   16493                 :         269 :                     con->reset_default_tblspc = true;
   16494                 :         269 :                     cmd->subtype = AT_ReAddConstraint;
   16495                 :         269 :                     tab->subcmds[AT_PASS_OLD_CONSTR] =
   16496                 :         269 :                         lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   16497                 :             : 
   16498                 :             :                     /*
   16499                 :             :                      * Recreate any comment on the constraint.  If we have
   16500                 :             :                      * recreated a primary key, then transformTableConstraint
   16501                 :             :                      * has added an unnamed not-null constraint here; skip
   16502                 :             :                      * this in that case.
   16503                 :             :                      */
   16504         [ +  - ]:         269 :                     if (con->conname)
   16505                 :         269 :                         RebuildConstraintComment(tab,
   16506                 :             :                                                  AT_PASS_OLD_CONSTR,
   16507                 :             :                                                  oldId,
   16508                 :             :                                                  rel,
   16509                 :             :                                                  NIL,
   16510                 :         269 :                                                  con->conname);
   16511                 :             :                     else
   16512                 :             :                         Assert(con->contype == CONSTR_NOTNULL);
   16513                 :             :                 }
   16514                 :             :                 else
   16515         [ #  # ]:           0 :                     elog(ERROR, "unexpected statement subtype: %d",
   16516                 :             :                          (int) cmd->subtype);
   16517                 :             :             }
   16518                 :             :         }
   16519         [ +  + ]:          70 :         else if (IsA(stm, AlterDomainStmt))
   16520                 :             :         {
   16521                 :          13 :             AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
   16522                 :             : 
   16523         [ +  - ]:          13 :             if (stmt->subtype == AD_AddConstraint)
   16524                 :             :             {
   16525                 :          13 :                 Constraint *con = castNode(Constraint, stmt->def);
   16526                 :          13 :                 AlterTableCmd *cmd = makeNode(AlterTableCmd);
   16527                 :             : 
   16528                 :          13 :                 cmd->subtype = AT_ReAddDomainConstraint;
   16529                 :          13 :                 cmd->def = (Node *) stmt;
   16530                 :          13 :                 tab->subcmds[AT_PASS_OLD_CONSTR] =
   16531                 :          13 :                     lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
   16532                 :             : 
   16533                 :             :                 /* recreate any comment on the constraint */
   16534                 :          13 :                 RebuildConstraintComment(tab,
   16535                 :             :                                          AT_PASS_OLD_CONSTR,
   16536                 :             :                                          oldId,
   16537                 :             :                                          NULL,
   16538                 :             :                                          stmt->typeName,
   16539                 :          13 :                                          con->conname);
   16540                 :             :             }
   16541                 :             :             else
   16542         [ #  # ]:           0 :                 elog(ERROR, "unexpected statement subtype: %d",
   16543                 :             :                      (int) stmt->subtype);
   16544                 :             :         }
   16545         [ +  - ]:          57 :         else if (IsA(stm, CreateStatsStmt))
   16546                 :             :         {
   16547                 :          57 :             CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
   16548                 :             :             AlterTableCmd *newcmd;
   16549                 :             : 
   16550                 :             :             /* keep the statistics object's comment */
   16551                 :          57 :             stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
   16552                 :             : 
   16553                 :          57 :             newcmd = makeNode(AlterTableCmd);
   16554                 :          57 :             newcmd->subtype = AT_ReAddStatistics;
   16555                 :          57 :             newcmd->def = (Node *) stmt;
   16556                 :          57 :             tab->subcmds[AT_PASS_MISC] =
   16557                 :          57 :                 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
   16558                 :             :         }
   16559                 :             :         else
   16560         [ #  # ]:           0 :             elog(ERROR, "unexpected statement type: %d",
   16561                 :             :                  (int) nodeTag(stm));
   16562                 :             :     }
   16563                 :             : 
   16564                 :         669 :     relation_close(rel, NoLock);
   16565                 :         669 : }
   16566                 :             : 
   16567                 :             : /*
   16568                 :             :  * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
   16569                 :             :  * for a table or domain constraint that is being rebuilt.
   16570                 :             :  *
   16571                 :             :  * objid is the OID of the constraint.
   16572                 :             :  * Pass "rel" for a table constraint, or "domname" (domain's qualified name
   16573                 :             :  * as a string list) for a domain constraint.
   16574                 :             :  * (We could dig that info, as well as the conname, out of the pg_constraint
   16575                 :             :  * entry; but callers already have them so might as well pass them.)
   16576                 :             :  */
   16577                 :             : static void
   16578                 :         434 : RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
   16579                 :             :                          Relation rel, List *domname,
   16580                 :             :                          const char *conname)
   16581                 :             : {
   16582                 :             :     CommentStmt *cmd;
   16583                 :             :     char       *comment_str;
   16584                 :             :     AlterTableCmd *newcmd;
   16585                 :             : 
   16586                 :             :     /* Look for comment for object wanted, and leave if none */
   16587                 :         434 :     comment_str = GetComment(objid, ConstraintRelationId, 0);
   16588         [ +  + ]:         434 :     if (comment_str == NULL)
   16589                 :         374 :         return;
   16590                 :             : 
   16591                 :             :     /* Build CommentStmt node, copying all input data for safety */
   16592                 :          60 :     cmd = makeNode(CommentStmt);
   16593         [ +  + ]:          60 :     if (rel)
   16594                 :             :     {
   16595                 :          52 :         cmd->objtype = OBJECT_TABCONSTRAINT;
   16596                 :          52 :         cmd->object = (Node *)
   16597                 :          52 :             list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
   16598                 :             :                        makeString(pstrdup(RelationGetRelationName(rel))),
   16599                 :             :                        makeString(pstrdup(conname)));
   16600                 :             :     }
   16601                 :             :     else
   16602                 :             :     {
   16603                 :           8 :         cmd->objtype = OBJECT_DOMCONSTRAINT;
   16604                 :           8 :         cmd->object = (Node *)
   16605                 :           8 :             list_make2(makeTypeNameFromNameList(copyObject(domname)),
   16606                 :             :                        makeString(pstrdup(conname)));
   16607                 :             :     }
   16608                 :          60 :     cmd->comment = comment_str;
   16609                 :             : 
   16610                 :             :     /* Append it to list of commands */
   16611                 :          60 :     newcmd = makeNode(AlterTableCmd);
   16612                 :          60 :     newcmd->subtype = AT_ReAddComment;
   16613                 :          60 :     newcmd->def = (Node *) cmd;
   16614                 :          60 :     tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
   16615                 :             : }
   16616                 :             : 
   16617                 :             : /*
   16618                 :             :  * Subroutine for ATPostAlterTypeParse().  Calls out to CheckIndexCompatible()
   16619                 :             :  * for the real analysis, then mutates the IndexStmt based on that verdict.
   16620                 :             :  */
   16621                 :             : static void
   16622                 :          85 : TryReuseIndex(Oid oldId, IndexStmt *stmt)
   16623                 :             : {
   16624         [ +  + ]:          85 :     if (CheckIndexCompatible(oldId,
   16625                 :          85 :                              stmt->accessMethod,
   16626                 :          85 :                              stmt->indexParams,
   16627                 :          85 :                              stmt->excludeOpNames,
   16628                 :          85 :                              stmt->iswithoutoverlaps))
   16629                 :             :     {
   16630                 :          69 :         Relation    irel = index_open(oldId, NoLock);
   16631                 :             : 
   16632                 :             :         /* If it's a partitioned index, there is no storage to share. */
   16633         [ +  + ]:          69 :         if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   16634                 :             :         {
   16635                 :          49 :             stmt->oldNumber = irel->rd_locator.relNumber;
   16636                 :          49 :             stmt->oldCreateSubid = irel->rd_createSubid;
   16637                 :          49 :             stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
   16638                 :             :         }
   16639                 :          69 :         index_close(irel, NoLock);
   16640                 :             :     }
   16641                 :          85 : }
   16642                 :             : 
   16643                 :             : /*
   16644                 :             :  * Subroutine for ATPostAlterTypeParse().
   16645                 :             :  *
   16646                 :             :  * Stash the old P-F equality operator into the Constraint node, for possible
   16647                 :             :  * use by ATAddForeignKeyConstraint() in determining whether revalidation of
   16648                 :             :  * this constraint can be skipped.
   16649                 :             :  */
   16650                 :             : static void
   16651                 :           4 : TryReuseForeignKey(Oid oldId, Constraint *con)
   16652                 :             : {
   16653                 :             :     HeapTuple   tup;
   16654                 :             :     Datum       adatum;
   16655                 :             :     ArrayType  *arr;
   16656                 :             :     Oid        *rawarr;
   16657                 :             :     int         numkeys;
   16658                 :             :     int         i;
   16659                 :             : 
   16660                 :             :     Assert(con->contype == CONSTR_FOREIGN);
   16661                 :             :     Assert(con->old_conpfeqop == NIL);   /* already prepared this node */
   16662                 :             : 
   16663                 :           4 :     tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
   16664         [ -  + ]:           4 :     if (!HeapTupleIsValid(tup)) /* should not happen */
   16665         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for constraint %u", oldId);
   16666                 :             : 
   16667                 :           4 :     adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
   16668                 :             :                                     Anum_pg_constraint_conpfeqop);
   16669                 :           4 :     arr = DatumGetArrayTypeP(adatum);   /* ensure not toasted */
   16670                 :           4 :     numkeys = ARR_DIMS(arr)[0];
   16671                 :             :     /* test follows the one in ri_FetchConstraintInfo() */
   16672         [ +  - ]:           4 :     if (ARR_NDIM(arr) != 1 ||
   16673         [ +  - ]:           4 :         ARR_HASNULL(arr) ||
   16674         [ -  + ]:           4 :         ARR_ELEMTYPE(arr) != OIDOID)
   16675         [ #  # ]:           0 :         elog(ERROR, "conpfeqop is not a 1-D Oid array");
   16676         [ -  + ]:           4 :     rawarr = (Oid *) ARR_DATA_PTR(arr);
   16677                 :             : 
   16678                 :             :     /* stash a List of the operator Oids in our Constraint node */
   16679         [ +  + ]:           8 :     for (i = 0; i < numkeys; i++)
   16680                 :           4 :         con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
   16681                 :             : 
   16682                 :           4 :     ReleaseSysCache(tup);
   16683                 :           4 : }
   16684                 :             : 
   16685                 :             : /*
   16686                 :             :  * ALTER COLUMN .. OPTIONS ( ... )
   16687                 :             :  *
   16688                 :             :  * Returns the address of the modified column
   16689                 :             :  */
   16690                 :             : static ObjectAddress
   16691                 :          94 : ATExecAlterColumnGenericOptions(Relation rel,
   16692                 :             :                                 const char *colName,
   16693                 :             :                                 List *options,
   16694                 :             :                                 LOCKMODE lockmode)
   16695                 :             : {
   16696                 :             :     Relation    ftrel;
   16697                 :             :     Relation    attrel;
   16698                 :             :     ForeignServer *server;
   16699                 :             :     ForeignDataWrapper *fdw;
   16700                 :             :     HeapTuple   tuple;
   16701                 :             :     HeapTuple   newtuple;
   16702                 :             :     bool        isnull;
   16703                 :             :     Datum       repl_val[Natts_pg_attribute];
   16704                 :             :     bool        repl_null[Natts_pg_attribute];
   16705                 :             :     bool        repl_repl[Natts_pg_attribute];
   16706                 :             :     Datum       datum;
   16707                 :             :     Form_pg_foreign_table fttableform;
   16708                 :             :     Form_pg_attribute atttableform;
   16709                 :             :     AttrNumber  attnum;
   16710                 :             :     ObjectAddress address;
   16711                 :             : 
   16712         [ -  + ]:          94 :     if (options == NIL)
   16713                 :           0 :         return InvalidObjectAddress;
   16714                 :             : 
   16715                 :             :     /* First, determine FDW validator associated to the foreign table. */
   16716                 :          94 :     ftrel = table_open(ForeignTableRelationId, AccessShareLock);
   16717                 :          94 :     tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
   16718         [ -  + ]:          94 :     if (!HeapTupleIsValid(tuple))
   16719         [ #  # ]:           0 :         ereport(ERROR,
   16720                 :             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   16721                 :             :                  errmsg("foreign table \"%s\" does not exist",
   16722                 :             :                         RelationGetRelationName(rel))));
   16723                 :          94 :     fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   16724                 :          94 :     server = GetForeignServer(fttableform->ftserver);
   16725                 :          94 :     fdw = GetForeignDataWrapper(server->fdwid);
   16726                 :             : 
   16727                 :          94 :     table_close(ftrel, AccessShareLock);
   16728                 :          94 :     ReleaseSysCache(tuple);
   16729                 :             : 
   16730                 :          94 :     attrel = table_open(AttributeRelationId, RowExclusiveLock);
   16731                 :          94 :     tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
   16732         [ -  + ]:          94 :     if (!HeapTupleIsValid(tuple))
   16733         [ #  # ]:           0 :         ereport(ERROR,
   16734                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   16735                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   16736                 :             :                         colName, RelationGetRelationName(rel))));
   16737                 :             : 
   16738                 :             :     /* Prevent them from altering a system attribute */
   16739                 :          94 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   16740                 :          94 :     attnum = atttableform->attnum;
   16741         [ +  + ]:          94 :     if (attnum <= 0)
   16742         [ +  - ]:           4 :         ereport(ERROR,
   16743                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16744                 :             :                  errmsg("cannot alter system column \"%s\"", colName)));
   16745                 :             : 
   16746                 :             : 
   16747                 :             :     /* Initialize buffers for new tuple values */
   16748                 :          90 :     memset(repl_val, 0, sizeof(repl_val));
   16749                 :          90 :     memset(repl_null, false, sizeof(repl_null));
   16750                 :          90 :     memset(repl_repl, false, sizeof(repl_repl));
   16751                 :             : 
   16752                 :             :     /* Extract the current options */
   16753                 :          90 :     datum = SysCacheGetAttr(ATTNAME,
   16754                 :             :                             tuple,
   16755                 :             :                             Anum_pg_attribute_attfdwoptions,
   16756                 :             :                             &isnull);
   16757         [ +  + ]:          90 :     if (isnull)
   16758                 :          84 :         datum = PointerGetDatum(NULL);
   16759                 :             : 
   16760                 :             :     /* Transform the options */
   16761                 :          90 :     datum = transformGenericOptions(AttributeRelationId,
   16762                 :             :                                     datum,
   16763                 :             :                                     options,
   16764                 :             :                                     fdw->fdwvalidator);
   16765                 :             : 
   16766         [ +  - ]:          90 :     if (DatumGetPointer(datum) != NULL)
   16767                 :          90 :         repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
   16768                 :             :     else
   16769                 :           0 :         repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
   16770                 :             : 
   16771                 :          90 :     repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
   16772                 :             : 
   16773                 :             :     /* Everything looks good - update the tuple */
   16774                 :             : 
   16775                 :          90 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
   16776                 :             :                                  repl_val, repl_null, repl_repl);
   16777                 :             : 
   16778                 :          90 :     CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
   16779                 :             : 
   16780         [ -  + ]:          90 :     InvokeObjectPostAlterHook(RelationRelationId,
   16781                 :             :                               RelationGetRelid(rel),
   16782                 :             :                               atttableform->attnum);
   16783                 :          90 :     ObjectAddressSubSet(address, RelationRelationId,
   16784                 :             :                         RelationGetRelid(rel), attnum);
   16785                 :             : 
   16786                 :          90 :     ReleaseSysCache(tuple);
   16787                 :             : 
   16788                 :          90 :     table_close(attrel, RowExclusiveLock);
   16789                 :             : 
   16790                 :          90 :     heap_freetuple(newtuple);
   16791                 :             : 
   16792                 :          90 :     return address;
   16793                 :             : }
   16794                 :             : 
   16795                 :             : /*
   16796                 :             :  * ALTER TABLE OWNER
   16797                 :             :  *
   16798                 :             :  * recursing is true if we are recursing from a table to its indexes,
   16799                 :             :  * sequences, or toast table.  We don't allow the ownership of those things to
   16800                 :             :  * be changed separately from the parent table.  Also, we can skip permission
   16801                 :             :  * checks (this is necessary not just an optimization, else we'd fail to
   16802                 :             :  * handle toast tables properly).
   16803                 :             :  *
   16804                 :             :  * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
   16805                 :             :  * free-standing composite type.
   16806                 :             :  */
   16807                 :             : void
   16808                 :        1388 : ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
   16809                 :             : {
   16810                 :             :     Relation    target_rel;
   16811                 :             :     Relation    class_rel;
   16812                 :             :     HeapTuple   tuple;
   16813                 :             :     Form_pg_class tuple_class;
   16814                 :             : 
   16815                 :             :     /*
   16816                 :             :      * Get exclusive lock till end of transaction on the target table. Use
   16817                 :             :      * relation_open so that we can work on indexes and sequences.
   16818                 :             :      */
   16819                 :        1388 :     target_rel = relation_open(relationOid, lockmode);
   16820                 :             : 
   16821                 :             :     /* Get its pg_class tuple, too */
   16822                 :        1388 :     class_rel = table_open(RelationRelationId, RowExclusiveLock);
   16823                 :             : 
   16824                 :        1388 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
   16825         [ -  + ]:        1388 :     if (!HeapTupleIsValid(tuple))
   16826         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", relationOid);
   16827                 :        1388 :     tuple_class = (Form_pg_class) GETSTRUCT(tuple);
   16828                 :             : 
   16829                 :             :     /* Can we change the ownership of this tuple? */
   16830   [ +  +  +  +  :        1388 :     switch (tuple_class->relkind)
                +  +  - ]
   16831                 :             :     {
   16832                 :        1154 :         case RELKIND_RELATION:
   16833                 :             :         case RELKIND_VIEW:
   16834                 :             :         case RELKIND_MATVIEW:
   16835                 :             :         case RELKIND_FOREIGN_TABLE:
   16836                 :             :         case RELKIND_PARTITIONED_TABLE:
   16837                 :             :         case RELKIND_PROPGRAPH:
   16838                 :             :             /* ok to change owner */
   16839                 :        1154 :             break;
   16840                 :          99 :         case RELKIND_INDEX:
   16841         [ -  + ]:          99 :             if (!recursing)
   16842                 :             :             {
   16843                 :             :                 /*
   16844                 :             :                  * Because ALTER INDEX OWNER used to be allowed, and in fact
   16845                 :             :                  * is generated by old versions of pg_dump, we give a warning
   16846                 :             :                  * and do nothing rather than erroring out.  Also, to avoid
   16847                 :             :                  * unnecessary chatter while restoring those old dumps, say
   16848                 :             :                  * nothing at all if the command would be a no-op anyway.
   16849                 :             :                  */
   16850         [ #  # ]:           0 :                 if (tuple_class->relowner != newOwnerId)
   16851         [ #  # ]:           0 :                     ereport(WARNING,
   16852                 :             :                             (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16853                 :             :                              errmsg("cannot change owner of index \"%s\"",
   16854                 :             :                                     NameStr(tuple_class->relname)),
   16855                 :             :                              errhint("Change the ownership of the index's table instead.")));
   16856                 :             :                 /* quick hack to exit via the no-op path */
   16857                 :           0 :                 newOwnerId = tuple_class->relowner;
   16858                 :             :             }
   16859                 :          99 :             break;
   16860                 :          14 :         case RELKIND_PARTITIONED_INDEX:
   16861         [ +  - ]:          14 :             if (recursing)
   16862                 :          14 :                 break;
   16863         [ #  # ]:           0 :             ereport(ERROR,
   16864                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16865                 :             :                      errmsg("cannot change owner of index \"%s\"",
   16866                 :             :                             NameStr(tuple_class->relname)),
   16867                 :             :                      errhint("Change the ownership of the index's table instead.")));
   16868                 :             :             break;
   16869                 :          65 :         case RELKIND_SEQUENCE:
   16870         [ +  + ]:          65 :             if (!recursing &&
   16871         [ -  + ]:          35 :                 tuple_class->relowner != newOwnerId)
   16872                 :             :             {
   16873                 :             :                 /* if it's an owned sequence, disallow changing it by itself */
   16874                 :             :                 Oid         tableId;
   16875                 :             :                 int32       colId;
   16876                 :             : 
   16877   [ #  #  #  # ]:           0 :                 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
   16878                 :           0 :                     sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
   16879         [ #  # ]:           0 :                     ereport(ERROR,
   16880                 :             :                             (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   16881                 :             :                              errmsg("cannot change owner of sequence \"%s\"",
   16882                 :             :                                     NameStr(tuple_class->relname)),
   16883                 :             :                              errdetail("Sequence \"%s\" is linked to table \"%s\".",
   16884                 :             :                                        NameStr(tuple_class->relname),
   16885                 :             :                                        get_rel_name(tableId))));
   16886                 :             :             }
   16887                 :          65 :             break;
   16888                 :           5 :         case RELKIND_COMPOSITE_TYPE:
   16889         [ +  - ]:           5 :             if (recursing)
   16890                 :           5 :                 break;
   16891         [ #  # ]:           0 :             ereport(ERROR,
   16892                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16893                 :             :                      errmsg("\"%s\" is a composite type",
   16894                 :             :                             NameStr(tuple_class->relname)),
   16895                 :             :             /* translator: %s is an SQL ALTER command */
   16896                 :             :                      errhint("Use %s instead.",
   16897                 :             :                              "ALTER TYPE")));
   16898                 :             :             break;
   16899                 :          51 :         case RELKIND_TOASTVALUE:
   16900         [ +  - ]:          51 :             if (recursing)
   16901                 :          51 :                 break;
   16902                 :             :             pg_fallthrough;
   16903                 :             :         default:
   16904         [ #  # ]:           0 :             ereport(ERROR,
   16905                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   16906                 :             :                      errmsg("cannot change owner of relation \"%s\"",
   16907                 :             :                             NameStr(tuple_class->relname)),
   16908                 :             :                      errdetail_relkind_not_supported(tuple_class->relkind)));
   16909                 :             :     }
   16910                 :             : 
   16911                 :             :     /*
   16912                 :             :      * If the new owner is the same as the existing owner, consider the
   16913                 :             :      * command to have succeeded.  This is for dump restoration purposes.
   16914                 :             :      */
   16915         [ +  + ]:        1388 :     if (tuple_class->relowner != newOwnerId)
   16916                 :             :     {
   16917                 :             :         Datum       repl_val[Natts_pg_class];
   16918                 :             :         bool        repl_null[Natts_pg_class];
   16919                 :             :         bool        repl_repl[Natts_pg_class];
   16920                 :             :         Acl        *newAcl;
   16921                 :             :         Datum       aclDatum;
   16922                 :             :         bool        isNull;
   16923                 :             :         HeapTuple   newtuple;
   16924                 :             : 
   16925                 :             :         /* skip permission checks when recursing to index or toast table */
   16926         [ +  + ]:         452 :         if (!recursing)
   16927                 :             :         {
   16928                 :             :             /* Superusers can always do it */
   16929         [ +  + ]:         219 :             if (!superuser())
   16930                 :             :             {
   16931                 :          28 :                 Oid         namespaceOid = tuple_class->relnamespace;
   16932                 :             :                 AclResult   aclresult;
   16933                 :             : 
   16934                 :             :                 /* Otherwise, must be owner of the existing object */
   16935         [ -  + ]:          28 :                 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
   16936                 :           0 :                     aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
   16937                 :           0 :                                    RelationGetRelationName(target_rel));
   16938                 :             : 
   16939                 :             :                 /* Must be able to become new owner */
   16940                 :          28 :                 check_can_set_role(GetUserId(), newOwnerId);
   16941                 :             : 
   16942                 :             :                 /* New owner must have CREATE privilege on namespace */
   16943                 :          20 :                 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
   16944                 :             :                                             ACL_CREATE);
   16945         [ -  + ]:          20 :                 if (aclresult != ACLCHECK_OK)
   16946                 :           0 :                     aclcheck_error(aclresult, OBJECT_SCHEMA,
   16947                 :           0 :                                    get_namespace_name(namespaceOid));
   16948                 :             :             }
   16949                 :             :         }
   16950                 :             : 
   16951                 :         444 :         memset(repl_null, false, sizeof(repl_null));
   16952                 :         444 :         memset(repl_repl, false, sizeof(repl_repl));
   16953                 :             : 
   16954                 :         444 :         repl_repl[Anum_pg_class_relowner - 1] = true;
   16955                 :         444 :         repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
   16956                 :             : 
   16957                 :             :         /*
   16958                 :             :          * Determine the modified ACL for the new owner.  This is only
   16959                 :             :          * necessary when the ACL is non-null.
   16960                 :             :          */
   16961                 :         444 :         aclDatum = SysCacheGetAttr(RELOID, tuple,
   16962                 :             :                                    Anum_pg_class_relacl,
   16963                 :             :                                    &isNull);
   16964         [ +  + ]:         444 :         if (!isNull)
   16965                 :             :         {
   16966                 :          60 :             newAcl = aclnewowner(DatumGetAclP(aclDatum),
   16967                 :             :                                  tuple_class->relowner, newOwnerId);
   16968                 :          60 :             repl_repl[Anum_pg_class_relacl - 1] = true;
   16969                 :          60 :             repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
   16970                 :             :         }
   16971                 :             : 
   16972                 :         444 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
   16973                 :             : 
   16974                 :         444 :         CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
   16975                 :             : 
   16976                 :         444 :         heap_freetuple(newtuple);
   16977                 :             : 
   16978                 :             :         /*
   16979                 :             :          * We must similarly update any per-column ACLs to reflect the new
   16980                 :             :          * owner; for neatness reasons that's split out as a subroutine.
   16981                 :             :          */
   16982                 :         444 :         change_owner_fix_column_acls(relationOid,
   16983                 :             :                                      tuple_class->relowner,
   16984                 :             :                                      newOwnerId);
   16985                 :             : 
   16986                 :             :         /*
   16987                 :             :          * Update owner dependency reference, if any.  A composite type has
   16988                 :             :          * none, because it's tracked for the pg_type entry instead of here;
   16989                 :             :          * indexes and TOAST tables don't have their own entries either.
   16990                 :             :          */
   16991         [ +  + ]:         444 :         if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
   16992         [ +  + ]:         439 :             tuple_class->relkind != RELKIND_INDEX &&
   16993         [ +  + ]:         340 :             tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
   16994         [ +  + ]:         326 :             tuple_class->relkind != RELKIND_TOASTVALUE)
   16995                 :         275 :             changeDependencyOnOwner(RelationRelationId, relationOid,
   16996                 :             :                                     newOwnerId);
   16997                 :             : 
   16998                 :             :         /*
   16999                 :             :          * Also change the ownership of the table's row type, if it has one
   17000                 :             :          */
   17001         [ +  + ]:         444 :         if (OidIsValid(tuple_class->reltype))
   17002                 :         255 :             AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
   17003                 :             : 
   17004                 :             :         /*
   17005                 :             :          * If we are operating on a table or materialized view, also change
   17006                 :             :          * the ownership of any indexes and sequences that belong to the
   17007                 :             :          * relation, as well as its toast table (if it has one).
   17008                 :             :          */
   17009         [ +  + ]:         444 :         if (tuple_class->relkind == RELKIND_RELATION ||
   17010         [ +  + ]:         245 :             tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
   17011         [ +  - ]:         208 :             tuple_class->relkind == RELKIND_MATVIEW ||
   17012         [ +  + ]:         208 :             tuple_class->relkind == RELKIND_TOASTVALUE)
   17013                 :             :         {
   17014                 :             :             List       *index_oid_list;
   17015                 :             :             ListCell   *i;
   17016                 :             : 
   17017                 :             :             /* Find all the indexes belonging to this relation */
   17018                 :         287 :             index_oid_list = RelationGetIndexList(target_rel);
   17019                 :             : 
   17020                 :             :             /* For each index, recursively change its ownership */
   17021   [ +  +  +  +  :         400 :             foreach(i, index_oid_list)
                   +  + ]
   17022                 :         113 :                 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
   17023                 :             : 
   17024                 :         287 :             list_free(index_oid_list);
   17025                 :             :         }
   17026                 :             : 
   17027                 :             :         /* If it has a toast table, recurse to change its ownership */
   17028         [ +  + ]:         444 :         if (tuple_class->reltoastrelid != InvalidOid)
   17029                 :          51 :             ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
   17030                 :             :                               true, lockmode);
   17031                 :             : 
   17032                 :             :         /* If it has dependent sequences, recurse to change them too */
   17033                 :         444 :         change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
   17034                 :             :     }
   17035                 :             : 
   17036         [ -  + ]:        1380 :     InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
   17037                 :             : 
   17038                 :        1380 :     ReleaseSysCache(tuple);
   17039                 :        1380 :     table_close(class_rel, RowExclusiveLock);
   17040                 :        1380 :     relation_close(target_rel, NoLock);
   17041                 :        1380 : }
   17042                 :             : 
   17043                 :             : /*
   17044                 :             :  * change_owner_fix_column_acls
   17045                 :             :  *
   17046                 :             :  * Helper function for ATExecChangeOwner.  Scan the columns of the table
   17047                 :             :  * and fix any non-null column ACLs to reflect the new owner.
   17048                 :             :  */
   17049                 :             : static void
   17050                 :         444 : change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
   17051                 :             : {
   17052                 :             :     Relation    attRelation;
   17053                 :             :     SysScanDesc scan;
   17054                 :             :     ScanKeyData key[1];
   17055                 :             :     HeapTuple   attributeTuple;
   17056                 :             : 
   17057                 :         444 :     attRelation = table_open(AttributeRelationId, RowExclusiveLock);
   17058                 :         444 :     ScanKeyInit(&key[0],
   17059                 :             :                 Anum_pg_attribute_attrelid,
   17060                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   17061                 :             :                 ObjectIdGetDatum(relationOid));
   17062                 :         444 :     scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
   17063                 :             :                               true, NULL, 1, key);
   17064         [ +  + ]:        3229 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   17065                 :             :     {
   17066                 :        2785 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   17067                 :             :         Datum       repl_val[Natts_pg_attribute];
   17068                 :             :         bool        repl_null[Natts_pg_attribute];
   17069                 :             :         bool        repl_repl[Natts_pg_attribute];
   17070                 :             :         Acl        *newAcl;
   17071                 :             :         Datum       aclDatum;
   17072                 :             :         bool        isNull;
   17073                 :             :         HeapTuple   newtuple;
   17074                 :             : 
   17075                 :             :         /* Ignore dropped columns */
   17076         [ -  + ]:        2785 :         if (att->attisdropped)
   17077                 :        2784 :             continue;
   17078                 :             : 
   17079                 :        2785 :         aclDatum = heap_getattr(attributeTuple,
   17080                 :             :                                 Anum_pg_attribute_attacl,
   17081                 :             :                                 RelationGetDescr(attRelation),
   17082                 :             :                                 &isNull);
   17083                 :             :         /* Null ACLs do not require changes */
   17084         [ +  + ]:        2785 :         if (isNull)
   17085                 :        2784 :             continue;
   17086                 :             : 
   17087                 :           1 :         memset(repl_null, false, sizeof(repl_null));
   17088                 :           1 :         memset(repl_repl, false, sizeof(repl_repl));
   17089                 :             : 
   17090                 :           1 :         newAcl = aclnewowner(DatumGetAclP(aclDatum),
   17091                 :             :                              oldOwnerId, newOwnerId);
   17092                 :           1 :         repl_repl[Anum_pg_attribute_attacl - 1] = true;
   17093                 :           1 :         repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
   17094                 :             : 
   17095                 :           1 :         newtuple = heap_modify_tuple(attributeTuple,
   17096                 :             :                                      RelationGetDescr(attRelation),
   17097                 :             :                                      repl_val, repl_null, repl_repl);
   17098                 :             : 
   17099                 :           1 :         CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
   17100                 :             : 
   17101                 :           1 :         heap_freetuple(newtuple);
   17102                 :             :     }
   17103                 :         444 :     systable_endscan(scan);
   17104                 :         444 :     table_close(attRelation, RowExclusiveLock);
   17105                 :         444 : }
   17106                 :             : 
   17107                 :             : /*
   17108                 :             :  * change_owner_recurse_to_sequences
   17109                 :             :  *
   17110                 :             :  * Helper function for ATExecChangeOwner.  Examines pg_depend searching
   17111                 :             :  * for sequences that are dependent on serial columns, and changes their
   17112                 :             :  * ownership.
   17113                 :             :  */
   17114                 :             : static void
   17115                 :         444 : change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
   17116                 :             : {
   17117                 :             :     Relation    depRel;
   17118                 :             :     SysScanDesc scan;
   17119                 :             :     ScanKeyData key[2];
   17120                 :             :     HeapTuple   tup;
   17121                 :             : 
   17122                 :             :     /*
   17123                 :             :      * SERIAL sequences are those having an auto dependency on one of the
   17124                 :             :      * table's columns (we don't care *which* column, exactly).
   17125                 :             :      */
   17126                 :         444 :     depRel = table_open(DependRelationId, AccessShareLock);
   17127                 :             : 
   17128                 :         444 :     ScanKeyInit(&key[0],
   17129                 :             :                 Anum_pg_depend_refclassid,
   17130                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   17131                 :             :                 ObjectIdGetDatum(RelationRelationId));
   17132                 :         444 :     ScanKeyInit(&key[1],
   17133                 :             :                 Anum_pg_depend_refobjid,
   17134                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   17135                 :             :                 ObjectIdGetDatum(relationOid));
   17136                 :             :     /* we leave refobjsubid unspecified */
   17137                 :             : 
   17138                 :         444 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   17139                 :             :                               NULL, 2, key);
   17140                 :             : 
   17141         [ +  + ]:        1408 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   17142                 :             :     {
   17143                 :         964 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   17144                 :             :         Relation    seqRel;
   17145                 :             : 
   17146                 :             :         /* skip dependencies other than auto dependencies on columns */
   17147         [ +  + ]:         964 :         if (depForm->refobjsubid == 0 ||
   17148         [ +  + ]:         415 :             depForm->classid != RelationRelationId ||
   17149         [ +  - ]:         139 :             depForm->objsubid != 0 ||
   17150   [ +  +  -  + ]:         139 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   17151                 :         825 :             continue;
   17152                 :             : 
   17153                 :             :         /* Use relation_open just in case it's an index */
   17154                 :         139 :         seqRel = relation_open(depForm->objid, lockmode);
   17155                 :             : 
   17156                 :             :         /* skip non-sequence relations */
   17157         [ +  + ]:         139 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   17158                 :             :         {
   17159                 :             :             /* No need to keep the lock */
   17160                 :         122 :             relation_close(seqRel, lockmode);
   17161                 :         122 :             continue;
   17162                 :             :         }
   17163                 :             : 
   17164                 :             :         /* We don't need to close the sequence while we alter it. */
   17165                 :          17 :         ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
   17166                 :             : 
   17167                 :             :         /* Now we can close it.  Keep the lock till end of transaction. */
   17168                 :          17 :         relation_close(seqRel, NoLock);
   17169                 :             :     }
   17170                 :             : 
   17171                 :         444 :     systable_endscan(scan);
   17172                 :             : 
   17173                 :         444 :     relation_close(depRel, AccessShareLock);
   17174                 :         444 : }
   17175                 :             : 
   17176                 :             : /*
   17177                 :             :  * ALTER TABLE CLUSTER ON
   17178                 :             :  *
   17179                 :             :  * The only thing we have to do is to change the indisclustered bits.
   17180                 :             :  *
   17181                 :             :  * Return the address of the new clustering index.
   17182                 :             :  */
   17183                 :             : static ObjectAddress
   17184                 :          39 : ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
   17185                 :             : {
   17186                 :             :     Oid         indexOid;
   17187                 :             :     ObjectAddress address;
   17188                 :             : 
   17189                 :          39 :     indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
   17190                 :             : 
   17191         [ -  + ]:          39 :     if (!OidIsValid(indexOid))
   17192         [ #  # ]:           0 :         ereport(ERROR,
   17193                 :             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   17194                 :             :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   17195                 :             :                         indexName, RelationGetRelationName(rel))));
   17196                 :             : 
   17197                 :             :     /* Check index is valid to cluster on */
   17198                 :          39 :     check_index_is_clusterable(rel, indexOid, lockmode);
   17199                 :             : 
   17200                 :             :     /* And do the work */
   17201                 :          39 :     mark_index_clustered(rel, indexOid, false);
   17202                 :             : 
   17203                 :          39 :     ObjectAddressSet(address,
   17204                 :             :                      RelationRelationId, indexOid);
   17205                 :             : 
   17206                 :          39 :     return address;
   17207                 :             : }
   17208                 :             : 
   17209                 :             : /*
   17210                 :             :  * ALTER TABLE SET WITHOUT CLUSTER
   17211                 :             :  *
   17212                 :             :  * We have to find any indexes on the table that have indisclustered bit
   17213                 :             :  * set and turn it off.
   17214                 :             :  */
   17215                 :             : static void
   17216                 :           8 : ATExecDropCluster(Relation rel, LOCKMODE lockmode)
   17217                 :             : {
   17218                 :           8 :     mark_index_clustered(rel, InvalidOid, false);
   17219                 :           8 : }
   17220                 :             : 
   17221                 :             : /*
   17222                 :             :  * Preparation phase for SET ACCESS METHOD
   17223                 :             :  *
   17224                 :             :  * Check that the access method exists and determine whether a change is
   17225                 :             :  * actually needed.
   17226                 :             :  */
   17227                 :             : static void
   17228                 :          73 : ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
   17229                 :             : {
   17230                 :             :     Oid         amoid;
   17231                 :             : 
   17232                 :             :     /*
   17233                 :             :      * Look up the access method name and check that it differs from the
   17234                 :             :      * table's current AM.  If DEFAULT was specified for a partitioned table
   17235                 :             :      * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
   17236                 :             :      */
   17237         [ +  + ]:          73 :     if (amname != NULL)
   17238                 :          49 :         amoid = get_table_am_oid(amname, false);
   17239         [ +  + ]:          24 :     else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   17240                 :          12 :         amoid = InvalidOid;
   17241                 :             :     else
   17242                 :          12 :         amoid = get_table_am_oid(default_table_access_method, false);
   17243                 :             : 
   17244                 :             :     /* if it's a match, phase 3 doesn't need to do anything */
   17245         [ +  + ]:          73 :     if (rel->rd_rel->relam == amoid)
   17246                 :           8 :         return;
   17247                 :             : 
   17248                 :             :     /* Save info for Phase 3 to do the real work */
   17249                 :          65 :     tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
   17250                 :          65 :     tab->newAccessMethod = amoid;
   17251                 :          65 :     tab->chgAccessMethod = true;
   17252                 :             : }
   17253                 :             : 
   17254                 :             : /*
   17255                 :             :  * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
   17256                 :             :  * storage that have an interest in preserving AM.
   17257                 :             :  *
   17258                 :             :  * Since these have no storage, setting the access method is a catalog only
   17259                 :             :  * operation.
   17260                 :             :  */
   17261                 :             : static void
   17262                 :          29 : ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
   17263                 :             : {
   17264                 :             :     Relation    pg_class;
   17265                 :             :     Oid         oldAccessMethodId;
   17266                 :             :     HeapTuple   tuple;
   17267                 :             :     Form_pg_class rd_rel;
   17268                 :          29 :     Oid         reloid = RelationGetRelid(rel);
   17269                 :             : 
   17270                 :             :     /*
   17271                 :             :      * Shouldn't be called on relations having storage; these are processed in
   17272                 :             :      * phase 3.
   17273                 :             :      */
   17274                 :             :     Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   17275                 :             : 
   17276                 :             :     /* Get a modifiable copy of the relation's pg_class row. */
   17277                 :          29 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   17278                 :             : 
   17279                 :          29 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
   17280         [ -  + ]:          29 :     if (!HeapTupleIsValid(tuple))
   17281         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", reloid);
   17282                 :          29 :     rd_rel = (Form_pg_class) GETSTRUCT(tuple);
   17283                 :             : 
   17284                 :             :     /* Update the pg_class row. */
   17285                 :          29 :     oldAccessMethodId = rd_rel->relam;
   17286                 :          29 :     rd_rel->relam = newAccessMethodId;
   17287                 :             : 
   17288                 :             :     /* Leave if no update required */
   17289         [ -  + ]:          29 :     if (rd_rel->relam == oldAccessMethodId)
   17290                 :             :     {
   17291                 :           0 :         heap_freetuple(tuple);
   17292                 :           0 :         table_close(pg_class, RowExclusiveLock);
   17293                 :           0 :         return;
   17294                 :             :     }
   17295                 :             : 
   17296                 :          29 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   17297                 :             : 
   17298                 :             :     /*
   17299                 :             :      * Update the dependency on the new access method.  No dependency is added
   17300                 :             :      * if the new access method is InvalidOid (default case).  Be very careful
   17301                 :             :      * that this has to compare the previous value stored in pg_class with the
   17302                 :             :      * new one.
   17303                 :             :      */
   17304   [ +  +  +  - ]:          29 :     if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
   17305                 :          13 :     {
   17306                 :             :         ObjectAddress relobj,
   17307                 :             :                     referenced;
   17308                 :             : 
   17309                 :             :         /*
   17310                 :             :          * New access method is defined and there was no dependency
   17311                 :             :          * previously, so record a new one.
   17312                 :             :          */
   17313                 :          13 :         ObjectAddressSet(relobj, RelationRelationId, reloid);
   17314                 :          13 :         ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
   17315                 :          13 :         recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
   17316                 :             :     }
   17317         [ +  - ]:          16 :     else if (OidIsValid(oldAccessMethodId) &&
   17318         [ +  + ]:          16 :              !OidIsValid(rd_rel->relam))
   17319                 :             :     {
   17320                 :             :         /*
   17321                 :             :          * There was an access method defined, and no new one, so just remove
   17322                 :             :          * the existing dependency.
   17323                 :             :          */
   17324                 :           8 :         deleteDependencyRecordsForClass(RelationRelationId, reloid,
   17325                 :             :                                         AccessMethodRelationId,
   17326                 :             :                                         DEPENDENCY_NORMAL);
   17327                 :             :     }
   17328                 :             :     else
   17329                 :             :     {
   17330                 :             :         Assert(OidIsValid(oldAccessMethodId) &&
   17331                 :             :                OidIsValid(rd_rel->relam));
   17332                 :             : 
   17333                 :             :         /* Both are valid, so update the dependency */
   17334                 :           8 :         changeDependencyFor(RelationRelationId, reloid,
   17335                 :             :                             AccessMethodRelationId,
   17336                 :             :                             oldAccessMethodId, rd_rel->relam);
   17337                 :             :     }
   17338                 :             : 
   17339                 :             :     /* make the relam and dependency changes visible */
   17340                 :          29 :     CommandCounterIncrement();
   17341                 :             : 
   17342         [ -  + ]:          29 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   17343                 :             : 
   17344                 :          29 :     heap_freetuple(tuple);
   17345                 :          29 :     table_close(pg_class, RowExclusiveLock);
   17346                 :             : }
   17347                 :             : 
   17348                 :             : /*
   17349                 :             :  * ALTER TABLE SET TABLESPACE
   17350                 :             :  */
   17351                 :             : static void
   17352                 :         105 : ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
   17353                 :             : {
   17354                 :             :     Oid         tablespaceId;
   17355                 :             : 
   17356                 :             :     /* Check that the tablespace exists */
   17357                 :         105 :     tablespaceId = get_tablespace_oid(tablespacename, false);
   17358                 :             : 
   17359                 :             :     /* Check permissions except when moving to database's default */
   17360   [ +  -  +  + ]:         105 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
   17361                 :             :     {
   17362                 :             :         AclResult   aclresult;
   17363                 :             : 
   17364                 :          48 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
   17365         [ -  + ]:          48 :         if (aclresult != ACLCHECK_OK)
   17366                 :           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
   17367                 :             :     }
   17368                 :             : 
   17369                 :             :     /* Save info for Phase 3 to do the real work */
   17370         [ -  + ]:         105 :     if (OidIsValid(tab->newTableSpace))
   17371         [ #  # ]:           0 :         ereport(ERROR,
   17372                 :             :                 (errcode(ERRCODE_SYNTAX_ERROR),
   17373                 :             :                  errmsg("cannot have multiple SET TABLESPACE subcommands")));
   17374                 :             : 
   17375                 :         105 :     tab->newTableSpace = tablespaceId;
   17376                 :         105 : }
   17377                 :             : 
   17378                 :             : /*
   17379                 :             :  * Set, reset, or replace reloptions.
   17380                 :             :  */
   17381                 :             : static void
   17382                 :         632 : ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
   17383                 :             :                     LOCKMODE lockmode)
   17384                 :             : {
   17385                 :             :     Oid         relid;
   17386                 :             :     Relation    pgclass;
   17387                 :             :     HeapTuple   tuple;
   17388                 :             :     HeapTuple   newtuple;
   17389                 :             :     Datum       datum;
   17390                 :             :     Datum       newOptions;
   17391                 :             :     Datum       repl_val[Natts_pg_class];
   17392                 :             :     bool        repl_null[Natts_pg_class];
   17393                 :             :     bool        repl_repl[Natts_pg_class];
   17394                 :         632 :     const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
   17395                 :             : 
   17396   [ +  +  -  + ]:         632 :     if (defList == NIL && operation != AT_ReplaceRelOptions)
   17397                 :           0 :         return;                 /* nothing to do */
   17398                 :             : 
   17399                 :         632 :     pgclass = table_open(RelationRelationId, RowExclusiveLock);
   17400                 :             : 
   17401                 :             :     /* Fetch heap tuple */
   17402                 :         632 :     relid = RelationGetRelid(rel);
   17403                 :         632 :     tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
   17404         [ -  + ]:         632 :     if (!HeapTupleIsValid(tuple))
   17405         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   17406                 :             : 
   17407         [ +  + ]:         632 :     if (operation == AT_ReplaceRelOptions)
   17408                 :             :     {
   17409                 :             :         /*
   17410                 :             :          * If we're supposed to replace the reloptions list, we just pretend
   17411                 :             :          * there were none before.
   17412                 :             :          */
   17413                 :         137 :         datum = (Datum) 0;
   17414                 :             :     }
   17415                 :             :     else
   17416                 :             :     {
   17417                 :             :         bool        isnull;
   17418                 :             : 
   17419                 :             :         /* Get the old reloptions */
   17420                 :         495 :         datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   17421                 :             :                                 &isnull);
   17422         [ +  + ]:         495 :         if (isnull)
   17423                 :         306 :             datum = (Datum) 0;
   17424                 :             :     }
   17425                 :             : 
   17426                 :             :     /* Generate new proposed reloptions (text array) */
   17427                 :         632 :     newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
   17428                 :             :                                      operation == AT_ResetRelOptions);
   17429                 :             : 
   17430                 :             :     /* Validate */
   17431   [ +  +  +  +  :         628 :     switch (rel->rd_rel->relkind)
                      - ]
   17432                 :             :     {
   17433                 :         348 :         case RELKIND_RELATION:
   17434                 :             :         case RELKIND_MATVIEW:
   17435                 :         348 :             (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
   17436                 :         348 :             break;
   17437                 :           4 :         case RELKIND_PARTITIONED_TABLE:
   17438                 :           4 :             (void) partitioned_table_reloptions(newOptions, true);
   17439                 :           0 :             break;
   17440                 :         205 :         case RELKIND_VIEW:
   17441                 :         205 :             (void) view_reloptions(newOptions, true);
   17442                 :         193 :             break;
   17443                 :          71 :         case RELKIND_INDEX:
   17444                 :             :         case RELKIND_PARTITIONED_INDEX:
   17445                 :          71 :             (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
   17446                 :          57 :             break;
   17447                 :           0 :         case RELKIND_TOASTVALUE:
   17448                 :             :             /* fall through to error -- shouldn't ever get here */
   17449                 :             :         default:
   17450         [ #  # ]:           0 :             ereport(ERROR,
   17451                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17452                 :             :                      errmsg("cannot set options for relation \"%s\"",
   17453                 :             :                             RelationGetRelationName(rel)),
   17454                 :             :                      errdetail_relkind_not_supported(rel->rd_rel->relkind)));
   17455                 :             :             break;
   17456                 :             :     }
   17457                 :             : 
   17458                 :             :     /* Special-case validation of view options */
   17459         [ +  + ]:         598 :     if (rel->rd_rel->relkind == RELKIND_VIEW)
   17460                 :             :     {
   17461                 :         193 :         Query      *view_query = get_view_query(rel);
   17462                 :         193 :         List       *view_options = untransformRelOptions(newOptions);
   17463                 :             :         ListCell   *cell;
   17464                 :         193 :         bool        check_option = false;
   17465                 :             : 
   17466   [ +  +  +  +  :         261 :         foreach(cell, view_options)
                   +  + ]
   17467                 :             :         {
   17468                 :          68 :             DefElem    *defel = (DefElem *) lfirst(cell);
   17469                 :             : 
   17470         [ +  + ]:          68 :             if (strcmp(defel->defname, "check_option") == 0)
   17471                 :          16 :                 check_option = true;
   17472                 :             :         }
   17473                 :             : 
   17474                 :             :         /*
   17475                 :             :          * If the check option is specified, look to see if the view is
   17476                 :             :          * actually auto-updatable or not.
   17477                 :             :          */
   17478         [ +  + ]:         193 :         if (check_option)
   17479                 :             :         {
   17480                 :             :             const char *view_updatable_error =
   17481                 :          16 :                 view_query_is_auto_updatable(view_query, true);
   17482                 :             : 
   17483         [ -  + ]:          16 :             if (view_updatable_error)
   17484         [ #  # ]:           0 :                 ereport(ERROR,
   17485                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   17486                 :             :                          errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
   17487                 :             :                          errhint("%s", _(view_updatable_error))));
   17488                 :             :         }
   17489                 :             :     }
   17490                 :             : 
   17491                 :             :     /*
   17492                 :             :      * All we need do here is update the pg_class row; the new options will be
   17493                 :             :      * propagated into relcaches during post-commit cache inval.
   17494                 :             :      */
   17495                 :         598 :     memset(repl_val, 0, sizeof(repl_val));
   17496                 :         598 :     memset(repl_null, false, sizeof(repl_null));
   17497                 :         598 :     memset(repl_repl, false, sizeof(repl_repl));
   17498                 :             : 
   17499         [ +  + ]:         598 :     if (newOptions != (Datum) 0)
   17500                 :         396 :         repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   17501                 :             :     else
   17502                 :         202 :         repl_null[Anum_pg_class_reloptions - 1] = true;
   17503                 :             : 
   17504                 :         598 :     repl_repl[Anum_pg_class_reloptions - 1] = true;
   17505                 :             : 
   17506                 :         598 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   17507                 :             :                                  repl_val, repl_null, repl_repl);
   17508                 :             : 
   17509                 :         598 :     CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   17510                 :         598 :     UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
   17511                 :             : 
   17512         [ -  + ]:         598 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   17513                 :             : 
   17514                 :         598 :     heap_freetuple(newtuple);
   17515                 :             : 
   17516                 :         598 :     ReleaseSysCache(tuple);
   17517                 :             : 
   17518                 :             :     /* repeat the whole exercise for the toast table, if there's one */
   17519         [ +  + ]:         598 :     if (OidIsValid(rel->rd_rel->reltoastrelid))
   17520                 :             :     {
   17521                 :             :         Relation    toastrel;
   17522                 :         182 :         Oid         toastid = rel->rd_rel->reltoastrelid;
   17523                 :             : 
   17524                 :         182 :         toastrel = table_open(toastid, lockmode);
   17525                 :             : 
   17526                 :             :         /* Fetch heap tuple */
   17527                 :         182 :         tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
   17528         [ -  + ]:         182 :         if (!HeapTupleIsValid(tuple))
   17529         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for relation %u", toastid);
   17530                 :             : 
   17531         [ -  + ]:         182 :         if (operation == AT_ReplaceRelOptions)
   17532                 :             :         {
   17533                 :             :             /*
   17534                 :             :              * If we're supposed to replace the reloptions list, we just
   17535                 :             :              * pretend there were none before.
   17536                 :             :              */
   17537                 :           0 :             datum = (Datum) 0;
   17538                 :             :         }
   17539                 :             :         else
   17540                 :             :         {
   17541                 :             :             bool        isnull;
   17542                 :             : 
   17543                 :             :             /* Get the old reloptions */
   17544                 :         182 :             datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
   17545                 :             :                                     &isnull);
   17546         [ +  + ]:         182 :             if (isnull)
   17547                 :         158 :                 datum = (Datum) 0;
   17548                 :             :         }
   17549                 :             : 
   17550                 :         182 :         newOptions = transformRelOptions(datum, defList, "toast", validnsps,
   17551                 :             :                                          false, operation == AT_ResetRelOptions);
   17552                 :             : 
   17553                 :         182 :         (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
   17554                 :             : 
   17555                 :         182 :         memset(repl_val, 0, sizeof(repl_val));
   17556                 :         182 :         memset(repl_null, false, sizeof(repl_null));
   17557                 :         182 :         memset(repl_repl, false, sizeof(repl_repl));
   17558                 :             : 
   17559         [ +  + ]:         182 :         if (newOptions != (Datum) 0)
   17560                 :          28 :             repl_val[Anum_pg_class_reloptions - 1] = newOptions;
   17561                 :             :         else
   17562                 :         154 :             repl_null[Anum_pg_class_reloptions - 1] = true;
   17563                 :             : 
   17564                 :         182 :         repl_repl[Anum_pg_class_reloptions - 1] = true;
   17565                 :             : 
   17566                 :         182 :         newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
   17567                 :             :                                      repl_val, repl_null, repl_repl);
   17568                 :             : 
   17569                 :         182 :         CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
   17570                 :             : 
   17571         [ -  + ]:         182 :         InvokeObjectPostAlterHookArg(RelationRelationId,
   17572                 :             :                                      RelationGetRelid(toastrel), 0,
   17573                 :             :                                      InvalidOid, true);
   17574                 :             : 
   17575                 :         182 :         heap_freetuple(newtuple);
   17576                 :             : 
   17577                 :         182 :         ReleaseSysCache(tuple);
   17578                 :             : 
   17579                 :         182 :         table_close(toastrel, NoLock);
   17580                 :             :     }
   17581                 :             : 
   17582                 :         598 :     table_close(pgclass, RowExclusiveLock);
   17583                 :             : }
   17584                 :             : 
   17585                 :             : /*
   17586                 :             :  * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
   17587                 :             :  * rewriting to be done, so we just want to copy the data as fast as possible.
   17588                 :             :  */
   17589                 :             : static void
   17590                 :         115 : ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
   17591                 :             : {
   17592                 :             :     Relation    rel;
   17593                 :             :     Oid         reltoastrelid;
   17594                 :             :     RelFileNumber newrelfilenumber;
   17595                 :             :     RelFileLocator newrlocator;
   17596                 :         115 :     List       *reltoastidxids = NIL;
   17597                 :             :     ListCell   *lc;
   17598                 :             : 
   17599                 :             :     /*
   17600                 :             :      * Need lock here in case we are recursing to toast table or index
   17601                 :             :      */
   17602                 :         115 :     rel = relation_open(tableOid, lockmode);
   17603                 :             : 
   17604                 :             :     /* Check first if relation can be moved to new tablespace */
   17605         [ +  + ]:         115 :     if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   17606                 :             :     {
   17607         [ -  + ]:           5 :         InvokeObjectPostAlterHook(RelationRelationId,
   17608                 :             :                                   RelationGetRelid(rel), 0);
   17609                 :           5 :         relation_close(rel, NoLock);
   17610                 :           5 :         return;
   17611                 :             :     }
   17612                 :             : 
   17613                 :         110 :     reltoastrelid = rel->rd_rel->reltoastrelid;
   17614                 :             :     /* Fetch the list of indexes on toast relation if necessary */
   17615         [ +  + ]:         110 :     if (OidIsValid(reltoastrelid))
   17616                 :             :     {
   17617                 :          17 :         Relation    toastRel = relation_open(reltoastrelid, lockmode);
   17618                 :             : 
   17619                 :          17 :         reltoastidxids = RelationGetIndexList(toastRel);
   17620                 :          17 :         relation_close(toastRel, lockmode);
   17621                 :             :     }
   17622                 :             : 
   17623                 :             :     /*
   17624                 :             :      * Relfilenumbers are not unique in databases across tablespaces, so we
   17625                 :             :      * need to allocate a new one in the new tablespace.
   17626                 :             :      */
   17627                 :         110 :     newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
   17628                 :         110 :                                            rel->rd_rel->relpersistence);
   17629                 :             : 
   17630                 :             :     /* Open old and new relation */
   17631                 :         110 :     newrlocator = rel->rd_locator;
   17632                 :         110 :     newrlocator.relNumber = newrelfilenumber;
   17633                 :         110 :     newrlocator.spcOid = newTableSpace;
   17634                 :             : 
   17635                 :             :     /* hand off to AM to actually create new rel storage and copy the data */
   17636         [ +  + ]:         110 :     if (rel->rd_rel->relkind == RELKIND_INDEX)
   17637                 :             :     {
   17638                 :          40 :         index_copy_data(rel, newrlocator);
   17639                 :             :     }
   17640                 :             :     else
   17641                 :             :     {
   17642                 :             :         Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
   17643                 :          70 :         table_relation_copy_data(rel, &newrlocator);
   17644                 :             :     }
   17645                 :             : 
   17646                 :             :     /*
   17647                 :             :      * Update the pg_class row.
   17648                 :             :      *
   17649                 :             :      * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
   17650                 :             :      * executed on pg_class or its indexes (the above copy wouldn't contain
   17651                 :             :      * the updated pg_class entry), but that's forbidden with
   17652                 :             :      * CheckRelationTableSpaceMove().
   17653                 :             :      */
   17654                 :         110 :     SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
   17655                 :             : 
   17656         [ -  + ]:         110 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   17657                 :             : 
   17658                 :         110 :     RelationAssumeNewRelfilelocator(rel);
   17659                 :             : 
   17660                 :         110 :     relation_close(rel, NoLock);
   17661                 :             : 
   17662                 :             :     /* Make sure the reltablespace change is visible */
   17663                 :         110 :     CommandCounterIncrement();
   17664                 :             : 
   17665                 :             :     /* Move associated toast relation and/or indexes, too */
   17666         [ +  + ]:         110 :     if (OidIsValid(reltoastrelid))
   17667                 :          17 :         ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
   17668   [ +  +  +  +  :         127 :     foreach(lc, reltoastidxids)
                   +  + ]
   17669                 :          17 :         ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
   17670                 :             : 
   17671                 :             :     /* Clean up */
   17672                 :         110 :     list_free(reltoastidxids);
   17673                 :             : }
   17674                 :             : 
   17675                 :             : /*
   17676                 :             :  * Special handling of ALTER TABLE SET TABLESPACE for relations with no
   17677                 :             :  * storage that have an interest in preserving tablespace.
   17678                 :             :  *
   17679                 :             :  * Since these have no storage the tablespace can be updated with a simple
   17680                 :             :  * metadata only operation to update the tablespace.
   17681                 :             :  */
   17682                 :             : static void
   17683                 :          24 : ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
   17684                 :             : {
   17685                 :             :     /*
   17686                 :             :      * Shouldn't be called on relations having storage; these are processed in
   17687                 :             :      * phase 3.
   17688                 :             :      */
   17689                 :             :     Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
   17690                 :             : 
   17691                 :             :     /* check if relation can be moved to its new tablespace */
   17692         [ -  + ]:          24 :     if (!CheckRelationTableSpaceMove(rel, newTableSpace))
   17693                 :             :     {
   17694         [ #  # ]:           0 :         InvokeObjectPostAlterHook(RelationRelationId,
   17695                 :             :                                   RelationGetRelid(rel),
   17696                 :             :                                   0);
   17697                 :           0 :         return;
   17698                 :             :     }
   17699                 :             : 
   17700                 :             :     /* Update can be done, so change reltablespace */
   17701                 :          20 :     SetRelationTableSpace(rel, newTableSpace, InvalidOid);
   17702                 :             : 
   17703         [ -  + ]:          20 :     InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
   17704                 :             : 
   17705                 :             :     /* Make sure the reltablespace change is visible */
   17706                 :          20 :     CommandCounterIncrement();
   17707                 :             : }
   17708                 :             : 
   17709                 :             : /*
   17710                 :             :  * Alter Table ALL ... SET TABLESPACE
   17711                 :             :  *
   17712                 :             :  * Allows a user to move all objects of some type in a given tablespace in the
   17713                 :             :  * current database to another tablespace.  Objects can be chosen based on the
   17714                 :             :  * owner of the object also, to allow users to move only their objects.
   17715                 :             :  * The user must have CREATE rights on the new tablespace, as usual.   The main
   17716                 :             :  * permissions handling is done by the lower-level table move function.
   17717                 :             :  *
   17718                 :             :  * All to-be-moved objects are locked first. If NOWAIT is specified and the
   17719                 :             :  * lock can't be acquired then we ereport(ERROR).
   17720                 :             :  */
   17721                 :             : Oid
   17722                 :          16 : AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
   17723                 :             : {
   17724                 :          16 :     List       *relations = NIL;
   17725                 :             :     ListCell   *l;
   17726                 :             :     ScanKeyData key[1];
   17727                 :             :     Relation    rel;
   17728                 :             :     TableScanDesc scan;
   17729                 :             :     HeapTuple   tuple;
   17730                 :             :     Oid         orig_tablespaceoid;
   17731                 :             :     Oid         new_tablespaceoid;
   17732                 :          16 :     List       *role_oids = roleSpecsToIds(stmt->roles);
   17733                 :             : 
   17734                 :             :     /* Ensure we were not asked to move something we can't */
   17735   [ +  +  +  + ]:          16 :     if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
   17736         [ -  + ]:           6 :         stmt->objtype != OBJECT_MATVIEW)
   17737         [ #  # ]:           0 :         ereport(ERROR,
   17738                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   17739                 :             :                  errmsg("only tables, indexes, and materialized views exist in tablespaces")));
   17740                 :             : 
   17741                 :             :     /* Get the orig and new tablespace OIDs */
   17742                 :          16 :     orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
   17743                 :          16 :     new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
   17744                 :             : 
   17745                 :             :     /* Can't move shared relations in to or out of pg_global */
   17746                 :             :     /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
   17747   [ +  -  -  + ]:          16 :     if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
   17748                 :             :         new_tablespaceoid == GLOBALTABLESPACE_OID)
   17749         [ #  # ]:           0 :         ereport(ERROR,
   17750                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   17751                 :             :                  errmsg("cannot move relations in to or out of pg_global tablespace")));
   17752                 :             : 
   17753                 :             :     /*
   17754                 :             :      * Must have CREATE rights on the new tablespace, unless it is the
   17755                 :             :      * database default tablespace (which all users implicitly have CREATE
   17756                 :             :      * rights on).
   17757                 :             :      */
   17758   [ +  -  +  + ]:          16 :     if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
   17759                 :             :     {
   17760                 :             :         AclResult   aclresult;
   17761                 :             : 
   17762                 :           1 :         aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
   17763                 :             :                                     ACL_CREATE);
   17764         [ -  + ]:           1 :         if (aclresult != ACLCHECK_OK)
   17765                 :           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
   17766                 :           0 :                            get_tablespace_name(new_tablespaceoid));
   17767                 :             :     }
   17768                 :             : 
   17769                 :             :     /*
   17770                 :             :      * Now that the checks are done, check if we should set either to
   17771                 :             :      * InvalidOid because it is our database's default tablespace.
   17772                 :             :      */
   17773         [ +  + ]:          16 :     if (orig_tablespaceoid == MyDatabaseTableSpace)
   17774                 :           1 :         orig_tablespaceoid = InvalidOid;
   17775                 :             : 
   17776         [ +  + ]:          16 :     if (new_tablespaceoid == MyDatabaseTableSpace)
   17777                 :          15 :         new_tablespaceoid = InvalidOid;
   17778                 :             : 
   17779                 :             :     /* no-op */
   17780         [ -  + ]:          16 :     if (orig_tablespaceoid == new_tablespaceoid)
   17781                 :           0 :         return new_tablespaceoid;
   17782                 :             : 
   17783                 :             :     /*
   17784                 :             :      * Walk the list of objects in the tablespace and move them. This will
   17785                 :             :      * only find objects in our database, of course.
   17786                 :             :      */
   17787                 :          16 :     ScanKeyInit(&key[0],
   17788                 :             :                 Anum_pg_class_reltablespace,
   17789                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   17790                 :             :                 ObjectIdGetDatum(orig_tablespaceoid));
   17791                 :             : 
   17792                 :          16 :     rel = table_open(RelationRelationId, AccessShareLock);
   17793                 :          16 :     scan = table_beginscan_catalog(rel, 1, key);
   17794         [ +  + ]:         478 :     while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
   17795                 :             :     {
   17796                 :         462 :         Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
   17797                 :         462 :         Oid         relOid = relForm->oid;
   17798                 :             : 
   17799                 :             :         /*
   17800                 :             :          * Do not move objects in pg_catalog as part of this, if an admin
   17801                 :             :          * really wishes to do so, they can issue the individual ALTER
   17802                 :             :          * commands directly.
   17803                 :             :          *
   17804                 :             :          * Also, explicitly avoid any shared tables, temp tables, TOAST (TOAST
   17805                 :             :          * will be moved with the main table).
   17806                 :             :          *
   17807                 :             :          * Conflict log tables are system-managed for logical replication and
   17808                 :             :          * cannot be altered directly, so skip them as well; otherwise a
   17809                 :             :          * single such table in the source tablespace would abort the whole
   17810                 :             :          * bulk move.
   17811                 :             :          */
   17812         [ +  + ]:         462 :         if (IsCatalogNamespace(relForm->relnamespace) ||
   17813   [ +  -  +  - ]:         404 :             relForm->relisshared ||
   17814         [ +  + ]:         404 :             isAnyTempNamespace(relForm->relnamespace) ||
   17815         [ +  + ]:         334 :             IsToastNamespace(relForm->relnamespace) ||
   17816                 :         132 :             IsConflictLogTableNamespace(relForm->relnamespace))
   17817                 :         331 :             continue;
   17818                 :             : 
   17819                 :             :         /* Only move the object type requested */
   17820         [ +  + ]:         131 :         if ((stmt->objtype == OBJECT_TABLE &&
   17821         [ +  + ]:         110 :              relForm->relkind != RELKIND_RELATION &&
   17822         [ -  + ]:          93 :              relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
   17823         [ +  + ]:          38 :             (stmt->objtype == OBJECT_INDEX &&
   17824         [ +  + ]:          18 :              relForm->relkind != RELKIND_INDEX &&
   17825         [ -  + ]:           3 :              relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
   17826         [ +  + ]:          35 :             (stmt->objtype == OBJECT_MATVIEW &&
   17827         [ -  + ]:           3 :              relForm->relkind != RELKIND_MATVIEW))
   17828                 :          96 :             continue;
   17829                 :             : 
   17830                 :             :         /* Check if we are only moving objects owned by certain roles */
   17831   [ -  +  -  - ]:          35 :         if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
   17832                 :           0 :             continue;
   17833                 :             : 
   17834                 :             :         /*
   17835                 :             :          * Handle permissions-checking here since we are locking the tables
   17836                 :             :          * and also to avoid doing a bunch of work only to fail part-way. Note
   17837                 :             :          * that permissions will also be checked by AlterTableInternal().
   17838                 :             :          *
   17839                 :             :          * Caller must be considered an owner on the table to move it.
   17840                 :             :          */
   17841         [ -  + ]:          35 :         if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
   17842                 :           0 :             aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
   17843                 :           0 :                            NameStr(relForm->relname));
   17844                 :             : 
   17845         [ -  + ]:          35 :         if (stmt->nowait &&
   17846         [ #  # ]:           0 :             !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
   17847         [ #  # ]:           0 :             ereport(ERROR,
   17848                 :             :                     (errcode(ERRCODE_OBJECT_IN_USE),
   17849                 :             :                      errmsg("aborting because lock on relation \"%s.%s\" is not available",
   17850                 :             :                             get_namespace_name(relForm->relnamespace),
   17851                 :             :                             NameStr(relForm->relname))));
   17852                 :             :         else
   17853                 :          35 :             LockRelationOid(relOid, AccessExclusiveLock);
   17854                 :             : 
   17855                 :             :         /* Add to our list of objects to move */
   17856                 :          35 :         relations = lappend_oid(relations, relOid);
   17857                 :             :     }
   17858                 :             : 
   17859                 :          16 :     table_endscan(scan);
   17860                 :          16 :     table_close(rel, AccessShareLock);
   17861                 :             : 
   17862         [ +  + ]:          16 :     if (relations == NIL)
   17863   [ +  -  +  - ]:           6 :         ereport(NOTICE,
   17864                 :             :                 (errcode(ERRCODE_NO_DATA_FOUND),
   17865                 :             :                  errmsg("no matching relations in tablespace \"%s\" found",
   17866                 :             :                         orig_tablespaceoid == InvalidOid ? "(database default)" :
   17867                 :             :                         get_tablespace_name(orig_tablespaceoid))));
   17868                 :             : 
   17869                 :             :     /* Everything is locked, loop through and move all of the relations. */
   17870   [ +  +  +  +  :          51 :     foreach(l, relations)
                   +  + ]
   17871                 :             :     {
   17872                 :          35 :         List       *cmds = NIL;
   17873                 :          35 :         AlterTableCmd *cmd = makeNode(AlterTableCmd);
   17874                 :             : 
   17875                 :          35 :         cmd->subtype = AT_SetTableSpace;
   17876                 :          35 :         cmd->name = stmt->new_tablespacename;
   17877                 :             : 
   17878                 :          35 :         cmds = lappend(cmds, cmd);
   17879                 :             : 
   17880                 :          35 :         EventTriggerAlterTableStart((Node *) stmt);
   17881                 :             :         /* OID is set by AlterTableInternal */
   17882                 :          35 :         AlterTableInternal(lfirst_oid(l), cmds, false);
   17883                 :          35 :         EventTriggerAlterTableEnd();
   17884                 :             :     }
   17885                 :             : 
   17886                 :          16 :     return new_tablespaceoid;
   17887                 :             : }
   17888                 :             : 
   17889                 :             : static void
   17890                 :          40 : index_copy_data(Relation rel, RelFileLocator newrlocator)
   17891                 :             : {
   17892                 :             :     SMgrRelation dstrel;
   17893                 :             : 
   17894                 :             :     /*
   17895                 :             :      * Since we copy the file directly without looking at the shared buffers,
   17896                 :             :      * we'd better first flush out any pages of the source relation that are
   17897                 :             :      * in shared buffers.  We assume no new changes will be made while we are
   17898                 :             :      * holding exclusive lock on the rel.
   17899                 :             :      */
   17900                 :          40 :     FlushRelationBuffers(rel);
   17901                 :             : 
   17902                 :             :     /*
   17903                 :             :      * Create and copy all forks of the relation, and schedule unlinking of
   17904                 :             :      * old physical files.
   17905                 :             :      *
   17906                 :             :      * NOTE: any conflict in relfilenumber value will be caught in
   17907                 :             :      * RelationCreateStorage().
   17908                 :             :      */
   17909                 :          40 :     dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
   17910                 :             : 
   17911                 :             :     /* copy main fork */
   17912                 :          40 :     RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
   17913                 :          40 :                         rel->rd_rel->relpersistence);
   17914                 :             : 
   17915                 :             :     /* copy those extra forks that exist */
   17916                 :          40 :     for (ForkNumber forkNum = MAIN_FORKNUM + 1;
   17917         [ +  + ]:         160 :          forkNum <= MAX_FORKNUM; forkNum++)
   17918                 :             :     {
   17919         [ -  + ]:         120 :         if (smgrexists(RelationGetSmgr(rel), forkNum))
   17920                 :             :         {
   17921                 :           0 :             smgrcreate(dstrel, forkNum, false);
   17922                 :             : 
   17923                 :             :             /*
   17924                 :             :              * WAL log creation if the relation is persistent, or this is the
   17925                 :             :              * init fork of an unlogged relation.
   17926                 :             :              */
   17927         [ #  # ]:           0 :             if (RelationIsPermanent(rel) ||
   17928   [ #  #  #  # ]:           0 :                 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
   17929                 :             :                  forkNum == INIT_FORKNUM))
   17930                 :           0 :                 log_smgrcreate(&newrlocator, forkNum);
   17931                 :           0 :             RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
   17932                 :           0 :                                 rel->rd_rel->relpersistence);
   17933                 :             :         }
   17934                 :             :     }
   17935                 :             : 
   17936                 :             :     /* drop old relation, and close new one */
   17937                 :          40 :     RelationDropStorage(rel);
   17938                 :          40 :     smgrclose(dstrel);
   17939                 :          40 : }
   17940                 :             : 
   17941                 :             : /*
   17942                 :             :  * ALTER TABLE ENABLE/DISABLE TRIGGER
   17943                 :             :  *
   17944                 :             :  * We just pass this off to trigger.c.
   17945                 :             :  */
   17946                 :             : static void
   17947                 :         191 : ATExecEnableDisableTrigger(Relation rel, const char *trigname,
   17948                 :             :                            char fires_when, bool skip_system, bool recurse,
   17949                 :             :                            LOCKMODE lockmode)
   17950                 :             : {
   17951                 :         191 :     EnableDisableTrigger(rel, trigname, InvalidOid,
   17952                 :             :                          fires_when, skip_system, recurse,
   17953                 :             :                          lockmode);
   17954                 :             : 
   17955         [ +  + ]:         191 :     InvokeObjectPostAlterHook(RelationRelationId,
   17956                 :             :                               RelationGetRelid(rel), 0);
   17957                 :         191 : }
   17958                 :             : 
   17959                 :             : /*
   17960                 :             :  * ALTER TABLE ENABLE/DISABLE RULE
   17961                 :             :  *
   17962                 :             :  * We just pass this off to rewriteDefine.c.
   17963                 :             :  */
   17964                 :             : static void
   17965                 :          29 : ATExecEnableDisableRule(Relation rel, const char *rulename,
   17966                 :             :                         char fires_when, LOCKMODE lockmode)
   17967                 :             : {
   17968                 :          29 :     EnableDisableRule(rel, rulename, fires_when);
   17969                 :             : 
   17970         [ +  + ]:          29 :     InvokeObjectPostAlterHook(RelationRelationId,
   17971                 :             :                               RelationGetRelid(rel), 0);
   17972                 :          29 : }
   17973                 :             : 
   17974                 :             : /*
   17975                 :             :  * Preparation phase of [NO] INHERIT
   17976                 :             :  *
   17977                 :             :  * Check the relation defined as a child.
   17978                 :             :  */
   17979                 :             : static void
   17980                 :         386 : ATPrepChangeInherit(Relation child_rel)
   17981                 :             : {
   17982         [ +  + ]:         386 :     if (child_rel->rd_rel->reloftype)
   17983         [ +  - ]:           8 :         ereport(ERROR,
   17984                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17985                 :             :                  errmsg("cannot change inheritance of typed table")));
   17986                 :             : 
   17987         [ +  + ]:         378 :     if (child_rel->rd_rel->relispartition)
   17988         [ +  - ]:          12 :         ereport(ERROR,
   17989                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   17990                 :             :                  errmsg("cannot change inheritance of a partition")));
   17991                 :         366 : }
   17992                 :             : 
   17993                 :             : /*
   17994                 :             :  * ALTER TABLE INHERIT
   17995                 :             :  *
   17996                 :             :  * Return the address of the new parent relation.
   17997                 :             :  */
   17998                 :             : static ObjectAddress
   17999                 :         297 : ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
   18000                 :             : {
   18001                 :             :     Relation    parent_rel;
   18002                 :             :     List       *children;
   18003                 :             :     ObjectAddress address;
   18004                 :             :     const char *trigger_name;
   18005                 :             : 
   18006                 :             :     /*
   18007                 :             :      * A self-exclusive lock is needed here.  See the similar case in
   18008                 :             :      * MergeAttributes() for a full explanation.
   18009                 :             :      */
   18010                 :         297 :     parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
   18011                 :             : 
   18012                 :             :     /*
   18013                 :             :      * Must be owner of both parent and child -- child was checked by
   18014                 :             :      * ATSimplePermissions call in ATPrepCmd
   18015                 :             :      */
   18016                 :         297 :     ATSimplePermissions(AT_AddInherit, parent_rel,
   18017                 :             :                         ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   18018                 :             : 
   18019                 :             :     /* Permanent rels cannot inherit from temporary ones */
   18020         [ +  + ]:         297 :     if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   18021         [ -  + ]:           4 :         child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   18022         [ #  # ]:           0 :         ereport(ERROR,
   18023                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18024                 :             :                  errmsg("cannot inherit from temporary relation \"%s\"",
   18025                 :             :                         RelationGetRelationName(parent_rel))));
   18026                 :             : 
   18027                 :             :     /* If parent rel is temp, it must belong to this session */
   18028   [ +  +  -  + ]:         297 :     if (RELATION_IS_OTHER_TEMP(parent_rel))
   18029         [ #  # ]:           0 :         ereport(ERROR,
   18030                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18031                 :             :                  errmsg("cannot inherit from temporary relation of another session")));
   18032                 :             : 
   18033                 :             :     /* Ditto for the child */
   18034   [ +  +  -  + ]:         297 :     if (RELATION_IS_OTHER_TEMP(child_rel))
   18035         [ #  # ]:           0 :         ereport(ERROR,
   18036                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18037                 :             :                  errmsg("cannot inherit to temporary relation of another session")));
   18038                 :             : 
   18039                 :             :     /* Prevent partitioned tables from becoming inheritance parents */
   18040         [ +  + ]:         297 :     if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   18041         [ +  - ]:           4 :         ereport(ERROR,
   18042                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18043                 :             :                  errmsg("cannot inherit from partitioned table \"%s\"",
   18044                 :             :                         parent->relname)));
   18045                 :             : 
   18046                 :             :     /* Likewise for partitions */
   18047         [ +  + ]:         293 :     if (parent_rel->rd_rel->relispartition)
   18048         [ +  - ]:           4 :         ereport(ERROR,
   18049                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18050                 :             :                  errmsg("cannot inherit from a partition")));
   18051                 :             : 
   18052                 :             :     /*
   18053                 :             :      * Prevent circularity by seeing if proposed parent inherits from child.
   18054                 :             :      * (In particular, this disallows making a rel inherit from itself.)
   18055                 :             :      *
   18056                 :             :      * This is not completely bulletproof because of race conditions: in
   18057                 :             :      * multi-level inheritance trees, someone else could concurrently be
   18058                 :             :      * making another inheritance link that closes the loop but does not join
   18059                 :             :      * either of the rels we have locked.  Preventing that seems to require
   18060                 :             :      * exclusive locks on the entire inheritance tree, which is a cure worse
   18061                 :             :      * than the disease.  find_all_inheritors() will cope with circularity
   18062                 :             :      * anyway, so don't sweat it too much.
   18063                 :             :      *
   18064                 :             :      * We use weakest lock we can on child's children, namely AccessShareLock.
   18065                 :             :      */
   18066                 :         289 :     children = find_all_inheritors(RelationGetRelid(child_rel),
   18067                 :             :                                    AccessShareLock, NULL);
   18068                 :             : 
   18069         [ +  + ]:         289 :     if (list_member_oid(children, RelationGetRelid(parent_rel)))
   18070         [ +  - ]:           8 :         ereport(ERROR,
   18071                 :             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   18072                 :             :                  errmsg("circular inheritance not allowed"),
   18073                 :             :                  errdetail("\"%s\" is already a child of \"%s\".",
   18074                 :             :                            parent->relname,
   18075                 :             :                            RelationGetRelationName(child_rel))));
   18076                 :             : 
   18077                 :             :     /*
   18078                 :             :      * If child_rel has row-level triggers with transition tables, we
   18079                 :             :      * currently don't allow it to become an inheritance child.  See also
   18080                 :             :      * prohibitions in ATExecAttachPartition() and CreateTrigger().
   18081                 :             :      */
   18082                 :         281 :     trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
   18083         [ +  + ]:         281 :     if (trigger_name != NULL)
   18084         [ +  - ]:           4 :         ereport(ERROR,
   18085                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   18086                 :             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
   18087                 :             :                         trigger_name, RelationGetRelationName(child_rel)),
   18088                 :             :                  errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
   18089                 :             : 
   18090                 :             :     /* OK to create inheritance */
   18091                 :         277 :     CreateInheritance(child_rel, parent_rel, false);
   18092                 :             : 
   18093                 :         217 :     ObjectAddressSet(address, RelationRelationId,
   18094                 :             :                      RelationGetRelid(parent_rel));
   18095                 :             : 
   18096                 :             :     /* keep our lock on the parent relation until commit */
   18097                 :         217 :     table_close(parent_rel, NoLock);
   18098                 :             : 
   18099                 :         217 :     return address;
   18100                 :             : }
   18101                 :             : 
   18102                 :             : /*
   18103                 :             :  * CreateInheritance
   18104                 :             :  *      Catalog manipulation portion of creating inheritance between a child
   18105                 :             :  *      table and a parent table.
   18106                 :             :  *
   18107                 :             :  * This verifies that all the columns and check constraints of the parent
   18108                 :             :  * appear in the child and that they have the same data types and expressions.
   18109                 :             :  *
   18110                 :             :  * Common to ATExecAddInherit() and ATExecAttachPartition().
   18111                 :             :  */
   18112                 :             : static void
   18113                 :        2262 : CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
   18114                 :             : {
   18115                 :             :     Relation    catalogRelation;
   18116                 :             :     SysScanDesc scan;
   18117                 :             :     ScanKeyData key;
   18118                 :             :     HeapTuple   inheritsTuple;
   18119                 :             :     int32       inhseqno;
   18120                 :             : 
   18121                 :             :     /* Note: get RowExclusiveLock because we will write pg_inherits below. */
   18122                 :        2262 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   18123                 :             : 
   18124                 :             :     /*
   18125                 :             :      * Check for duplicates in the list of parents, and determine the highest
   18126                 :             :      * inhseqno already present; we'll use the next one for the new parent.
   18127                 :             :      * Also, if proposed child is a partition, it cannot already be
   18128                 :             :      * inheriting.
   18129                 :             :      *
   18130                 :             :      * Note: we do not reject the case where the child already inherits from
   18131                 :             :      * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
   18132                 :             :      */
   18133                 :        2262 :     ScanKeyInit(&key,
   18134                 :             :                 Anum_pg_inherits_inhrelid,
   18135                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   18136                 :             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   18137                 :        2262 :     scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
   18138                 :             :                               true, NULL, 1, &key);
   18139                 :             : 
   18140                 :             :     /* inhseqno sequences start at 1 */
   18141                 :        2262 :     inhseqno = 0;
   18142         [ +  + ]:        2313 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   18143                 :             :     {
   18144                 :          55 :         Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   18145                 :             : 
   18146         [ +  + ]:          55 :         if (inh->inhparent == RelationGetRelid(parent_rel))
   18147         [ +  - ]:           4 :             ereport(ERROR,
   18148                 :             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   18149                 :             :                      errmsg("relation \"%s\" would be inherited from more than once",
   18150                 :             :                             RelationGetRelationName(parent_rel))));
   18151                 :             : 
   18152         [ +  - ]:          51 :         if (inh->inhseqno > inhseqno)
   18153                 :          51 :             inhseqno = inh->inhseqno;
   18154                 :             :     }
   18155                 :        2258 :     systable_endscan(scan);
   18156                 :             : 
   18157                 :             :     /* Match up the columns and bump attinhcount as needed */
   18158                 :        2258 :     MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
   18159                 :             : 
   18160                 :             :     /* Match up the constraints and bump coninhcount as needed */
   18161                 :        2170 :     MergeConstraintsIntoExisting(child_rel, parent_rel);
   18162                 :             : 
   18163                 :             :     /*
   18164                 :             :      * OK, it looks valid.  Make the catalog entries that show inheritance.
   18165                 :             :      */
   18166                 :        2130 :     StoreCatalogInheritance1(RelationGetRelid(child_rel),
   18167                 :             :                              RelationGetRelid(parent_rel),
   18168                 :             :                              inhseqno + 1,
   18169                 :             :                              catalogRelation,
   18170                 :        2130 :                              parent_rel->rd_rel->relkind ==
   18171                 :             :                              RELKIND_PARTITIONED_TABLE);
   18172                 :             : 
   18173                 :             :     /* Now we're done with pg_inherits */
   18174                 :        2130 :     table_close(catalogRelation, RowExclusiveLock);
   18175                 :        2130 : }
   18176                 :             : 
   18177                 :             : /*
   18178                 :             :  * Obtain the source-text form of the constraint expression for a check
   18179                 :             :  * constraint, given its pg_constraint tuple
   18180                 :             :  */
   18181                 :             : static char *
   18182                 :         600 : decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
   18183                 :             : {
   18184                 :             :     Form_pg_constraint con;
   18185                 :             :     bool        isnull;
   18186                 :             :     Datum       attr;
   18187                 :             :     Datum       expr;
   18188                 :             : 
   18189                 :         600 :     con = (Form_pg_constraint) GETSTRUCT(contup);
   18190                 :         600 :     attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
   18191         [ -  + ]:         600 :     if (isnull)
   18192         [ #  # ]:           0 :         elog(ERROR, "null conbin for constraint %u", con->oid);
   18193                 :             : 
   18194                 :         600 :     expr = DirectFunctionCall2(pg_get_expr, attr,
   18195                 :             :                                ObjectIdGetDatum(con->conrelid));
   18196                 :         600 :     return TextDatumGetCString(expr);
   18197                 :             : }
   18198                 :             : 
   18199                 :             : /*
   18200                 :             :  * Determine whether two check constraints are functionally equivalent
   18201                 :             :  *
   18202                 :             :  * The test we apply is to see whether they reverse-compile to the same
   18203                 :             :  * source string.  This insulates us from issues like whether attributes
   18204                 :             :  * have the same physical column numbers in parent and child relations.
   18205                 :             :  *
   18206                 :             :  * Note that we ignore enforceability as there are cases where constraints
   18207                 :             :  * with differing enforceability are allowed.
   18208                 :             :  */
   18209                 :             : static bool
   18210                 :         300 : constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
   18211                 :             : {
   18212                 :         300 :     Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
   18213                 :         300 :     Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
   18214                 :             : 
   18215         [ +  - ]:         300 :     if (acon->condeferrable != bcon->condeferrable ||
   18216         [ +  - ]:         300 :         acon->condeferred != bcon->condeferred ||
   18217         [ +  + ]:         300 :         strcmp(decompile_conbin(a, tupleDesc),
   18218                 :         300 :                decompile_conbin(b, tupleDesc)) != 0)
   18219                 :           4 :         return false;
   18220                 :             :     else
   18221                 :         296 :         return true;
   18222                 :             : }
   18223                 :             : 
   18224                 :             : /*
   18225                 :             :  * Check columns in child table match up with columns in parent, and increment
   18226                 :             :  * their attinhcount.
   18227                 :             :  *
   18228                 :             :  * Called by CreateInheritance
   18229                 :             :  *
   18230                 :             :  * Currently all parent columns must be found in child. Missing columns are an
   18231                 :             :  * error.  One day we might consider creating new columns like CREATE TABLE
   18232                 :             :  * does.  However, that is widely unpopular --- in the common use case of
   18233                 :             :  * partitioned tables it's a foot-gun.
   18234                 :             :  *
   18235                 :             :  * The data type must match exactly. If the parent column is NOT NULL then
   18236                 :             :  * the child must be as well. Defaults are not compared, however.
   18237                 :             :  */
   18238                 :             : static void
   18239                 :        2258 : MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
   18240                 :             : {
   18241                 :             :     Relation    attrrel;
   18242                 :             :     TupleDesc   parent_desc;
   18243                 :             : 
   18244                 :        2258 :     attrrel = table_open(AttributeRelationId, RowExclusiveLock);
   18245                 :        2258 :     parent_desc = RelationGetDescr(parent_rel);
   18246                 :             : 
   18247         [ +  + ]:        7378 :     for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
   18248                 :             :     {
   18249                 :        5208 :         Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
   18250                 :        5208 :         char       *parent_attname = NameStr(parent_att->attname);
   18251                 :             :         HeapTuple   tuple;
   18252                 :             : 
   18253                 :             :         /* Ignore dropped columns in the parent. */
   18254         [ +  + ]:        5208 :         if (parent_att->attisdropped)
   18255                 :         180 :             continue;
   18256                 :             : 
   18257                 :             :         /* Find same column in child (matching on column name). */
   18258                 :        5028 :         tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
   18259         [ +  + ]:        5028 :         if (HeapTupleIsValid(tuple))
   18260                 :             :         {
   18261                 :        5020 :             Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
   18262                 :             : 
   18263         [ +  + ]:        5020 :             if (parent_att->atttypid != child_att->atttypid ||
   18264         [ +  + ]:        5016 :                 parent_att->atttypmod != child_att->atttypmod)
   18265         [ +  - ]:           8 :                 ereport(ERROR,
   18266                 :             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18267                 :             :                          errmsg("child table \"%s\" has different type for column \"%s\"",
   18268                 :             :                                 RelationGetRelationName(child_rel), parent_attname)));
   18269                 :             : 
   18270         [ +  + ]:        5012 :             if (parent_att->attcollation != child_att->attcollation)
   18271         [ +  - ]:           4 :                 ereport(ERROR,
   18272                 :             :                         (errcode(ERRCODE_COLLATION_MISMATCH),
   18273                 :             :                          errmsg("child table \"%s\" has different collation for column \"%s\"",
   18274                 :             :                                 RelationGetRelationName(child_rel), parent_attname)));
   18275                 :             : 
   18276                 :             :             /*
   18277                 :             :              * If the parent has a not-null constraint that's not NO INHERIT,
   18278                 :             :              * make sure the child has one too.
   18279                 :             :              *
   18280                 :             :              * Other constraints are checked elsewhere.
   18281                 :             :              */
   18282   [ +  +  +  + ]:        5008 :             if (parent_att->attnotnull && !child_att->attnotnull)
   18283                 :             :             {
   18284                 :             :                 HeapTuple   contup;
   18285                 :             : 
   18286                 :          32 :                 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
   18287                 :          32 :                                                      parent_att->attnum);
   18288         [ +  - ]:          32 :                 if (HeapTupleIsValid(contup) &&
   18289         [ +  + ]:          32 :                     !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
   18290         [ +  - ]:          20 :                     ereport(ERROR,
   18291                 :             :                             errcode(ERRCODE_DATATYPE_MISMATCH),
   18292                 :             :                             errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
   18293                 :             :                                    parent_attname, RelationGetRelationName(child_rel)));
   18294                 :             :             }
   18295                 :             : 
   18296                 :             :             /*
   18297                 :             :              * Child column must be generated if and only if parent column is.
   18298                 :             :              */
   18299   [ +  +  +  + ]:        4988 :             if (parent_att->attgenerated && !child_att->attgenerated)
   18300         [ +  - ]:          24 :                 ereport(ERROR,
   18301                 :             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18302                 :             :                          errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
   18303   [ +  +  +  + ]:        4964 :             if (child_att->attgenerated && !parent_att->attgenerated)
   18304         [ +  - ]:          16 :                 ereport(ERROR,
   18305                 :             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18306                 :             :                          errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
   18307                 :             : 
   18308   [ +  +  +  -  :        4948 :             if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
                   +  + ]
   18309   [ +  -  +  +  :           8 :                 ereport(ERROR,
                   +  + ]
   18310                 :             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18311                 :             :                          errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
   18312                 :             :                          errdetail("Parent column is %s, child column is %s.",
   18313                 :             :                                    parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
   18314                 :             :                                    child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
   18315                 :             : 
   18316                 :             :             /*
   18317                 :             :              * Regular inheritance children are independent enough not to
   18318                 :             :              * inherit identity columns.  But partitions are integral part of
   18319                 :             :              * a partitioned table and inherit identity column.
   18320                 :             :              */
   18321         [ +  + ]:        4940 :             if (ispartition)
   18322                 :        4453 :                 child_att->attidentity = parent_att->attidentity;
   18323                 :             : 
   18324                 :             :             /*
   18325                 :             :              * OK, bump the child column's inheritance count.  (If we fail
   18326                 :             :              * later on, this change will just roll back.)
   18327                 :             :              */
   18328         [ -  + ]:        4940 :             if (pg_add_s16_overflow(child_att->attinhcount, 1,
   18329                 :             :                                     &child_att->attinhcount))
   18330         [ #  # ]:           0 :                 ereport(ERROR,
   18331                 :             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   18332                 :             :                         errmsg("too many inheritance parents"));
   18333                 :             : 
   18334                 :             :             /*
   18335                 :             :              * In case of partitions, we must enforce that value of attislocal
   18336                 :             :              * is same in all partitions. (Note: there are only inherited
   18337                 :             :              * attributes in partitions)
   18338                 :             :              */
   18339         [ +  + ]:        4940 :             if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   18340                 :             :             {
   18341                 :             :                 Assert(child_att->attinhcount == 1);
   18342                 :        4453 :                 child_att->attislocal = false;
   18343                 :             :             }
   18344                 :             : 
   18345                 :        4940 :             CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
   18346                 :        4940 :             heap_freetuple(tuple);
   18347                 :             :         }
   18348                 :             :         else
   18349                 :             :         {
   18350         [ +  - ]:           8 :             ereport(ERROR,
   18351                 :             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   18352                 :             :                      errmsg("child table is missing column \"%s\"", parent_attname)));
   18353                 :             :         }
   18354                 :             :     }
   18355                 :             : 
   18356                 :        2170 :     table_close(attrrel, RowExclusiveLock);
   18357                 :        2170 : }
   18358                 :             : 
   18359                 :             : /*
   18360                 :             :  * Check constraints in child table match up with constraints in parent,
   18361                 :             :  * and increment their coninhcount.
   18362                 :             :  *
   18363                 :             :  * Constraints that are marked ONLY in the parent are ignored.
   18364                 :             :  *
   18365                 :             :  * Called by CreateInheritance
   18366                 :             :  *
   18367                 :             :  * Currently all constraints in parent must be present in the child. One day we
   18368                 :             :  * may consider adding new constraints like CREATE TABLE does.
   18369                 :             :  *
   18370                 :             :  * XXX This is O(N^2) which may be an issue with tables with hundreds of
   18371                 :             :  * constraints. As long as tables have more like 10 constraints it shouldn't be
   18372                 :             :  * a problem though. Even 100 constraints ought not be the end of the world.
   18373                 :             :  *
   18374                 :             :  * XXX See MergeWithExistingConstraint too if you change this code.
   18375                 :             :  */
   18376                 :             : static void
   18377                 :        2170 : MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
   18378                 :             : {
   18379                 :             :     Relation    constraintrel;
   18380                 :             :     SysScanDesc parent_scan;
   18381                 :             :     ScanKeyData parent_key;
   18382                 :             :     HeapTuple   parent_tuple;
   18383                 :        2170 :     Oid         parent_relid = RelationGetRelid(parent_rel);
   18384                 :             :     AttrMap    *attmap;
   18385                 :             : 
   18386                 :        2170 :     constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
   18387                 :             : 
   18388                 :             :     /* Outer loop scans through the parent's constraint definitions */
   18389                 :        2170 :     ScanKeyInit(&parent_key,
   18390                 :             :                 Anum_pg_constraint_conrelid,
   18391                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   18392                 :             :                 ObjectIdGetDatum(parent_relid));
   18393                 :        2170 :     parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
   18394                 :             :                                      true, NULL, 1, &parent_key);
   18395                 :             : 
   18396                 :        2170 :     attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
   18397                 :             :                                    RelationGetDescr(child_rel),
   18398                 :             :                                    true);
   18399                 :             : 
   18400         [ +  + ]:        3789 :     while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
   18401                 :             :     {
   18402                 :        1659 :         Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
   18403                 :             :         SysScanDesc child_scan;
   18404                 :             :         ScanKeyData child_key;
   18405                 :             :         HeapTuple   child_tuple;
   18406                 :             :         AttrNumber  parent_attno;
   18407                 :        1659 :         bool        found = false;
   18408                 :             : 
   18409         [ +  + ]:        1659 :         if (parent_con->contype != CONSTRAINT_CHECK &&
   18410         [ +  + ]:        1483 :             parent_con->contype != CONSTRAINT_NOTNULL)
   18411                 :         770 :             continue;
   18412                 :             : 
   18413                 :             :         /* if the parent's constraint is marked NO INHERIT, it's not inherited */
   18414         [ +  + ]:         921 :         if (parent_con->connoinherit)
   18415                 :          32 :             continue;
   18416                 :             : 
   18417         [ +  + ]:         889 :         if (parent_con->contype == CONSTRAINT_NOTNULL)
   18418                 :         729 :             parent_attno = extractNotNullColumn(parent_tuple);
   18419                 :             :         else
   18420                 :         160 :             parent_attno = InvalidAttrNumber;
   18421                 :             : 
   18422                 :             :         /* Search for a child constraint matching this one */
   18423                 :         889 :         ScanKeyInit(&child_key,
   18424                 :             :                     Anum_pg_constraint_conrelid,
   18425                 :             :                     BTEqualStrategyNumber, F_OIDEQ,
   18426                 :             :                     ObjectIdGetDatum(RelationGetRelid(child_rel)));
   18427                 :         889 :         child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
   18428                 :             :                                         true, NULL, 1, &child_key);
   18429                 :             : 
   18430         [ +  + ]:        1416 :         while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
   18431                 :             :         {
   18432                 :        1400 :             Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
   18433                 :             :             HeapTuple   child_copy;
   18434                 :             : 
   18435         [ +  + ]:        1400 :             if (child_con->contype != parent_con->contype)
   18436                 :         274 :                 continue;
   18437                 :             : 
   18438                 :             :             /*
   18439                 :             :              * CHECK constraint are matched by constraint name, NOT NULL ones
   18440                 :             :              * by attribute number.
   18441                 :             :              */
   18442         [ +  + ]:        1126 :             if (child_con->contype == CONSTRAINT_CHECK)
   18443                 :             :             {
   18444                 :         270 :                 if (strcmp(NameStr(parent_con->conname),
   18445         [ +  + ]:         207 :                            NameStr(child_con->conname)) != 0)
   18446                 :          63 :                     continue;
   18447                 :             :             }
   18448         [ +  - ]:         919 :             else if (child_con->contype == CONSTRAINT_NOTNULL)
   18449                 :             :             {
   18450                 :             :                 Form_pg_attribute parent_attr;
   18451                 :             :                 Form_pg_attribute child_attr;
   18452                 :             :                 AttrNumber  child_attno;
   18453                 :             : 
   18454                 :         919 :                 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
   18455                 :         919 :                 child_attno = extractNotNullColumn(child_tuple);
   18456         [ +  + ]:         919 :                 if (parent_attno != attmap->attnums[child_attno - 1])
   18457                 :         190 :                     continue;
   18458                 :             : 
   18459                 :         729 :                 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
   18460                 :             :                 /* there shouldn't be constraints on dropped columns */
   18461   [ +  -  -  + ]:         729 :                 if (parent_attr->attisdropped || child_attr->attisdropped)
   18462         [ #  # ]:           0 :                     elog(ERROR, "found not-null constraint on dropped columns");
   18463                 :             :             }
   18464                 :             : 
   18465         [ +  + ]:         873 :             if (child_con->contype == CONSTRAINT_CHECK &&
   18466         [ +  + ]:         144 :                 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
   18467         [ +  - ]:           4 :                 ereport(ERROR,
   18468                 :             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   18469                 :             :                          errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
   18470                 :             :                                 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
   18471                 :             : 
   18472                 :             :             /*
   18473                 :             :              * If the child constraint is "no inherit" then cannot merge
   18474                 :             :              */
   18475         [ +  + ]:         869 :             if (child_con->connoinherit)
   18476         [ +  - ]:           8 :                 ereport(ERROR,
   18477                 :             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18478                 :             :                          errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
   18479                 :             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   18480                 :             : 
   18481                 :             :             /*
   18482                 :             :              * If the child constraint is "not valid" then cannot merge with a
   18483                 :             :              * valid parent constraint
   18484                 :             :              */
   18485   [ +  +  +  + ]:         861 :             if (parent_con->convalidated && child_con->conenforced &&
   18486         [ +  + ]:         781 :                 !child_con->convalidated)
   18487         [ +  - ]:           8 :                 ereport(ERROR,
   18488                 :             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18489                 :             :                          errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
   18490                 :             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   18491                 :             : 
   18492                 :             :             /*
   18493                 :             :              * A NOT ENFORCED child constraint cannot be merged with an
   18494                 :             :              * ENFORCED parent constraint. However, the reverse is allowed,
   18495                 :             :              * where the child constraint is ENFORCED.
   18496                 :             :              */
   18497   [ +  +  +  + ]:         853 :             if (parent_con->conenforced && !child_con->conenforced)
   18498         [ +  - ]:           4 :                 ereport(ERROR,
   18499                 :             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   18500                 :             :                          errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
   18501                 :             :                                 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
   18502                 :             : 
   18503                 :             :             /*
   18504                 :             :              * OK, bump the child constraint's inheritance count.  (If we fail
   18505                 :             :              * later on, this change will just roll back.)
   18506                 :             :              */
   18507                 :         849 :             child_copy = heap_copytuple(child_tuple);
   18508                 :         849 :             child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
   18509                 :             : 
   18510         [ -  + ]:         849 :             if (pg_add_s16_overflow(child_con->coninhcount, 1,
   18511                 :             :                                     &child_con->coninhcount))
   18512         [ #  # ]:           0 :                 ereport(ERROR,
   18513                 :             :                         errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
   18514                 :             :                         errmsg("too many inheritance parents"));
   18515                 :             : 
   18516                 :             :             /*
   18517                 :             :              * In case of partitions, an inherited constraint must be
   18518                 :             :              * inherited only once since it cannot have multiple parents and
   18519                 :             :              * it is never considered local.
   18520                 :             :              */
   18521         [ +  + ]:         849 :             if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   18522                 :             :             {
   18523                 :             :                 Assert(child_con->coninhcount == 1);
   18524                 :         750 :                 child_con->conislocal = false;
   18525                 :             :             }
   18526                 :             : 
   18527                 :         849 :             CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
   18528                 :         849 :             heap_freetuple(child_copy);
   18529                 :             : 
   18530                 :         849 :             found = true;
   18531                 :         849 :             break;
   18532                 :             :         }
   18533                 :             : 
   18534                 :         865 :         systable_endscan(child_scan);
   18535                 :             : 
   18536         [ +  + ]:         865 :         if (!found)
   18537                 :             :         {
   18538         [ -  + ]:          16 :             if (parent_con->contype == CONSTRAINT_NOTNULL)
   18539         [ #  # ]:           0 :                 ereport(ERROR,
   18540                 :             :                         errcode(ERRCODE_DATATYPE_MISMATCH),
   18541                 :             :                         errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
   18542                 :             :                                get_attname(parent_relid,
   18543                 :             :                                            extractNotNullColumn(parent_tuple),
   18544                 :             :                                            false),
   18545                 :             :                                RelationGetRelationName(child_rel)));
   18546                 :             : 
   18547         [ +  - ]:          16 :             ereport(ERROR,
   18548                 :             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   18549                 :             :                      errmsg("child table is missing constraint \"%s\"",
   18550                 :             :                             NameStr(parent_con->conname))));
   18551                 :             :         }
   18552                 :             :     }
   18553                 :             : 
   18554                 :        2130 :     systable_endscan(parent_scan);
   18555                 :        2130 :     table_close(constraintrel, RowExclusiveLock);
   18556                 :        2130 : }
   18557                 :             : 
   18558                 :             : /*
   18559                 :             :  * ALTER TABLE NO INHERIT
   18560                 :             :  *
   18561                 :             :  * Return value is the address of the relation that is no longer parent.
   18562                 :             :  */
   18563                 :             : static ObjectAddress
   18564                 :          69 : ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
   18565                 :             : {
   18566                 :             :     ObjectAddress address;
   18567                 :             :     Relation    parent_rel;
   18568                 :             : 
   18569                 :             :     /*
   18570                 :             :      * AccessShareLock on the parent is probably enough, seeing that DROP
   18571                 :             :      * TABLE doesn't lock parent tables at all.  We need some lock since we'll
   18572                 :             :      * be inspecting the parent's schema.
   18573                 :             :      */
   18574                 :          69 :     parent_rel = table_openrv(parent, AccessShareLock);
   18575                 :             : 
   18576                 :             :     /*
   18577                 :             :      * We don't bother to check ownership of the parent table --- ownership of
   18578                 :             :      * the child is presumed enough rights.
   18579                 :             :      */
   18580                 :             : 
   18581                 :             :     /* Off to RemoveInheritance() where most of the work happens */
   18582                 :          69 :     RemoveInheritance(rel, parent_rel, false);
   18583                 :             : 
   18584                 :          65 :     ObjectAddressSet(address, RelationRelationId,
   18585                 :             :                      RelationGetRelid(parent_rel));
   18586                 :             : 
   18587                 :             :     /* keep our lock on the parent relation until commit */
   18588                 :          65 :     table_close(parent_rel, NoLock);
   18589                 :             : 
   18590                 :          65 :     return address;
   18591                 :             : }
   18592                 :             : 
   18593                 :             : /*
   18594                 :             :  * MarkInheritDetached
   18595                 :             :  *
   18596                 :             :  * Set inhdetachpending for a partition, for ATExecDetachPartition
   18597                 :             :  * in concurrent mode.  While at it, verify that no other partition is
   18598                 :             :  * already pending detach.
   18599                 :             :  */
   18600                 :             : static void
   18601                 :          75 : MarkInheritDetached(Relation child_rel, Relation parent_rel)
   18602                 :             : {
   18603                 :             :     Relation    catalogRelation;
   18604                 :             :     SysScanDesc scan;
   18605                 :             :     ScanKeyData key;
   18606                 :             :     HeapTuple   inheritsTuple;
   18607                 :          75 :     bool        found = false;
   18608                 :             : 
   18609                 :             :     Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   18610                 :             : 
   18611                 :             :     /*
   18612                 :             :      * Find pg_inherits entries by inhparent.  (We need to scan them all in
   18613                 :             :      * order to verify that no other partition is pending detach.)
   18614                 :             :      */
   18615                 :          75 :     catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
   18616                 :          75 :     ScanKeyInit(&key,
   18617                 :             :                 Anum_pg_inherits_inhparent,
   18618                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   18619                 :             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   18620                 :          75 :     scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
   18621                 :             :                               true, NULL, 1, &key);
   18622                 :             : 
   18623         [ +  + ]:         294 :     while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
   18624                 :             :     {
   18625                 :             :         Form_pg_inherits inhForm;
   18626                 :             : 
   18627                 :         145 :         inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
   18628         [ +  + ]:         145 :         if (inhForm->inhdetachpending)
   18629         [ +  - ]:           1 :             ereport(ERROR,
   18630                 :             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   18631                 :             :                     errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
   18632                 :             :                            get_rel_name(inhForm->inhrelid),
   18633                 :             :                            get_namespace_name(parent_rel->rd_rel->relnamespace),
   18634                 :             :                            RelationGetRelationName(parent_rel)),
   18635                 :             :                     errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
   18636                 :             : 
   18637         [ +  + ]:         144 :         if (inhForm->inhrelid == RelationGetRelid(child_rel))
   18638                 :             :         {
   18639                 :             :             HeapTuple   newtup;
   18640                 :             : 
   18641                 :          74 :             newtup = heap_copytuple(inheritsTuple);
   18642                 :          74 :             ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
   18643                 :             : 
   18644                 :          74 :             CatalogTupleUpdate(catalogRelation,
   18645                 :          74 :                                &inheritsTuple->t_self,
   18646                 :             :                                newtup);
   18647                 :          74 :             found = true;
   18648                 :          74 :             heap_freetuple(newtup);
   18649                 :             :             /* keep looking, to ensure we catch others pending detach */
   18650                 :             :         }
   18651                 :             :     }
   18652                 :             : 
   18653                 :             :     /* Done */
   18654                 :          74 :     systable_endscan(scan);
   18655                 :          74 :     table_close(catalogRelation, RowExclusiveLock);
   18656                 :             : 
   18657         [ -  + ]:          74 :     if (!found)
   18658         [ #  # ]:           0 :         ereport(ERROR,
   18659                 :             :                 (errcode(ERRCODE_UNDEFINED_TABLE),
   18660                 :             :                  errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   18661                 :             :                         RelationGetRelationName(child_rel),
   18662                 :             :                         RelationGetRelationName(parent_rel))));
   18663                 :          74 : }
   18664                 :             : 
   18665                 :             : /*
   18666                 :             :  * RemoveInheritance
   18667                 :             :  *
   18668                 :             :  * Drop a parent from the child's parents. This just adjusts the attinhcount
   18669                 :             :  * and attislocal of the columns and removes the pg_inherit and pg_depend
   18670                 :             :  * entries.  expect_detached is passed down to DeleteInheritsTuple, q.v..
   18671                 :             :  *
   18672                 :             :  * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
   18673                 :             :  * up attislocal stays true, which means if a child is ever removed from a
   18674                 :             :  * parent then its columns will never be automatically dropped which may
   18675                 :             :  * surprise. But at least we'll never surprise by dropping columns someone
   18676                 :             :  * isn't expecting to be dropped which would actually mean data loss.
   18677                 :             :  *
   18678                 :             :  * coninhcount and conislocal for inherited constraints are adjusted in
   18679                 :             :  * exactly the same way.
   18680                 :             :  *
   18681                 :             :  * Common to ATExecDropInherit() and ATExecDetachPartition().
   18682                 :             :  */
   18683                 :             : static void
   18684                 :         842 : RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
   18685                 :             : {
   18686                 :             :     Relation    catalogRelation;
   18687                 :             :     SysScanDesc scan;
   18688                 :             :     ScanKeyData key[3];
   18689                 :             :     HeapTuple   attributeTuple,
   18690                 :             :                 constraintTuple;
   18691                 :             :     AttrMap    *attmap;
   18692                 :             :     List       *connames;
   18693                 :             :     List       *nncolumns;
   18694                 :             :     bool        found;
   18695                 :             :     bool        is_partitioning;
   18696                 :             : 
   18697                 :         842 :     is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
   18698                 :             : 
   18699                 :         842 :     found = DeleteInheritsTuple(RelationGetRelid(child_rel),
   18700                 :             :                                 RelationGetRelid(parent_rel),
   18701                 :             :                                 expect_detached,
   18702                 :         842 :                                 RelationGetRelationName(child_rel));
   18703         [ +  + ]:         842 :     if (!found)
   18704                 :             :     {
   18705         [ +  + ]:          16 :         if (is_partitioning)
   18706         [ +  - ]:          12 :             ereport(ERROR,
   18707                 :             :                     (errcode(ERRCODE_UNDEFINED_TABLE),
   18708                 :             :                      errmsg("relation \"%s\" is not a partition of relation \"%s\"",
   18709                 :             :                             RelationGetRelationName(child_rel),
   18710                 :             :                             RelationGetRelationName(parent_rel))));
   18711                 :             :         else
   18712         [ +  - ]:           4 :             ereport(ERROR,
   18713                 :             :                     (errcode(ERRCODE_UNDEFINED_TABLE),
   18714                 :             :                      errmsg("relation \"%s\" is not a parent of relation \"%s\"",
   18715                 :             :                             RelationGetRelationName(parent_rel),
   18716                 :             :                             RelationGetRelationName(child_rel))));
   18717                 :             :     }
   18718                 :             : 
   18719                 :             :     /*
   18720                 :             :      * Search through child columns looking for ones matching parent rel
   18721                 :             :      */
   18722                 :         826 :     catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
   18723                 :         826 :     ScanKeyInit(&key[0],
   18724                 :             :                 Anum_pg_attribute_attrelid,
   18725                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   18726                 :             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   18727                 :         826 :     scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
   18728                 :             :                               true, NULL, 1, key);
   18729         [ +  + ]:        7578 :     while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
   18730                 :             :     {
   18731                 :        6752 :         Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
   18732                 :             : 
   18733                 :             :         /* Ignore if dropped or not inherited */
   18734         [ +  + ]:        6752 :         if (att->attisdropped)
   18735                 :          28 :             continue;
   18736         [ +  + ]:        6724 :         if (att->attinhcount <= 0)
   18737                 :        4984 :             continue;
   18738                 :             : 
   18739         [ +  + ]:        1740 :         if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
   18740                 :        1740 :                                         NameStr(att->attname)))
   18741                 :             :         {
   18742                 :             :             /* Decrement inhcount and possibly set islocal to true */
   18743                 :        1704 :             HeapTuple   copyTuple = heap_copytuple(attributeTuple);
   18744                 :        1704 :             Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
   18745                 :             : 
   18746                 :        1704 :             copy_att->attinhcount--;
   18747         [ +  + ]:        1704 :             if (copy_att->attinhcount == 0)
   18748                 :        1684 :                 copy_att->attislocal = true;
   18749                 :             : 
   18750                 :        1704 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   18751                 :        1704 :             heap_freetuple(copyTuple);
   18752                 :             :         }
   18753                 :             :     }
   18754                 :         826 :     systable_endscan(scan);
   18755                 :         826 :     table_close(catalogRelation, RowExclusiveLock);
   18756                 :             : 
   18757                 :             :     /*
   18758                 :             :      * Likewise, find inherited check and not-null constraints and disinherit
   18759                 :             :      * them. To do this, we first need a list of the names of the parent's
   18760                 :             :      * check constraints.  (We cheat a bit by only checking for name matches,
   18761                 :             :      * assuming that the expressions will match.)
   18762                 :             :      *
   18763                 :             :      * For NOT NULL columns, we store column numbers to match, mapping them in
   18764                 :             :      * to the child rel's attribute numbers.
   18765                 :             :      */
   18766                 :         826 :     attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
   18767                 :             :                                    RelationGetDescr(parent_rel),
   18768                 :             :                                    false);
   18769                 :             : 
   18770                 :         826 :     catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
   18771                 :         826 :     ScanKeyInit(&key[0],
   18772                 :             :                 Anum_pg_constraint_conrelid,
   18773                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   18774                 :             :                 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
   18775                 :         826 :     scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   18776                 :             :                               true, NULL, 1, key);
   18777                 :             : 
   18778                 :         826 :     connames = NIL;
   18779                 :         826 :     nncolumns = NIL;
   18780                 :             : 
   18781         [ +  + ]:        1523 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   18782                 :             :     {
   18783                 :         697 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   18784                 :             : 
   18785         [ +  + ]:         697 :         if (con->connoinherit)
   18786                 :         152 :             continue;
   18787                 :             : 
   18788         [ +  + ]:         545 :         if (con->contype == CONSTRAINT_CHECK)
   18789                 :          76 :             connames = lappend(connames, pstrdup(NameStr(con->conname)));
   18790         [ +  + ]:         545 :         if (con->contype == CONSTRAINT_NOTNULL)
   18791                 :             :         {
   18792                 :         249 :             AttrNumber  parent_attno = extractNotNullColumn(constraintTuple);
   18793                 :             : 
   18794                 :         249 :             nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
   18795                 :             :         }
   18796                 :             :     }
   18797                 :             : 
   18798                 :         826 :     systable_endscan(scan);
   18799                 :             : 
   18800                 :             :     /* Now scan the child's constraints to find matches */
   18801                 :         826 :     ScanKeyInit(&key[0],
   18802                 :             :                 Anum_pg_constraint_conrelid,
   18803                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   18804                 :             :                 ObjectIdGetDatum(RelationGetRelid(child_rel)));
   18805                 :         826 :     scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
   18806                 :             :                               true, NULL, 1, key);
   18807                 :             : 
   18808         [ +  + ]:        1519 :     while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
   18809                 :             :     {
   18810                 :         693 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
   18811                 :         693 :         bool        match = false;
   18812                 :             : 
   18813                 :             :         /*
   18814                 :             :          * Match CHECK constraints by name, not-null constraints by column
   18815                 :             :          * number, and ignore all others.
   18816                 :             :          */
   18817         [ +  + ]:         693 :         if (con->contype == CONSTRAINT_CHECK)
   18818                 :             :         {
   18819   [ +  +  +  +  :         228 :             foreach_ptr(char, chkname, connames)
                   +  + ]
   18820                 :             :             {
   18821         [ +  - ]:          80 :                 if (con->contype == CONSTRAINT_CHECK &&
   18822         [ +  + ]:          80 :                     strcmp(NameStr(con->conname), chkname) == 0)
   18823                 :             :                 {
   18824                 :          76 :                     match = true;
   18825                 :          76 :                     connames = foreach_delete_current(connames, chkname);
   18826                 :          76 :                     break;
   18827                 :             :                 }
   18828                 :             :             }
   18829                 :             :         }
   18830         [ +  + ]:         581 :         else if (con->contype == CONSTRAINT_NOTNULL)
   18831                 :             :         {
   18832                 :         289 :             AttrNumber  child_attno = extractNotNullColumn(constraintTuple);
   18833                 :             : 
   18834   [ +  +  +  +  :         582 :             foreach_int(prevattno, nncolumns)
                   +  + ]
   18835                 :             :             {
   18836         [ +  + ]:         253 :                 if (prevattno == child_attno)
   18837                 :             :                 {
   18838                 :         249 :                     match = true;
   18839                 :         249 :                     nncolumns = foreach_delete_current(nncolumns, prevattno);
   18840                 :         249 :                     break;
   18841                 :             :                 }
   18842                 :             :             }
   18843                 :             :         }
   18844                 :             :         else
   18845                 :         292 :             continue;
   18846                 :             : 
   18847         [ +  + ]:         401 :         if (match)
   18848                 :             :         {
   18849                 :             :             /* Decrement inhcount and possibly set islocal to true */
   18850                 :         325 :             HeapTuple   copyTuple = heap_copytuple(constraintTuple);
   18851                 :         325 :             Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
   18852                 :             : 
   18853         [ -  + ]:         325 :             if (copy_con->coninhcount <= 0) /* shouldn't happen */
   18854         [ #  # ]:           0 :                 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
   18855                 :             :                      RelationGetRelid(child_rel), NameStr(copy_con->conname));
   18856                 :             : 
   18857                 :         325 :             copy_con->coninhcount--;
   18858         [ +  + ]:         325 :             if (copy_con->coninhcount == 0)
   18859                 :         313 :                 copy_con->conislocal = true;
   18860                 :             : 
   18861                 :         325 :             CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
   18862                 :         325 :             heap_freetuple(copyTuple);
   18863                 :             :         }
   18864                 :             :     }
   18865                 :             : 
   18866                 :             :     /* We should have matched all constraints */
   18867   [ +  -  -  + ]:         826 :     if (connames != NIL || nncolumns != NIL)
   18868         [ #  # ]:           0 :         elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
   18869                 :             :              list_length(connames) + list_length(nncolumns),
   18870                 :             :              RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
   18871                 :             : 
   18872                 :         826 :     systable_endscan(scan);
   18873                 :         826 :     table_close(catalogRelation, RowExclusiveLock);
   18874                 :             : 
   18875         [ +  + ]:         826 :     drop_parent_dependency(RelationGetRelid(child_rel),
   18876                 :             :                            RelationRelationId,
   18877                 :             :                            RelationGetRelid(parent_rel),
   18878                 :             :                            child_dependency_type(is_partitioning));
   18879                 :             : 
   18880                 :             :     /*
   18881                 :             :      * Post alter hook of this inherits. Since object_access_hook doesn't take
   18882                 :             :      * multiple object identifiers, we relay oid of parent relation using
   18883                 :             :      * auxiliary_id argument.
   18884                 :             :      */
   18885         [ -  + ]:         826 :     InvokeObjectPostAlterHookArg(InheritsRelationId,
   18886                 :             :                                  RelationGetRelid(child_rel), 0,
   18887                 :             :                                  RelationGetRelid(parent_rel), false);
   18888                 :         826 : }
   18889                 :             : 
   18890                 :             : /*
   18891                 :             :  * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
   18892                 :             :  * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
   18893                 :             :  * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
   18894                 :             :  * be TypeRelationId).  There's no convenient way to do this, so go trawling
   18895                 :             :  * through pg_depend.
   18896                 :             :  */
   18897                 :             : static void
   18898                 :         834 : drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
   18899                 :             :                        DependencyType deptype)
   18900                 :             : {
   18901                 :             :     Relation    catalogRelation;
   18902                 :             :     SysScanDesc scan;
   18903                 :             :     ScanKeyData key[3];
   18904                 :             :     HeapTuple   depTuple;
   18905                 :             : 
   18906                 :         834 :     catalogRelation = table_open(DependRelationId, RowExclusiveLock);
   18907                 :             : 
   18908                 :         834 :     ScanKeyInit(&key[0],
   18909                 :             :                 Anum_pg_depend_classid,
   18910                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   18911                 :             :                 ObjectIdGetDatum(RelationRelationId));
   18912                 :         834 :     ScanKeyInit(&key[1],
   18913                 :             :                 Anum_pg_depend_objid,
   18914                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   18915                 :             :                 ObjectIdGetDatum(relid));
   18916                 :         834 :     ScanKeyInit(&key[2],
   18917                 :             :                 Anum_pg_depend_objsubid,
   18918                 :             :                 BTEqualStrategyNumber, F_INT4EQ,
   18919                 :             :                 Int32GetDatum(0));
   18920                 :             : 
   18921                 :         834 :     scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
   18922                 :             :                               NULL, 3, key);
   18923                 :             : 
   18924         [ +  + ]:        2555 :     while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
   18925                 :             :     {
   18926                 :        1721 :         Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
   18927                 :             : 
   18928         [ +  + ]:        1721 :         if (dep->refclassid == refclassid &&
   18929         [ +  + ]:         862 :             dep->refobjid == refobjid &&
   18930         [ +  - ]:         834 :             dep->refobjsubid == 0 &&
   18931         [ +  - ]:         834 :             dep->deptype == deptype)
   18932                 :         834 :             CatalogTupleDelete(catalogRelation, &depTuple->t_self);
   18933                 :             :     }
   18934                 :             : 
   18935                 :         834 :     systable_endscan(scan);
   18936                 :         834 :     table_close(catalogRelation, RowExclusiveLock);
   18937                 :         834 : }
   18938                 :             : 
   18939                 :             : /*
   18940                 :             :  * ALTER TABLE OF
   18941                 :             :  *
   18942                 :             :  * Attach a table to a composite type, as though it had been created with CREATE
   18943                 :             :  * TABLE OF.  All attname, atttypid, atttypmod and attcollation must match.  The
   18944                 :             :  * subject table must not have inheritance parents.  These restrictions ensure
   18945                 :             :  * that you cannot create a configuration impossible with CREATE TABLE OF alone.
   18946                 :             :  *
   18947                 :             :  * The address of the type is returned.
   18948                 :             :  */
   18949                 :             : static ObjectAddress
   18950                 :          50 : ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
   18951                 :             : {
   18952                 :          50 :     Oid         relid = RelationGetRelid(rel);
   18953                 :             :     Type        typetuple;
   18954                 :             :     Form_pg_type typeform;
   18955                 :             :     Oid         typeid;
   18956                 :             :     Relation    inheritsRelation,
   18957                 :             :                 relationRelation;
   18958                 :             :     SysScanDesc scan;
   18959                 :             :     ScanKeyData key;
   18960                 :             :     AttrNumber  table_attno,
   18961                 :             :                 type_attno;
   18962                 :             :     TupleDesc   typeTupleDesc,
   18963                 :             :                 tableTupleDesc;
   18964                 :             :     ObjectAddress tableobj,
   18965                 :             :                 typeobj;
   18966                 :             :     HeapTuple   classtuple;
   18967                 :             :     AclResult   aclresult;
   18968                 :             : 
   18969                 :             :     /* Validate the type. */
   18970                 :          50 :     typetuple = typenameType(NULL, ofTypename, NULL);
   18971                 :          50 :     check_of_type(typetuple);
   18972                 :          50 :     typeform = (Form_pg_type) GETSTRUCT(typetuple);
   18973                 :          50 :     typeid = typeform->oid;
   18974                 :             : 
   18975                 :          50 :     aclresult = object_aclcheck(TypeRelationId, typeid, GetUserId(), ACL_USAGE);
   18976         [ +  + ]:          50 :     if (aclresult != ACLCHECK_OK)
   18977                 :           4 :         aclcheck_error_type(aclresult, typeid);
   18978                 :             : 
   18979                 :             :     /* Fail if the table has any inheritance parents. */
   18980                 :          46 :     inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
   18981                 :          46 :     ScanKeyInit(&key,
   18982                 :             :                 Anum_pg_inherits_inhrelid,
   18983                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   18984                 :             :                 ObjectIdGetDatum(relid));
   18985                 :          46 :     scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
   18986                 :             :                               true, NULL, 1, &key);
   18987         [ +  + ]:          46 :     if (HeapTupleIsValid(systable_getnext(scan)))
   18988         [ +  - ]:           4 :         ereport(ERROR,
   18989                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   18990                 :             :                  errmsg("typed tables cannot inherit")));
   18991                 :          42 :     systable_endscan(scan);
   18992                 :          42 :     table_close(inheritsRelation, AccessShareLock);
   18993                 :             : 
   18994                 :             :     /*
   18995                 :             :      * Check the tuple descriptors for compatibility.  Unlike inheritance, we
   18996                 :             :      * require that the order also match.  However, attnotnull need not match.
   18997                 :             :      */
   18998                 :          42 :     typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
   18999                 :          42 :     tableTupleDesc = RelationGetDescr(rel);
   19000                 :          42 :     table_attno = 1;
   19001         [ +  + ]:         133 :     for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
   19002                 :             :     {
   19003                 :             :         Form_pg_attribute type_attr,
   19004                 :             :                     table_attr;
   19005                 :             :         const char *type_attname,
   19006                 :             :                    *table_attname;
   19007                 :             : 
   19008                 :             :         /* Get the next non-dropped type attribute. */
   19009                 :         107 :         type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
   19010         [ +  + ]:         107 :         if (type_attr->attisdropped)
   19011                 :          29 :             continue;
   19012                 :          78 :         type_attname = NameStr(type_attr->attname);
   19013                 :             : 
   19014                 :             :         /* Get the next non-dropped table attribute. */
   19015                 :             :         do
   19016                 :             :         {
   19017         [ +  + ]:          86 :             if (table_attno > tableTupleDesc->natts)
   19018         [ +  - ]:           4 :                 ereport(ERROR,
   19019                 :             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   19020                 :             :                          errmsg("table is missing column \"%s\"",
   19021                 :             :                                 type_attname)));
   19022                 :          82 :             table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
   19023                 :          82 :             table_attno++;
   19024         [ +  + ]:          82 :         } while (table_attr->attisdropped);
   19025                 :          74 :         table_attname = NameStr(table_attr->attname);
   19026                 :             : 
   19027                 :             :         /* Compare name. */
   19028         [ +  + ]:          74 :         if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
   19029         [ +  - ]:           4 :             ereport(ERROR,
   19030                 :             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   19031                 :             :                      errmsg("table has column \"%s\" where type requires \"%s\"",
   19032                 :             :                             table_attname, type_attname)));
   19033                 :             : 
   19034                 :             :         /* Compare type. */
   19035         [ +  + ]:          70 :         if (table_attr->atttypid != type_attr->atttypid ||
   19036         [ +  + ]:          66 :             table_attr->atttypmod != type_attr->atttypmod ||
   19037         [ -  + ]:          62 :             table_attr->attcollation != type_attr->attcollation)
   19038         [ +  - ]:           8 :             ereport(ERROR,
   19039                 :             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   19040                 :             :                      errmsg("table \"%s\" has different type for column \"%s\"",
   19041                 :             :                             RelationGetRelationName(rel), type_attname)));
   19042                 :             :     }
   19043         [ +  - ]:          26 :     ReleaseTupleDesc(typeTupleDesc);
   19044                 :             : 
   19045                 :             :     /* Any remaining columns at the end of the table had better be dropped. */
   19046         [ +  + ]:          26 :     for (; table_attno <= tableTupleDesc->natts; table_attno++)
   19047                 :             :     {
   19048                 :           4 :         Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
   19049                 :             :                                                      table_attno - 1);
   19050                 :             : 
   19051         [ +  - ]:           4 :         if (!table_attr->attisdropped)
   19052         [ +  - ]:           4 :             ereport(ERROR,
   19053                 :             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   19054                 :             :                      errmsg("table has extra column \"%s\"",
   19055                 :             :                             NameStr(table_attr->attname))));
   19056                 :             :     }
   19057                 :             : 
   19058                 :             :     /* If the table was already typed, drop the existing dependency. */
   19059         [ +  + ]:          22 :     if (rel->rd_rel->reloftype)
   19060                 :           4 :         drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   19061                 :             :                                DEPENDENCY_NORMAL);
   19062                 :             : 
   19063                 :             :     /* Record a dependency on the new type. */
   19064                 :          22 :     tableobj.classId = RelationRelationId;
   19065                 :          22 :     tableobj.objectId = relid;
   19066                 :          22 :     tableobj.objectSubId = 0;
   19067                 :          22 :     typeobj.classId = TypeRelationId;
   19068                 :          22 :     typeobj.objectId = typeid;
   19069                 :          22 :     typeobj.objectSubId = 0;
   19070                 :          22 :     recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
   19071                 :             : 
   19072                 :             :     /* Update pg_class.reloftype */
   19073                 :          22 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   19074                 :          22 :     classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   19075         [ -  + ]:          22 :     if (!HeapTupleIsValid(classtuple))
   19076         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   19077                 :          22 :     ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
   19078                 :          22 :     CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
   19079                 :             : 
   19080         [ -  + ]:          22 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   19081                 :             : 
   19082                 :          22 :     heap_freetuple(classtuple);
   19083                 :          22 :     table_close(relationRelation, RowExclusiveLock);
   19084                 :             : 
   19085                 :          22 :     ReleaseSysCache(typetuple);
   19086                 :             : 
   19087                 :          22 :     return typeobj;
   19088                 :             : }
   19089                 :             : 
   19090                 :             : /*
   19091                 :             :  * ALTER TABLE NOT OF
   19092                 :             :  *
   19093                 :             :  * Detach a typed table from its originating type.  Just clear reloftype and
   19094                 :             :  * remove the dependency.
   19095                 :             :  */
   19096                 :             : static void
   19097                 :           4 : ATExecDropOf(Relation rel, LOCKMODE lockmode)
   19098                 :             : {
   19099                 :           4 :     Oid         relid = RelationGetRelid(rel);
   19100                 :             :     Relation    relationRelation;
   19101                 :             :     HeapTuple   tuple;
   19102                 :             : 
   19103         [ -  + ]:           4 :     if (!OidIsValid(rel->rd_rel->reloftype))
   19104         [ #  # ]:           0 :         ereport(ERROR,
   19105                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19106                 :             :                  errmsg("\"%s\" is not a typed table",
   19107                 :             :                         RelationGetRelationName(rel))));
   19108                 :             : 
   19109                 :             :     /*
   19110                 :             :      * We don't bother to check ownership of the type --- ownership of the
   19111                 :             :      * table is presumed enough rights.  No lock required on the type, either.
   19112                 :             :      */
   19113                 :             : 
   19114                 :           4 :     drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
   19115                 :             :                            DEPENDENCY_NORMAL);
   19116                 :             : 
   19117                 :             :     /* Clear pg_class.reloftype */
   19118                 :           4 :     relationRelation = table_open(RelationRelationId, RowExclusiveLock);
   19119                 :           4 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   19120         [ -  + ]:           4 :     if (!HeapTupleIsValid(tuple))
   19121         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   19122                 :           4 :     ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
   19123                 :           4 :     CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
   19124                 :             : 
   19125         [ -  + ]:           4 :     InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
   19126                 :             : 
   19127                 :           4 :     heap_freetuple(tuple);
   19128                 :           4 :     table_close(relationRelation, RowExclusiveLock);
   19129                 :           4 : }
   19130                 :             : 
   19131                 :             : /*
   19132                 :             :  * relation_mark_replica_identity: Update a table's replica identity
   19133                 :             :  *
   19134                 :             :  * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
   19135                 :             :  * index. Otherwise, it must be InvalidOid.
   19136                 :             :  *
   19137                 :             :  * Caller had better hold an exclusive lock on the relation, as the results
   19138                 :             :  * of running two of these concurrently wouldn't be pretty.
   19139                 :             :  */
   19140                 :             : static void
   19141                 :         290 : relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
   19142                 :             :                                bool is_internal)
   19143                 :             : {
   19144                 :             :     Relation    pg_index;
   19145                 :             :     Relation    pg_class;
   19146                 :             :     HeapTuple   pg_class_tuple;
   19147                 :             :     HeapTuple   pg_index_tuple;
   19148                 :             :     Form_pg_class pg_class_form;
   19149                 :             :     Form_pg_index pg_index_form;
   19150                 :             :     ListCell   *index;
   19151                 :             : 
   19152                 :             :     /*
   19153                 :             :      * Check whether relreplident has changed, and update it if so.
   19154                 :             :      */
   19155                 :         290 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   19156                 :         290 :     pg_class_tuple = SearchSysCacheCopy1(RELOID,
   19157                 :             :                                          ObjectIdGetDatum(RelationGetRelid(rel)));
   19158         [ -  + ]:         290 :     if (!HeapTupleIsValid(pg_class_tuple))
   19159         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation \"%s\"",
   19160                 :             :              RelationGetRelationName(rel));
   19161                 :         290 :     pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
   19162         [ +  + ]:         290 :     if (pg_class_form->relreplident != ri_type)
   19163                 :             :     {
   19164                 :         257 :         pg_class_form->relreplident = ri_type;
   19165                 :         257 :         CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
   19166                 :             :     }
   19167                 :         290 :     table_close(pg_class, RowExclusiveLock);
   19168                 :         290 :     heap_freetuple(pg_class_tuple);
   19169                 :             : 
   19170                 :             :     /*
   19171                 :             :      * Update the per-index indisreplident flags correctly.
   19172                 :             :      */
   19173                 :         290 :     pg_index = table_open(IndexRelationId, RowExclusiveLock);
   19174   [ +  +  +  +  :         765 :     foreach(index, RelationGetIndexList(rel))
                   +  + ]
   19175                 :             :     {
   19176                 :         475 :         Oid         thisIndexOid = lfirst_oid(index);
   19177                 :         475 :         bool        dirty = false;
   19178                 :             : 
   19179                 :         475 :         pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
   19180                 :             :                                              ObjectIdGetDatum(thisIndexOid));
   19181         [ -  + ]:         475 :         if (!HeapTupleIsValid(pg_index_tuple))
   19182         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
   19183                 :         475 :         pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
   19184                 :             : 
   19185         [ +  + ]:         475 :         if (thisIndexOid == indexOid)
   19186                 :             :         {
   19187                 :             :             /* Set the bit if not already set. */
   19188         [ +  + ]:         157 :             if (!pg_index_form->indisreplident)
   19189                 :             :             {
   19190                 :         145 :                 dirty = true;
   19191                 :         145 :                 pg_index_form->indisreplident = true;
   19192                 :             :             }
   19193                 :             :         }
   19194                 :             :         else
   19195                 :             :         {
   19196                 :             :             /* Unset the bit if set. */
   19197         [ +  + ]:         318 :             if (pg_index_form->indisreplident)
   19198                 :             :             {
   19199                 :          34 :                 dirty = true;
   19200                 :          34 :                 pg_index_form->indisreplident = false;
   19201                 :             :             }
   19202                 :             :         }
   19203                 :             : 
   19204         [ +  + ]:         475 :         if (dirty)
   19205                 :             :         {
   19206                 :         179 :             CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
   19207         [ -  + ]:         179 :             InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
   19208                 :             :                                          InvalidOid, is_internal);
   19209                 :             : 
   19210                 :             :             /*
   19211                 :             :              * Invalidate the relcache for the table, so that after we commit
   19212                 :             :              * all sessions will refresh the table's replica identity index
   19213                 :             :              * before attempting any UPDATE or DELETE on the table.  (If we
   19214                 :             :              * changed the table's pg_class row above, then a relcache inval
   19215                 :             :              * is already queued due to that; but we might not have.)
   19216                 :             :              */
   19217                 :         179 :             CacheInvalidateRelcache(rel);
   19218                 :             :         }
   19219                 :         475 :         heap_freetuple(pg_index_tuple);
   19220                 :             :     }
   19221                 :             : 
   19222                 :         290 :     table_close(pg_index, RowExclusiveLock);
   19223                 :         290 : }
   19224                 :             : 
   19225                 :             : /*
   19226                 :             :  * ALTER TABLE <name> REPLICA IDENTITY ...
   19227                 :             :  */
   19228                 :             : static void
   19229                 :         326 : ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
   19230                 :             : {
   19231                 :             :     Oid         indexOid;
   19232                 :             :     Relation    indexRel;
   19233                 :             :     int         key;
   19234                 :             : 
   19235         [ +  + ]:         326 :     if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
   19236                 :             :     {
   19237                 :           5 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   19238                 :           5 :         return;
   19239                 :             :     }
   19240         [ +  + ]:         321 :     else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
   19241                 :             :     {
   19242                 :          98 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   19243                 :          98 :         return;
   19244                 :             :     }
   19245         [ +  + ]:         223 :     else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
   19246                 :             :     {
   19247                 :          30 :         relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
   19248                 :          30 :         return;
   19249                 :             :     }
   19250         [ -  + ]:         193 :     else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
   19251                 :             :     {
   19252                 :             :          /* fallthrough */ ;
   19253                 :             :     }
   19254                 :             :     else
   19255         [ #  # ]:           0 :         elog(ERROR, "unexpected identity type %u", stmt->identity_type);
   19256                 :             : 
   19257                 :             :     /* Check that the index exists */
   19258                 :         193 :     indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
   19259         [ -  + ]:         193 :     if (!OidIsValid(indexOid))
   19260         [ #  # ]:           0 :         ereport(ERROR,
   19261                 :             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   19262                 :             :                  errmsg("index \"%s\" for table \"%s\" does not exist",
   19263                 :             :                         stmt->name, RelationGetRelationName(rel))));
   19264                 :             : 
   19265                 :         193 :     indexRel = index_open(indexOid, ShareLock);
   19266                 :             : 
   19267                 :             :     /* Check that the index is on the relation we're altering. */
   19268         [ +  - ]:         193 :     if (indexRel->rd_index == NULL ||
   19269         [ +  + ]:         193 :         indexRel->rd_index->indrelid != RelationGetRelid(rel))
   19270         [ +  - ]:           4 :         ereport(ERROR,
   19271                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19272                 :             :                  errmsg("\"%s\" is not an index for table \"%s\"",
   19273                 :             :                         RelationGetRelationName(indexRel),
   19274                 :             :                         RelationGetRelationName(rel))));
   19275                 :             : 
   19276                 :             :     /*
   19277                 :             :      * The AM must support uniqueness, and the index must in fact be unique.
   19278                 :             :      * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
   19279                 :             :      * exclusion), we can use that too.
   19280                 :             :      */
   19281         [ +  + ]:         189 :     if ((!indexRel->rd_indam->amcanunique ||
   19282         [ +  + ]:         175 :          !indexRel->rd_index->indisunique) &&
   19283   [ +  +  -  + ]:          18 :         !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
   19284         [ +  - ]:           8 :         ereport(ERROR,
   19285                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19286                 :             :                  errmsg("cannot use non-unique index \"%s\" as replica identity",
   19287                 :             :                         RelationGetRelationName(indexRel))));
   19288                 :             :     /* Deferred indexes are not guaranteed to be always unique. */
   19289         [ +  + ]:         181 :     if (!indexRel->rd_index->indimmediate)
   19290         [ +  - ]:           8 :         ereport(ERROR,
   19291                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   19292                 :             :                  errmsg("cannot use non-immediate index \"%s\" as replica identity",
   19293                 :             :                         RelationGetRelationName(indexRel))));
   19294                 :             :     /* Expression indexes aren't supported. */
   19295         [ +  + ]:         173 :     if (RelationGetIndexExpressions(indexRel) != NIL)
   19296         [ +  - ]:           4 :         ereport(ERROR,
   19297                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   19298                 :             :                  errmsg("cannot use expression index \"%s\" as replica identity",
   19299                 :             :                         RelationGetRelationName(indexRel))));
   19300                 :             :     /* Predicate indexes aren't supported. */
   19301         [ +  + ]:         169 :     if (RelationGetIndexPredicate(indexRel) != NIL)
   19302         [ +  - ]:           4 :         ereport(ERROR,
   19303                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   19304                 :             :                  errmsg("cannot use partial index \"%s\" as replica identity",
   19305                 :             :                         RelationGetRelationName(indexRel))));
   19306                 :             : 
   19307                 :             :     /* Check index for nullable columns. */
   19308         [ +  + ]:         366 :     for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
   19309                 :             :     {
   19310                 :         209 :         int16       attno = indexRel->rd_index->indkey.values[key];
   19311                 :             :         Form_pg_attribute attr;
   19312                 :             :         HeapTuple   contup;
   19313                 :             :         Form_pg_constraint conForm;
   19314                 :             : 
   19315                 :             :         /*
   19316                 :             :          * Reject any other system columns.  (Going forward, we'll disallow
   19317                 :             :          * indexes containing such columns in the first place, but they might
   19318                 :             :          * exist in older branches.)
   19319                 :             :          */
   19320         [ -  + ]:         209 :         if (attno <= 0)
   19321         [ #  # ]:           0 :             ereport(ERROR,
   19322                 :             :                     (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
   19323                 :             :                      errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
   19324                 :             :                             RelationGetRelationName(indexRel), attno)));
   19325                 :             : 
   19326                 :         209 :         attr = TupleDescAttr(rel->rd_att, attno - 1);
   19327         [ +  + ]:         209 :         if (!attr->attnotnull)
   19328         [ +  - ]:           4 :             ereport(ERROR,
   19329                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   19330                 :             :                      errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
   19331                 :             :                             RelationGetRelationName(indexRel),
   19332                 :             :                             NameStr(attr->attname))));
   19333                 :             : 
   19334                 :             :         /*
   19335                 :             :          * Verify that the not-null constraint for the column is valid.
   19336                 :             :          */
   19337                 :         205 :         contup = findNotNullConstraintAttnum(RelationGetRelid(rel), attno);
   19338         [ -  + ]:         205 :         if (!HeapTupleIsValid(contup))
   19339         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
   19340                 :             :                  NameStr(attr->attname), RelationGetRelationName(rel));
   19341                 :         205 :         conForm = (Form_pg_constraint) GETSTRUCT(contup);
   19342         [ +  + ]:         205 :         if (!conForm->convalidated)
   19343         [ +  - ]:           4 :             ereport(ERROR,
   19344                 :             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19345                 :             :                     errmsg("cannot use index \"%s\" as replica identity",
   19346                 :             :                            RelationGetRelationName(indexRel)),
   19347                 :             :             /*- translator: third %s is a constraint characteristic such as NOT VALID */
   19348                 :             :                     errdetail("The constraint \"%s\" on column \"%s\" is marked %s.",
   19349                 :             :                               NameStr(conForm->conname), NameStr(attr->attname), "NOT VALID"),
   19350                 :             :                     errhint("You might need to validate it using %s.",
   19351                 :             :                             "ALTER TABLE ... VALIDATE CONSTRAINT"));
   19352                 :         201 :         heap_freetuple(contup);
   19353                 :             :     }
   19354                 :             : 
   19355                 :             :     /* This index is suitable for use as a replica identity. Mark it. */
   19356                 :         157 :     relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
   19357                 :             : 
   19358                 :         157 :     index_close(indexRel, NoLock);
   19359                 :             : }
   19360                 :             : 
   19361                 :             : /*
   19362                 :             :  * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
   19363                 :             :  */
   19364                 :             : static void
   19365                 :         254 : ATExecSetRowSecurity(Relation rel, bool rls)
   19366                 :             : {
   19367                 :             :     Relation    pg_class;
   19368                 :             :     Oid         relid;
   19369                 :             :     HeapTuple   tuple;
   19370                 :             : 
   19371                 :         254 :     relid = RelationGetRelid(rel);
   19372                 :             : 
   19373                 :             :     /* Pull the record for this relation and update it */
   19374                 :         254 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   19375                 :             : 
   19376                 :         254 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   19377                 :             : 
   19378         [ -  + ]:         254 :     if (!HeapTupleIsValid(tuple))
   19379         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   19380                 :             : 
   19381                 :         254 :     ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
   19382                 :         254 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   19383                 :             : 
   19384         [ +  + ]:         254 :     InvokeObjectPostAlterHook(RelationRelationId,
   19385                 :             :                               RelationGetRelid(rel), 0);
   19386                 :             : 
   19387                 :         254 :     table_close(pg_class, RowExclusiveLock);
   19388                 :         254 :     heap_freetuple(tuple);
   19389                 :         254 : }
   19390                 :             : 
   19391                 :             : /*
   19392                 :             :  * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
   19393                 :             :  */
   19394                 :             : static void
   19395                 :          90 : ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
   19396                 :             : {
   19397                 :             :     Relation    pg_class;
   19398                 :             :     Oid         relid;
   19399                 :             :     HeapTuple   tuple;
   19400                 :             : 
   19401                 :          90 :     relid = RelationGetRelid(rel);
   19402                 :             : 
   19403                 :          90 :     pg_class = table_open(RelationRelationId, RowExclusiveLock);
   19404                 :             : 
   19405                 :          90 :     tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
   19406                 :             : 
   19407         [ -  + ]:          90 :     if (!HeapTupleIsValid(tuple))
   19408         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   19409                 :             : 
   19410                 :          90 :     ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
   19411                 :          90 :     CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
   19412                 :             : 
   19413         [ +  + ]:          90 :     InvokeObjectPostAlterHook(RelationRelationId,
   19414                 :             :                               RelationGetRelid(rel), 0);
   19415                 :             : 
   19416                 :          90 :     table_close(pg_class, RowExclusiveLock);
   19417                 :          90 :     heap_freetuple(tuple);
   19418                 :          90 : }
   19419                 :             : 
   19420                 :             : /*
   19421                 :             :  * ALTER FOREIGN TABLE <name> OPTIONS (...)
   19422                 :             :  */
   19423                 :             : static void
   19424                 :          33 : ATExecGenericOptions(Relation rel, List *options)
   19425                 :             : {
   19426                 :             :     Relation    ftrel;
   19427                 :             :     ForeignServer *server;
   19428                 :             :     ForeignDataWrapper *fdw;
   19429                 :             :     HeapTuple   tuple;
   19430                 :             :     bool        isnull;
   19431                 :             :     Datum       repl_val[Natts_pg_foreign_table];
   19432                 :             :     bool        repl_null[Natts_pg_foreign_table];
   19433                 :             :     bool        repl_repl[Natts_pg_foreign_table];
   19434                 :             :     Datum       datum;
   19435                 :             :     Form_pg_foreign_table tableform;
   19436                 :             : 
   19437         [ -  + ]:          33 :     if (options == NIL)
   19438                 :           0 :         return;
   19439                 :             : 
   19440                 :          33 :     ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
   19441                 :             : 
   19442                 :          33 :     tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
   19443                 :             :                                 ObjectIdGetDatum(rel->rd_id));
   19444         [ -  + ]:          33 :     if (!HeapTupleIsValid(tuple))
   19445         [ #  # ]:           0 :         ereport(ERROR,
   19446                 :             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   19447                 :             :                  errmsg("foreign table \"%s\" does not exist",
   19448                 :             :                         RelationGetRelationName(rel))));
   19449                 :          33 :     tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
   19450                 :          33 :     server = GetForeignServer(tableform->ftserver);
   19451                 :          33 :     fdw = GetForeignDataWrapper(server->fdwid);
   19452                 :             : 
   19453                 :          33 :     memset(repl_val, 0, sizeof(repl_val));
   19454                 :          33 :     memset(repl_null, false, sizeof(repl_null));
   19455                 :          33 :     memset(repl_repl, false, sizeof(repl_repl));
   19456                 :             : 
   19457                 :             :     /* Extract the current options */
   19458                 :          33 :     datum = SysCacheGetAttr(FOREIGNTABLEREL,
   19459                 :             :                             tuple,
   19460                 :             :                             Anum_pg_foreign_table_ftoptions,
   19461                 :             :                             &isnull);
   19462         [ +  + ]:          33 :     if (isnull)
   19463                 :           2 :         datum = PointerGetDatum(NULL);
   19464                 :             : 
   19465                 :             :     /* Transform the options */
   19466                 :          33 :     datum = transformGenericOptions(ForeignTableRelationId,
   19467                 :             :                                     datum,
   19468                 :             :                                     options,
   19469                 :             :                                     fdw->fdwvalidator);
   19470                 :             : 
   19471         [ +  - ]:          32 :     if (DatumGetPointer(datum) != NULL)
   19472                 :          32 :         repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
   19473                 :             :     else
   19474                 :           0 :         repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
   19475                 :             : 
   19476                 :          32 :     repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
   19477                 :             : 
   19478                 :             :     /* Everything looks good - update the tuple */
   19479                 :             : 
   19480                 :          32 :     tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
   19481                 :             :                               repl_val, repl_null, repl_repl);
   19482                 :             : 
   19483                 :          32 :     CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
   19484                 :             : 
   19485                 :             :     /*
   19486                 :             :      * Invalidate relcache so that all sessions will refresh any cached plans
   19487                 :             :      * that might depend on the old options.
   19488                 :             :      */
   19489                 :          32 :     CacheInvalidateRelcache(rel);
   19490                 :             : 
   19491         [ -  + ]:          32 :     InvokeObjectPostAlterHook(ForeignTableRelationId,
   19492                 :             :                               RelationGetRelid(rel), 0);
   19493                 :             : 
   19494                 :          32 :     table_close(ftrel, RowExclusiveLock);
   19495                 :             : 
   19496                 :          32 :     heap_freetuple(tuple);
   19497                 :             : }
   19498                 :             : 
   19499                 :             : /*
   19500                 :             :  * ALTER TABLE ALTER COLUMN SET COMPRESSION
   19501                 :             :  *
   19502                 :             :  * Return value is the address of the modified column
   19503                 :             :  */
   19504                 :             : static ObjectAddress
   19505                 :          48 : ATExecSetCompression(Relation rel,
   19506                 :             :                      const char *column,
   19507                 :             :                      Node *newValue,
   19508                 :             :                      LOCKMODE lockmode)
   19509                 :             : {
   19510                 :             :     Relation    attrel;
   19511                 :             :     HeapTuple   tuple;
   19512                 :             :     Form_pg_attribute atttableform;
   19513                 :             :     AttrNumber  attnum;
   19514                 :             :     char       *compression;
   19515                 :             :     char        cmethod;
   19516                 :             :     ObjectAddress address;
   19517                 :             : 
   19518                 :          48 :     compression = strVal(newValue);
   19519                 :             : 
   19520                 :          48 :     attrel = table_open(AttributeRelationId, RowExclusiveLock);
   19521                 :             : 
   19522                 :             :     /* copy the cache entry so we can scribble on it below */
   19523                 :          48 :     tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
   19524         [ -  + ]:          48 :     if (!HeapTupleIsValid(tuple))
   19525         [ #  # ]:           0 :         ereport(ERROR,
   19526                 :             :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
   19527                 :             :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
   19528                 :             :                         column, RelationGetRelationName(rel))));
   19529                 :             : 
   19530                 :             :     /* prevent them from altering a system attribute */
   19531                 :          48 :     atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
   19532                 :          48 :     attnum = atttableform->attnum;
   19533         [ -  + ]:          48 :     if (attnum <= 0)
   19534         [ #  # ]:           0 :         ereport(ERROR,
   19535                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   19536                 :             :                  errmsg("cannot alter system column \"%s\"", column)));
   19537                 :             : 
   19538                 :             :     /*
   19539                 :             :      * Check that column type is compressible, then get the attribute
   19540                 :             :      * compression method code
   19541                 :             :      */
   19542                 :          48 :     cmethod = GetAttributeCompression(atttableform->atttypid, compression);
   19543                 :             : 
   19544                 :             :     /* update pg_attribute entry */
   19545                 :          44 :     atttableform->attcompression = cmethod;
   19546                 :          44 :     CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
   19547                 :             : 
   19548         [ -  + ]:          44 :     InvokeObjectPostAlterHook(RelationRelationId,
   19549                 :             :                               RelationGetRelid(rel),
   19550                 :             :                               attnum);
   19551                 :             : 
   19552                 :             :     /*
   19553                 :             :      * Apply the change to indexes as well (only for simple index columns,
   19554                 :             :      * matching behavior of index.c ConstructTupleDescriptor()).
   19555                 :             :      */
   19556                 :          44 :     SetIndexStorageProperties(rel, attrel, attnum,
   19557                 :             :                               false, 0,
   19558                 :             :                               true, cmethod,
   19559                 :             :                               lockmode);
   19560                 :             : 
   19561                 :          44 :     heap_freetuple(tuple);
   19562                 :             : 
   19563                 :          44 :     table_close(attrel, RowExclusiveLock);
   19564                 :             : 
   19565                 :             :     /* make changes visible */
   19566                 :          44 :     CommandCounterIncrement();
   19567                 :             : 
   19568                 :          44 :     ObjectAddressSubSet(address, RelationRelationId,
   19569                 :             :                         RelationGetRelid(rel), attnum);
   19570                 :          44 :     return address;
   19571                 :             : }
   19572                 :             : 
   19573                 :             : 
   19574                 :             : /*
   19575                 :             :  * Preparation phase for SET LOGGED/UNLOGGED
   19576                 :             :  *
   19577                 :             :  * This verifies that we're not trying to change a temp table.  Also,
   19578                 :             :  * existing foreign key constraints are checked to avoid ending up with
   19579                 :             :  * permanent tables referencing unlogged tables.
   19580                 :             :  */
   19581                 :             : static void
   19582                 :          67 : ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
   19583                 :             : {
   19584                 :             :     Relation    pg_constraint;
   19585                 :             :     HeapTuple   tuple;
   19586                 :             :     SysScanDesc scan;
   19587                 :             :     ScanKeyData skey[1];
   19588                 :             : 
   19589                 :             :     /*
   19590                 :             :      * Disallow changing status for a temp table.  Also verify whether we can
   19591                 :             :      * get away with doing nothing; in such cases we don't need to run the
   19592                 :             :      * checks below, either.
   19593                 :             :      */
   19594   [ -  +  +  - ]:          67 :     switch (rel->rd_rel->relpersistence)
   19595                 :             :     {
   19596                 :           0 :         case RELPERSISTENCE_TEMP:
   19597         [ #  # ]:           0 :             ereport(ERROR,
   19598                 :             :                     (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   19599                 :             :                      errmsg("cannot change logged status of table \"%s\" because it is temporary",
   19600                 :             :                             RelationGetRelationName(rel)),
   19601                 :             :                      errtable(rel)));
   19602                 :             :             break;
   19603                 :          37 :         case RELPERSISTENCE_PERMANENT:
   19604         [ +  + ]:          37 :             if (toLogged)
   19605                 :             :                 /* nothing to do */
   19606                 :           8 :                 return;
   19607                 :          33 :             break;
   19608                 :          30 :         case RELPERSISTENCE_UNLOGGED:
   19609         [ +  + ]:          30 :             if (!toLogged)
   19610                 :             :                 /* nothing to do */
   19611                 :           4 :                 return;
   19612                 :          26 :             break;
   19613                 :             :     }
   19614                 :             : 
   19615                 :             :     /*
   19616                 :             :      * Check that the table is not part of any publication when changing to
   19617                 :             :      * UNLOGGED, as UNLOGGED tables can't be published.
   19618                 :             :      */
   19619   [ +  +  -  + ]:          92 :     if (!toLogged &&
   19620                 :          33 :         GetRelationIncludedPublications(RelationGetRelid(rel)) != NIL)
   19621         [ #  # ]:           0 :         ereport(ERROR,
   19622                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   19623                 :             :                  errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
   19624                 :             :                         RelationGetRelationName(rel)),
   19625                 :             :                  errdetail("Unlogged relations cannot be replicated.")));
   19626                 :             : 
   19627                 :             :     /*
   19628                 :             :      * Check existing foreign key constraints to preserve the invariant that
   19629                 :             :      * permanent tables cannot reference unlogged ones.  Self-referencing
   19630                 :             :      * foreign keys can safely be ignored.
   19631                 :             :      */
   19632                 :          59 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   19633                 :             : 
   19634                 :             :     /*
   19635                 :             :      * Scan conrelid if changing to permanent, else confrelid.  This also
   19636                 :             :      * determines whether a useful index exists.
   19637                 :             :      */
   19638         [ +  + ]:          59 :     ScanKeyInit(&skey[0],
   19639                 :             :                 toLogged ? Anum_pg_constraint_conrelid :
   19640                 :             :                 Anum_pg_constraint_confrelid,
   19641                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   19642                 :             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   19643         [ +  + ]:          59 :     scan = systable_beginscan(pg_constraint,
   19644                 :             :                               toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
   19645                 :             :                               true, NULL, 1, skey);
   19646                 :             : 
   19647         [ +  + ]:          95 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   19648                 :             :     {
   19649                 :          44 :         Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
   19650                 :             : 
   19651         [ +  + ]:          44 :         if (con->contype == CONSTRAINT_FOREIGN)
   19652                 :             :         {
   19653                 :             :             Oid         foreignrelid;
   19654                 :             :             Relation    foreignrel;
   19655                 :             : 
   19656                 :             :             /* the opposite end of what we used as scankey */
   19657         [ +  + ]:          20 :             foreignrelid = toLogged ? con->confrelid : con->conrelid;
   19658                 :             : 
   19659                 :             :             /* ignore if self-referencing */
   19660         [ +  + ]:          20 :             if (RelationGetRelid(rel) == foreignrelid)
   19661                 :           8 :                 continue;
   19662                 :             : 
   19663                 :          12 :             foreignrel = relation_open(foreignrelid, AccessShareLock);
   19664                 :             : 
   19665         [ +  + ]:          12 :             if (toLogged)
   19666                 :             :             {
   19667         [ +  - ]:           4 :                 if (!RelationIsPermanent(foreignrel))
   19668         [ +  - ]:           4 :                     ereport(ERROR,
   19669                 :             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   19670                 :             :                              errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
   19671                 :             :                                     RelationGetRelationName(rel),
   19672                 :             :                                     RelationGetRelationName(foreignrel)),
   19673                 :             :                              errtableconstraint(rel, NameStr(con->conname))));
   19674                 :             :             }
   19675                 :             :             else
   19676                 :             :             {
   19677         [ +  + ]:           8 :                 if (RelationIsPermanent(foreignrel))
   19678         [ +  - ]:           4 :                     ereport(ERROR,
   19679                 :             :                             (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   19680                 :             :                              errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
   19681                 :             :                                     RelationGetRelationName(rel),
   19682                 :             :                                     RelationGetRelationName(foreignrel)),
   19683                 :             :                              errtableconstraint(rel, NameStr(con->conname))));
   19684                 :             :             }
   19685                 :             : 
   19686                 :           4 :             relation_close(foreignrel, AccessShareLock);
   19687                 :             :         }
   19688                 :             :     }
   19689                 :             : 
   19690                 :          51 :     systable_endscan(scan);
   19691                 :             : 
   19692                 :          51 :     table_close(pg_constraint, AccessShareLock);
   19693                 :             : 
   19694                 :             :     /* force rewrite if necessary; see comment in ATRewriteTables */
   19695                 :          51 :     tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
   19696         [ +  + ]:          51 :     if (toLogged)
   19697                 :          22 :         tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
   19698                 :             :     else
   19699                 :          29 :         tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
   19700                 :          51 :     tab->chgPersistence = true;
   19701                 :             : }
   19702                 :             : 
   19703                 :             : /*
   19704                 :             :  * Execute ALTER TABLE SET SCHEMA
   19705                 :             :  */
   19706                 :             : ObjectAddress
   19707                 :          96 : AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
   19708                 :             : {
   19709                 :             :     Relation    rel;
   19710                 :             :     Oid         relid;
   19711                 :             :     Oid         oldNspOid;
   19712                 :             :     Oid         nspOid;
   19713                 :             :     RangeVar   *newrv;
   19714                 :             :     ObjectAddresses *objsMoved;
   19715                 :             :     ObjectAddress myself;
   19716                 :             : 
   19717                 :          96 :     relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
   19718                 :          96 :                                      stmt->missing_ok ? RVR_MISSING_OK : 0,
   19719                 :             :                                      RangeVarCallbackForAlterRelation,
   19720                 :             :                                      stmt);
   19721                 :             : 
   19722         [ +  + ]:          87 :     if (!OidIsValid(relid))
   19723                 :             :     {
   19724         [ +  - ]:          12 :         ereport(NOTICE,
   19725                 :             :                 (errmsg("relation \"%s\" does not exist, skipping",
   19726                 :             :                         stmt->relation->relname)));
   19727                 :          12 :         return InvalidObjectAddress;
   19728                 :             :     }
   19729                 :             : 
   19730                 :          75 :     rel = relation_open(relid, NoLock);
   19731                 :             : 
   19732                 :          75 :     oldNspOid = RelationGetNamespace(rel);
   19733                 :             : 
   19734                 :             :     /* If it's an owned sequence, disallow moving it by itself. */
   19735         [ +  + ]:          75 :     if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
   19736                 :             :     {
   19737                 :             :         Oid         tableId;
   19738                 :             :         int32       colId;
   19739                 :             : 
   19740   [ +  +  -  + ]:           6 :         if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
   19741                 :           1 :             sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
   19742         [ +  - ]:           4 :             ereport(ERROR,
   19743                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   19744                 :             :                      errmsg("cannot move an owned sequence into another schema"),
   19745                 :             :                      errdetail("Sequence \"%s\" is linked to table \"%s\".",
   19746                 :             :                                RelationGetRelationName(rel),
   19747                 :             :                                get_rel_name(tableId))));
   19748                 :             :     }
   19749                 :             : 
   19750                 :             :     /* Get and lock schema OID and check its permissions. */
   19751                 :          71 :     newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
   19752                 :          71 :     nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
   19753                 :             : 
   19754                 :             :     /* common checks on switching namespaces */
   19755                 :          67 :     CheckSetNamespace(oldNspOid, nspOid);
   19756                 :             : 
   19757                 :          67 :     objsMoved = new_object_addresses();
   19758                 :          67 :     AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
   19759                 :          63 :     free_object_addresses(objsMoved);
   19760                 :             : 
   19761                 :          63 :     ObjectAddressSet(myself, RelationRelationId, relid);
   19762                 :             : 
   19763         [ +  - ]:          63 :     if (oldschema)
   19764                 :          63 :         *oldschema = oldNspOid;
   19765                 :             : 
   19766                 :             :     /* close rel, but keep lock until commit */
   19767                 :          63 :     relation_close(rel, NoLock);
   19768                 :             : 
   19769                 :          63 :     return myself;
   19770                 :             : }
   19771                 :             : 
   19772                 :             : /*
   19773                 :             :  * The guts of relocating a table or materialized view to another namespace:
   19774                 :             :  * besides moving the relation itself, its dependent objects are relocated to
   19775                 :             :  * the new schema.
   19776                 :             :  */
   19777                 :             : void
   19778                 :          68 : AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
   19779                 :             :                             ObjectAddresses *objsMoved)
   19780                 :             : {
   19781                 :             :     Relation    classRel;
   19782                 :             : 
   19783                 :             :     Assert(objsMoved != NULL);
   19784                 :             : 
   19785                 :             :     /* OK, modify the pg_class row and pg_depend entry */
   19786                 :          68 :     classRel = table_open(RelationRelationId, RowExclusiveLock);
   19787                 :             : 
   19788                 :          68 :     AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
   19789                 :             :                                    nspOid, true, objsMoved);
   19790                 :             : 
   19791                 :             :     /* Fix the table's row type too, if it has one */
   19792         [ +  + ]:          64 :     if (OidIsValid(rel->rd_rel->reltype))
   19793                 :          55 :         AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
   19794                 :             :                                    false,   /* isImplicitArray */
   19795                 :             :                                    false,   /* ignoreDependent */
   19796                 :             :                                    false,   /* errorOnTableType */
   19797                 :             :                                    objsMoved);
   19798                 :             : 
   19799                 :             :     /* Fix other dependent stuff */
   19800                 :          64 :     AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
   19801                 :          64 :     AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
   19802                 :             :                        objsMoved, AccessExclusiveLock);
   19803                 :          64 :     AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
   19804                 :             :                               false, objsMoved);
   19805                 :             : 
   19806                 :          64 :     table_close(classRel, RowExclusiveLock);
   19807                 :          64 : }
   19808                 :             : 
   19809                 :             : /*
   19810                 :             :  * The guts of relocating a relation to another namespace: fix the pg_class
   19811                 :             :  * entry, and the pg_depend entry if any.  Caller must already have
   19812                 :             :  * opened and write-locked pg_class.
   19813                 :             :  */
   19814                 :             : void
   19815                 :         135 : AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
   19816                 :             :                                Oid oldNspOid, Oid newNspOid,
   19817                 :             :                                bool hasDependEntry,
   19818                 :             :                                ObjectAddresses *objsMoved)
   19819                 :             : {
   19820                 :             :     HeapTuple   classTup;
   19821                 :             :     Form_pg_class classForm;
   19822                 :             :     ObjectAddress thisobj;
   19823                 :         135 :     bool        already_done = false;
   19824                 :             : 
   19825                 :             :     /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
   19826                 :         135 :     classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
   19827         [ -  + ]:         135 :     if (!HeapTupleIsValid(classTup))
   19828         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", relOid);
   19829                 :         135 :     classForm = (Form_pg_class) GETSTRUCT(classTup);
   19830                 :             : 
   19831                 :             :     Assert(classForm->relnamespace == oldNspOid);
   19832                 :             : 
   19833                 :         135 :     thisobj.classId = RelationRelationId;
   19834                 :         135 :     thisobj.objectId = relOid;
   19835                 :         135 :     thisobj.objectSubId = 0;
   19836                 :             : 
   19837                 :             :     /*
   19838                 :             :      * If the object has already been moved, don't move it again.  If it's
   19839                 :             :      * already in the right place, don't move it, but still fire the object
   19840                 :             :      * access hook.
   19841                 :             :      */
   19842                 :         135 :     already_done = object_address_present(&thisobj, objsMoved);
   19843   [ +  -  +  + ]:         135 :     if (!already_done && oldNspOid != newNspOid)
   19844                 :         103 :     {
   19845                 :         107 :         ItemPointerData otid = classTup->t_self;
   19846                 :             : 
   19847                 :             :         /* check for duplicate name (more friendly than unique-index failure) */
   19848         [ +  + ]:         107 :         if (get_relname_relid(NameStr(classForm->relname),
   19849                 :             :                               newNspOid) != InvalidOid)
   19850         [ +  - ]:           4 :             ereport(ERROR,
   19851                 :             :                     (errcode(ERRCODE_DUPLICATE_TABLE),
   19852                 :             :                      errmsg("relation \"%s\" already exists in schema \"%s\"",
   19853                 :             :                             NameStr(classForm->relname),
   19854                 :             :                             get_namespace_name(newNspOid))));
   19855                 :             : 
   19856                 :             :         /* classTup is a copy, so OK to scribble on */
   19857                 :         103 :         classForm->relnamespace = newNspOid;
   19858                 :             : 
   19859                 :         103 :         CatalogTupleUpdate(classRel, &otid, classTup);
   19860                 :         103 :         UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
   19861                 :             : 
   19862                 :             : 
   19863                 :             :         /* Update dependency on schema if caller said so */
   19864   [ +  +  -  + ]:         179 :         if (hasDependEntry &&
   19865                 :          76 :             changeDependencyFor(RelationRelationId,
   19866                 :             :                                 relOid,
   19867                 :             :                                 NamespaceRelationId,
   19868                 :             :                                 oldNspOid,
   19869                 :             :                                 newNspOid) != 1)
   19870         [ #  # ]:           0 :             elog(ERROR, "could not change schema dependency for relation \"%s\"",
   19871                 :             :                  NameStr(classForm->relname));
   19872                 :             :     }
   19873                 :             :     else
   19874                 :          28 :         UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
   19875         [ +  - ]:         131 :     if (!already_done)
   19876                 :             :     {
   19877                 :         131 :         add_exact_object_address(&thisobj, objsMoved);
   19878                 :             : 
   19879         [ -  + ]:         131 :         InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
   19880                 :             :     }
   19881                 :             : 
   19882                 :         131 :     heap_freetuple(classTup);
   19883                 :         131 : }
   19884                 :             : 
   19885                 :             : /*
   19886                 :             :  * Move all indexes for the specified relation to another namespace.
   19887                 :             :  *
   19888                 :             :  * Note: we assume adequate permission checking was done by the caller,
   19889                 :             :  * and that the caller has a suitable lock on the owning relation.
   19890                 :             :  */
   19891                 :             : static void
   19892                 :          64 : AlterIndexNamespaces(Relation classRel, Relation rel,
   19893                 :             :                      Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
   19894                 :             : {
   19895                 :             :     List       *indexList;
   19896                 :             :     ListCell   *l;
   19897                 :             : 
   19898                 :          64 :     indexList = RelationGetIndexList(rel);
   19899                 :             : 
   19900   [ +  +  +  +  :          94 :     foreach(l, indexList)
                   +  + ]
   19901                 :             :     {
   19902                 :          30 :         Oid         indexOid = lfirst_oid(l);
   19903                 :             :         ObjectAddress thisobj;
   19904                 :             : 
   19905                 :          30 :         thisobj.classId = RelationRelationId;
   19906                 :          30 :         thisobj.objectId = indexOid;
   19907                 :          30 :         thisobj.objectSubId = 0;
   19908                 :             : 
   19909                 :             :         /*
   19910                 :             :          * Note: currently, the index will not have its own dependency on the
   19911                 :             :          * namespace, so we don't need to do changeDependencyFor(). There's no
   19912                 :             :          * row type in pg_type, either.
   19913                 :             :          *
   19914                 :             :          * XXX this objsMoved test may be pointless -- surely we have a single
   19915                 :             :          * dependency link from a relation to each index?
   19916                 :             :          */
   19917         [ +  - ]:          30 :         if (!object_address_present(&thisobj, objsMoved))
   19918                 :             :         {
   19919                 :          30 :             AlterRelationNamespaceInternal(classRel, indexOid,
   19920                 :             :                                            oldNspOid, newNspOid,
   19921                 :             :                                            false, objsMoved);
   19922                 :          30 :             add_exact_object_address(&thisobj, objsMoved);
   19923                 :             :         }
   19924                 :             :     }
   19925                 :             : 
   19926                 :          64 :     list_free(indexList);
   19927                 :          64 : }
   19928                 :             : 
   19929                 :             : /*
   19930                 :             :  * Move all identity and SERIAL-column sequences of the specified relation to another
   19931                 :             :  * namespace.
   19932                 :             :  *
   19933                 :             :  * Note: we assume adequate permission checking was done by the caller,
   19934                 :             :  * and that the caller has a suitable lock on the owning relation.
   19935                 :             :  */
   19936                 :             : static void
   19937                 :          64 : AlterSeqNamespaces(Relation classRel, Relation rel,
   19938                 :             :                    Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
   19939                 :             :                    LOCKMODE lockmode)
   19940                 :             : {
   19941                 :             :     Relation    depRel;
   19942                 :             :     SysScanDesc scan;
   19943                 :             :     ScanKeyData key[2];
   19944                 :             :     HeapTuple   tup;
   19945                 :             : 
   19946                 :             :     /*
   19947                 :             :      * SERIAL sequences are those having an auto dependency on one of the
   19948                 :             :      * table's columns (we don't care *which* column, exactly).
   19949                 :             :      */
   19950                 :          64 :     depRel = table_open(DependRelationId, AccessShareLock);
   19951                 :             : 
   19952                 :          64 :     ScanKeyInit(&key[0],
   19953                 :             :                 Anum_pg_depend_refclassid,
   19954                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   19955                 :             :                 ObjectIdGetDatum(RelationRelationId));
   19956                 :          64 :     ScanKeyInit(&key[1],
   19957                 :             :                 Anum_pg_depend_refobjid,
   19958                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   19959                 :             :                 ObjectIdGetDatum(RelationGetRelid(rel)));
   19960                 :             :     /* we leave refobjsubid unspecified */
   19961                 :             : 
   19962                 :          64 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
   19963                 :             :                               NULL, 2, key);
   19964                 :             : 
   19965         [ +  + ]:         417 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
   19966                 :             :     {
   19967                 :         353 :         Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
   19968                 :             :         Relation    seqRel;
   19969                 :             : 
   19970                 :             :         /* skip dependencies other than auto dependencies on columns */
   19971         [ +  + ]:         353 :         if (depForm->refobjsubid == 0 ||
   19972         [ +  + ]:         252 :             depForm->classid != RelationRelationId ||
   19973         [ +  - ]:          28 :             depForm->objsubid != 0 ||
   19974   [ -  +  -  - ]:          28 :             !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
   19975                 :         325 :             continue;
   19976                 :             : 
   19977                 :             :         /* Use relation_open just in case it's an index */
   19978                 :          28 :         seqRel = relation_open(depForm->objid, lockmode);
   19979                 :             : 
   19980                 :             :         /* skip non-sequence relations */
   19981         [ -  + ]:          28 :         if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
   19982                 :             :         {
   19983                 :             :             /* No need to keep the lock */
   19984                 :           0 :             relation_close(seqRel, lockmode);
   19985                 :           0 :             continue;
   19986                 :             :         }
   19987                 :             : 
   19988                 :             :         /* Fix the pg_class and pg_depend entries */
   19989                 :          28 :         AlterRelationNamespaceInternal(classRel, depForm->objid,
   19990                 :             :                                        oldNspOid, newNspOid,
   19991                 :             :                                        true, objsMoved);
   19992                 :             : 
   19993                 :             :         /*
   19994                 :             :          * Sequences used to have entries in pg_type, but no longer do.  If we
   19995                 :             :          * ever re-instate that, we'll need to move the pg_type entry to the
   19996                 :             :          * new namespace, too (using AlterTypeNamespaceInternal).
   19997                 :             :          */
   19998                 :             :         Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
   19999                 :             : 
   20000                 :             :         /* Now we can close it.  Keep the lock till end of transaction. */
   20001                 :          28 :         relation_close(seqRel, NoLock);
   20002                 :             :     }
   20003                 :             : 
   20004                 :          64 :     systable_endscan(scan);
   20005                 :             : 
   20006                 :          64 :     relation_close(depRel, AccessShareLock);
   20007                 :          64 : }
   20008                 :             : 
   20009                 :             : 
   20010                 :             : /*
   20011                 :             :  * This code supports
   20012                 :             :  *  CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
   20013                 :             :  *
   20014                 :             :  * Because we only support this for TEMP tables, it's sufficient to remember
   20015                 :             :  * the state in a backend-local data structure.
   20016                 :             :  */
   20017                 :             : 
   20018                 :             : /*
   20019                 :             :  * Register a newly-created relation's ON COMMIT action.
   20020                 :             :  */
   20021                 :             : void
   20022                 :         120 : register_on_commit_action(Oid relid, OnCommitAction action)
   20023                 :             : {
   20024                 :             :     OnCommitItem *oc;
   20025                 :             :     MemoryContext oldcxt;
   20026                 :             : 
   20027                 :             :     /*
   20028                 :             :      * We needn't bother registering the relation unless there is an ON COMMIT
   20029                 :             :      * action we need to take.
   20030                 :             :      */
   20031   [ +  -  +  + ]:         120 :     if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
   20032                 :          16 :         return;
   20033                 :             : 
   20034                 :         104 :     oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
   20035                 :             : 
   20036                 :         104 :     oc = palloc_object(OnCommitItem);
   20037                 :         104 :     oc->relid = relid;
   20038                 :         104 :     oc->oncommit = action;
   20039                 :         104 :     oc->creating_subid = GetCurrentSubTransactionId();
   20040                 :         104 :     oc->deleting_subid = InvalidSubTransactionId;
   20041                 :             : 
   20042                 :             :     /*
   20043                 :             :      * We use lcons() here so that ON COMMIT actions are processed in reverse
   20044                 :             :      * order of registration.  That might not be essential but it seems
   20045                 :             :      * reasonable.
   20046                 :             :      */
   20047                 :         104 :     on_commits = lcons(oc, on_commits);
   20048                 :             : 
   20049                 :         104 :     MemoryContextSwitchTo(oldcxt);
   20050                 :             : }
   20051                 :             : 
   20052                 :             : /*
   20053                 :             :  * Unregister any ON COMMIT action when a relation is deleted.
   20054                 :             :  *
   20055                 :             :  * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
   20056                 :             :  */
   20057                 :             : void
   20058                 :       34312 : remove_on_commit_action(Oid relid)
   20059                 :             : {
   20060                 :             :     ListCell   *l;
   20061                 :             : 
   20062   [ +  +  +  +  :       34425 :     foreach(l, on_commits)
                   +  + ]
   20063                 :             :     {
   20064                 :         205 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   20065                 :             : 
   20066         [ +  + ]:         205 :         if (oc->relid == relid)
   20067                 :             :         {
   20068                 :          92 :             oc->deleting_subid = GetCurrentSubTransactionId();
   20069                 :          92 :             break;
   20070                 :             :         }
   20071                 :             :     }
   20072                 :       34312 : }
   20073                 :             : 
   20074                 :             : /*
   20075                 :             :  * Perform ON COMMIT actions.
   20076                 :             :  *
   20077                 :             :  * This is invoked just before actually committing, since it's possible
   20078                 :             :  * to encounter errors.
   20079                 :             :  */
   20080                 :             : void
   20081                 :      625210 : PreCommit_on_commit_actions(void)
   20082                 :             : {
   20083                 :             :     ListCell   *l;
   20084                 :      625210 :     List       *oids_to_truncate = NIL;
   20085                 :      625210 :     List       *oids_to_drop = NIL;
   20086                 :             : 
   20087   [ +  +  +  +  :      625756 :     foreach(l, on_commits)
                   +  + ]
   20088                 :             :     {
   20089                 :         546 :         OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   20090                 :             : 
   20091                 :             :         /* Ignore entry if already dropped in this xact */
   20092         [ +  + ]:         546 :         if (oc->deleting_subid != InvalidSubTransactionId)
   20093                 :          49 :             continue;
   20094                 :             : 
   20095   [ -  +  +  - ]:         497 :         switch (oc->oncommit)
   20096                 :             :         {
   20097                 :           0 :             case ONCOMMIT_NOOP:
   20098                 :             :             case ONCOMMIT_PRESERVE_ROWS:
   20099                 :             :                 /* Do nothing (there shouldn't be such entries, actually) */
   20100                 :           0 :                 break;
   20101                 :         462 :             case ONCOMMIT_DELETE_ROWS:
   20102                 :             : 
   20103                 :             :                 /*
   20104                 :             :                  * If this transaction hasn't accessed any temporary
   20105                 :             :                  * relations, we can skip truncating ON COMMIT DELETE ROWS
   20106                 :             :                  * tables, as they must still be empty.
   20107                 :             :                  */
   20108         [ +  + ]:         462 :                 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
   20109                 :         298 :                     oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
   20110                 :         462 :                 break;
   20111                 :          35 :             case ONCOMMIT_DROP:
   20112                 :          35 :                 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
   20113                 :          35 :                 break;
   20114                 :             :         }
   20115                 :             :     }
   20116                 :             : 
   20117                 :             :     /*
   20118                 :             :      * Truncate relations before dropping so that all dependencies between
   20119                 :             :      * relations are removed after they are worked on.  Doing it like this
   20120                 :             :      * might be a waste as it is possible that a relation being truncated will
   20121                 :             :      * be dropped anyway due to its parent being dropped, but this makes the
   20122                 :             :      * code more robust because of not having to re-check that the relation
   20123                 :             :      * exists at truncation time.
   20124                 :             :      */
   20125         [ +  + ]:      625210 :     if (oids_to_truncate != NIL)
   20126                 :         254 :         heap_truncate(oids_to_truncate);
   20127                 :             : 
   20128         [ +  + ]:      625206 :     if (oids_to_drop != NIL)
   20129                 :             :     {
   20130                 :          31 :         ObjectAddresses *targetObjects = new_object_addresses();
   20131                 :             : 
   20132   [ +  -  +  +  :          66 :         foreach(l, oids_to_drop)
                   +  + ]
   20133                 :             :         {
   20134                 :             :             ObjectAddress object;
   20135                 :             : 
   20136                 :          35 :             object.classId = RelationRelationId;
   20137                 :          35 :             object.objectId = lfirst_oid(l);
   20138                 :          35 :             object.objectSubId = 0;
   20139                 :             : 
   20140                 :             :             Assert(!object_address_present(&object, targetObjects));
   20141                 :             : 
   20142                 :          35 :             add_exact_object_address(&object, targetObjects);
   20143                 :             :         }
   20144                 :             : 
   20145                 :             :         /*
   20146                 :             :          * Object deletion might involve toast table access (to clean up
   20147                 :             :          * toasted catalog entries), so ensure we have a valid snapshot.
   20148                 :             :          */
   20149                 :          31 :         PushActiveSnapshot(GetTransactionSnapshot());
   20150                 :             : 
   20151                 :             :         /*
   20152                 :             :          * Since this is an automatic drop, rather than one directly initiated
   20153                 :             :          * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
   20154                 :             :          */
   20155                 :          31 :         performMultipleDeletions(targetObjects, DROP_CASCADE,
   20156                 :             :                                  PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
   20157                 :             : 
   20158                 :          31 :         PopActiveSnapshot();
   20159                 :             : 
   20160                 :             : #ifdef USE_ASSERT_CHECKING
   20161                 :             : 
   20162                 :             :         /*
   20163                 :             :          * Note that table deletion will call remove_on_commit_action, so the
   20164                 :             :          * entry should get marked as deleted.
   20165                 :             :          */
   20166                 :             :         foreach(l, on_commits)
   20167                 :             :         {
   20168                 :             :             OnCommitItem *oc = (OnCommitItem *) lfirst(l);
   20169                 :             : 
   20170                 :             :             if (oc->oncommit != ONCOMMIT_DROP)
   20171                 :             :                 continue;
   20172                 :             : 
   20173                 :             :             Assert(oc->deleting_subid != InvalidSubTransactionId);
   20174                 :             :         }
   20175                 :             : #endif
   20176                 :             :     }
   20177                 :      625206 : }
   20178                 :             : 
   20179                 :             : /*
   20180                 :             :  * Post-commit or post-abort cleanup for ON COMMIT management.
   20181                 :             :  *
   20182                 :             :  * All we do here is remove no-longer-needed OnCommitItem entries.
   20183                 :             :  *
   20184                 :             :  * During commit, remove entries that were deleted during this transaction;
   20185                 :             :  * during abort, remove those created during this transaction.
   20186                 :             :  */
   20187                 :             : void
   20188                 :      661206 : AtEOXact_on_commit_actions(bool isCommit)
   20189                 :             : {
   20190                 :             :     ListCell   *cur_item;
   20191                 :             : 
   20192   [ +  +  +  +  :      661776 :     foreach(cur_item, on_commits)
                   +  + ]
   20193                 :             :     {
   20194                 :         570 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   20195                 :             : 
   20196   [ +  +  +  + ]:         642 :         if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
   20197                 :          72 :             oc->creating_subid != InvalidSubTransactionId)
   20198                 :             :         {
   20199                 :             :             /* cur_item must be removed */
   20200                 :         104 :             on_commits = foreach_delete_current(on_commits, cur_item);
   20201                 :         104 :             pfree(oc);
   20202                 :             :         }
   20203                 :             :         else
   20204                 :             :         {
   20205                 :             :             /* cur_item must be preserved */
   20206                 :         466 :             oc->creating_subid = InvalidSubTransactionId;
   20207                 :         466 :             oc->deleting_subid = InvalidSubTransactionId;
   20208                 :             :         }
   20209                 :             :     }
   20210                 :      661206 : }
   20211                 :             : 
   20212                 :             : /*
   20213                 :             :  * Post-subcommit or post-subabort cleanup for ON COMMIT management.
   20214                 :             :  *
   20215                 :             :  * During subabort, we can immediately remove entries created during this
   20216                 :             :  * subtransaction.  During subcommit, just relabel entries marked during
   20217                 :             :  * this subtransaction as being the parent's responsibility.
   20218                 :             :  */
   20219                 :             : void
   20220                 :       12698 : AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
   20221                 :             :                               SubTransactionId parentSubid)
   20222                 :             : {
   20223                 :             :     ListCell   *cur_item;
   20224                 :             : 
   20225   [ -  +  -  -  :       12698 :     foreach(cur_item, on_commits)
                   -  + ]
   20226                 :             :     {
   20227                 :           0 :         OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
   20228                 :             : 
   20229   [ #  #  #  # ]:           0 :         if (!isCommit && oc->creating_subid == mySubid)
   20230                 :             :         {
   20231                 :             :             /* cur_item must be removed */
   20232                 :           0 :             on_commits = foreach_delete_current(on_commits, cur_item);
   20233                 :           0 :             pfree(oc);
   20234                 :             :         }
   20235                 :             :         else
   20236                 :             :         {
   20237                 :             :             /* cur_item must be preserved */
   20238         [ #  # ]:           0 :             if (oc->creating_subid == mySubid)
   20239                 :           0 :                 oc->creating_subid = parentSubid;
   20240         [ #  # ]:           0 :             if (oc->deleting_subid == mySubid)
   20241         [ #  # ]:           0 :                 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
   20242                 :             :         }
   20243                 :             :     }
   20244                 :       12698 : }
   20245                 :             : 
   20246                 :             : /*
   20247                 :             :  * This is intended as a callback for RangeVarGetRelidExtended().  It allows
   20248                 :             :  * the relation to be locked only if (1) it's a plain or partitioned table,
   20249                 :             :  * materialized view, or TOAST table and (2) the current user is the owner (or
   20250                 :             :  * the superuser) or has been granted MAINTAIN.  This meets the
   20251                 :             :  * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
   20252                 :             :  * MATERIALIZED VIEW; we expose it here so that it can be used by all.
   20253                 :             :  */
   20254                 :             : void
   20255                 :         719 : RangeVarCallbackMaintainsTable(const RangeVar *relation,
   20256                 :             :                                Oid relId, Oid oldRelId, void *arg)
   20257                 :             : {
   20258                 :             :     char        relkind;
   20259                 :             :     AclResult   aclresult;
   20260                 :             : 
   20261                 :             :     /* Nothing to do if the relation was not found. */
   20262         [ +  + ]:         719 :     if (!OidIsValid(relId))
   20263                 :           3 :         return;
   20264                 :             : 
   20265                 :             :     /*
   20266                 :             :      * If the relation does exist, check whether it's an index.  But note that
   20267                 :             :      * the relation might have been dropped between the time we did the name
   20268                 :             :      * lookup and now.  In that case, there's nothing to do.
   20269                 :             :      */
   20270                 :         716 :     relkind = get_rel_relkind(relId);
   20271         [ -  + ]:         716 :     if (!relkind)
   20272                 :           0 :         return;
   20273   [ +  +  +  +  :         716 :     if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
                   +  + ]
   20274         [ +  + ]:         102 :         relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
   20275         [ +  - ]:          18 :         ereport(ERROR,
   20276                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20277                 :             :                  errmsg("\"%s\" is not a table or materialized view", relation->relname)));
   20278                 :             : 
   20279                 :             :     /* Check permissions */
   20280                 :         698 :     aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
   20281         [ +  + ]:         698 :     if (aclresult != ACLCHECK_OK)
   20282                 :          20 :         aclcheck_error(aclresult,
   20283                 :          20 :                        get_relkind_objtype(get_rel_relkind(relId)),
   20284                 :          20 :                        relation->relname);
   20285                 :             : }
   20286                 :             : 
   20287                 :             : /*
   20288                 :             :  * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
   20289                 :             :  */
   20290                 :             : static void
   20291                 :        1479 : RangeVarCallbackForTruncate(const RangeVar *relation,
   20292                 :             :                             Oid relId, Oid oldRelId, void *arg)
   20293                 :             : {
   20294                 :             :     HeapTuple   tuple;
   20295                 :             : 
   20296                 :             :     /* Nothing to do if the relation was not found. */
   20297         [ -  + ]:        1479 :     if (!OidIsValid(relId))
   20298                 :           0 :         return;
   20299                 :             : 
   20300                 :        1479 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   20301         [ -  + ]:        1479 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   20302         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   20303                 :             : 
   20304                 :        1479 :     truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
   20305                 :        1476 :     truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
   20306                 :             : 
   20307                 :        1456 :     ReleaseSysCache(tuple);
   20308                 :             : }
   20309                 :             : 
   20310                 :             : /*
   20311                 :             :  * Callback for RangeVarGetRelidExtended().  Checks that the current user is
   20312                 :             :  * the owner of the relation, or superuser.
   20313                 :             :  */
   20314                 :             : void
   20315                 :       12249 : RangeVarCallbackOwnsRelation(const RangeVar *relation,
   20316                 :             :                              Oid relId, Oid oldRelId, void *arg)
   20317                 :             : {
   20318                 :             :     HeapTuple   tuple;
   20319                 :             : 
   20320                 :             :     /* Nothing to do if the relation was not found. */
   20321         [ +  + ]:       12249 :     if (!OidIsValid(relId))
   20322                 :          16 :         return;
   20323                 :             : 
   20324                 :       12233 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
   20325         [ -  + ]:       12233 :     if (!HeapTupleIsValid(tuple))   /* should not happen */
   20326         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u", relId);
   20327                 :             : 
   20328         [ +  + ]:       12233 :     if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
   20329                 :          16 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
   20330                 :          16 :                        relation->relname);
   20331                 :             : 
   20332                 :             :     /*
   20333                 :             :      * Conflict log tables are used internally for logical replication
   20334                 :             :      * conflict logging and should not be modified directly, as it could
   20335                 :             :      * disrupt conflict logging.
   20336                 :             :      */
   20337         [ +  + ]:       12217 :     if (IsConflictLogTableClass((Form_pg_class) GETSTRUCT(tuple)))
   20338         [ +  - ]:           4 :         ereport(ERROR,
   20339                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20340                 :             :                  errmsg("cannot change conflict log table \"%s\"",
   20341                 :             :                         relation->relname),
   20342                 :             :                  errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
   20343                 :             : 
   20344   [ +  +  +  + ]:       24366 :     if (!allowSystemTableMods &&
   20345                 :       12153 :         IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
   20346         [ +  - ]:           1 :         ereport(ERROR,
   20347                 :             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   20348                 :             :                  errmsg("permission denied: \"%s\" is a system catalog",
   20349                 :             :                         relation->relname)));
   20350                 :             : 
   20351                 :       12212 :     ReleaseSysCache(tuple);
   20352                 :             : }
   20353                 :             : 
   20354                 :             : /*
   20355                 :             :  * Common RangeVarGetRelid callback for rename, set schema, and alter table
   20356                 :             :  * processing.
   20357                 :             :  */
   20358                 :             : static void
   20359                 :       23169 : RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
   20360                 :             :                                  void *arg)
   20361                 :             : {
   20362                 :       23169 :     Node       *stmt = (Node *) arg;
   20363                 :             :     ObjectType  reltype;
   20364                 :             :     HeapTuple   tuple;
   20365                 :             :     Form_pg_class classform;
   20366                 :             :     AclResult   aclresult;
   20367                 :             :     char        relkind;
   20368                 :             : 
   20369                 :       23169 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   20370         [ +  + ]:       23169 :     if (!HeapTupleIsValid(tuple))
   20371                 :         162 :         return;                 /* concurrently dropped */
   20372                 :       23007 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   20373                 :       23007 :     relkind = classform->relkind;
   20374                 :             : 
   20375                 :             :     /* Must own relation. */
   20376         [ +  + ]:       23007 :     if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
   20377                 :          56 :         aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
   20378                 :             : 
   20379                 :             :     /*
   20380                 :             :      * Conflict log tables are used internally for logical replication
   20381                 :             :      * conflict logging and should not be altered directly, as it could
   20382                 :             :      * disrupt conflict logging.
   20383                 :             :      */
   20384         [ +  + ]:       22951 :     if (IsConflictLogTableClass(classform))
   20385         [ +  - ]:          13 :         ereport(ERROR,
   20386                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20387                 :             :                  errmsg("cannot alter conflict log table \"%s\"",
   20388                 :             :                         rv->relname),
   20389                 :             :                  errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
   20390                 :             : 
   20391                 :             :     /* No system table modifications unless explicitly allowed. */
   20392   [ +  +  +  + ]:       22938 :     if (!allowSystemTableMods && IsSystemClass(relid, classform))
   20393         [ +  - ]:          18 :         ereport(ERROR,
   20394                 :             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
   20395                 :             :                  errmsg("permission denied: \"%s\" is a system catalog",
   20396                 :             :                         rv->relname)));
   20397                 :             : 
   20398                 :             :     /*
   20399                 :             :      * Extract the specified relation type from the statement parse tree.
   20400                 :             :      *
   20401                 :             :      * Also, for ALTER .. RENAME, check permissions: the user must (still)
   20402                 :             :      * have CREATE rights on the containing namespace.
   20403                 :             :      */
   20404         [ +  + ]:       22920 :     if (IsA(stmt, RenameStmt))
   20405                 :             :     {
   20406                 :         309 :         aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
   20407                 :             :                                     GetUserId(), ACL_CREATE);
   20408         [ -  + ]:         309 :         if (aclresult != ACLCHECK_OK)
   20409                 :           0 :             aclcheck_error(aclresult, OBJECT_SCHEMA,
   20410                 :           0 :                            get_namespace_name(classform->relnamespace));
   20411                 :         309 :         reltype = ((RenameStmt *) stmt)->renameType;
   20412                 :             :     }
   20413         [ +  + ]:       22611 :     else if (IsA(stmt, AlterObjectSchemaStmt))
   20414                 :          75 :         reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
   20415                 :             : 
   20416         [ +  - ]:       22536 :     else if (IsA(stmt, AlterTableStmt))
   20417                 :       22536 :         reltype = ((AlterTableStmt *) stmt)->objtype;
   20418                 :             :     else
   20419                 :             :     {
   20420         [ #  # ]:           0 :         elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
   20421                 :             :         reltype = OBJECT_TABLE; /* placate compiler */
   20422                 :             :     }
   20423                 :             : 
   20424                 :             :     /*
   20425                 :             :      * For compatibility with prior releases, we allow ALTER TABLE to be used
   20426                 :             :      * with most other types of relations (but not composite types). We allow
   20427                 :             :      * similar flexibility for ALTER INDEX in the case of RENAME, but not
   20428                 :             :      * otherwise.  Otherwise, the user must select the correct form of the
   20429                 :             :      * command for the relation at issue.
   20430                 :             :      */
   20431   [ +  +  -  + ]:       22920 :     if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
   20432         [ #  # ]:           0 :         ereport(ERROR,
   20433                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20434                 :             :                  errmsg("\"%s\" is not a sequence", rv->relname)));
   20435                 :             : 
   20436   [ +  +  -  + ]:       22920 :     if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
   20437         [ #  # ]:           0 :         ereport(ERROR,
   20438                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20439                 :             :                  errmsg("\"%s\" is not a view", rv->relname)));
   20440                 :             : 
   20441   [ +  +  -  + ]:       22920 :     if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
   20442         [ #  # ]:           0 :         ereport(ERROR,
   20443                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20444                 :             :                  errmsg("\"%s\" is not a materialized view", rv->relname)));
   20445                 :             : 
   20446   [ +  +  -  + ]:       22920 :     if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
   20447         [ #  # ]:           0 :         ereport(ERROR,
   20448                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20449                 :             :                  errmsg("\"%s\" is not a foreign table", rv->relname)));
   20450                 :             : 
   20451   [ +  +  -  + ]:       22920 :     if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
   20452         [ #  # ]:           0 :         ereport(ERROR,
   20453                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20454                 :             :                  errmsg("\"%s\" is not a composite type", rv->relname)));
   20455                 :             : 
   20456   [ +  +  -  + ]:       22920 :     if (reltype == OBJECT_PROPGRAPH && relkind != RELKIND_PROPGRAPH)
   20457         [ #  # ]:           0 :         ereport(ERROR,
   20458                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20459                 :             :                  errmsg("\"%s\" is not a property graph", rv->relname)));
   20460                 :             : 
   20461   [ +  +  +  +  :       22920 :     if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
                   +  + ]
   20462                 :             :         relkind != RELKIND_PARTITIONED_INDEX
   20463         [ +  + ]:          24 :         && !IsA(stmt, RenameStmt))
   20464         [ +  - ]:           4 :         ereport(ERROR,
   20465                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20466                 :             :                  errmsg("\"%s\" is not an index", rv->relname)));
   20467                 :             : 
   20468                 :             :     /*
   20469                 :             :      * Don't allow ALTER TABLE on composite types. We want people to use ALTER
   20470                 :             :      * TYPE for that.
   20471                 :             :      */
   20472   [ +  +  -  + ]:       22916 :     if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
   20473         [ #  # ]:           0 :         ereport(ERROR,
   20474                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20475                 :             :                  errmsg("\"%s\" is a composite type", rv->relname),
   20476                 :             :         /* translator: %s is an SQL ALTER command */
   20477                 :             :                  errhint("Use %s instead.",
   20478                 :             :                          "ALTER TYPE")));
   20479                 :             : 
   20480                 :             :     /*
   20481                 :             :      * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
   20482                 :             :      * to a different schema, such as indexes and TOAST tables.
   20483                 :             :      */
   20484         [ +  + ]:       22916 :     if (IsA(stmt, AlterObjectSchemaStmt))
   20485                 :             :     {
   20486   [ +  -  -  + ]:          75 :         if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
   20487         [ #  # ]:           0 :             ereport(ERROR,
   20488                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20489                 :             :                      errmsg("cannot change schema of index \"%s\"",
   20490                 :             :                             rv->relname),
   20491                 :             :                      errhint("Change the schema of the table instead.")));
   20492         [ -  + ]:          75 :         else if (relkind == RELKIND_COMPOSITE_TYPE)
   20493         [ #  # ]:           0 :             ereport(ERROR,
   20494                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20495                 :             :                      errmsg("cannot change schema of composite type \"%s\"",
   20496                 :             :                             rv->relname),
   20497                 :             :             /* translator: %s is an SQL ALTER command */
   20498                 :             :                      errhint("Use %s instead.",
   20499                 :             :                              "ALTER TYPE")));
   20500         [ -  + ]:          75 :         else if (relkind == RELKIND_TOASTVALUE)
   20501         [ #  # ]:           0 :             ereport(ERROR,
   20502                 :             :                     (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   20503                 :             :                      errmsg("cannot change schema of TOAST table \"%s\"",
   20504                 :             :                             rv->relname),
   20505                 :             :                      errhint("Change the schema of the table instead.")));
   20506                 :             :     }
   20507                 :             : 
   20508                 :       22916 :     ReleaseSysCache(tuple);
   20509                 :             : }
   20510                 :             : 
   20511                 :             : /*
   20512                 :             :  * Transform any expressions present in the partition key
   20513                 :             :  *
   20514                 :             :  * Returns a transformed PartitionSpec.
   20515                 :             :  */
   20516                 :             : static PartitionSpec *
   20517                 :        3668 : transformPartitionSpec(Relation rel, PartitionSpec *partspec)
   20518                 :             : {
   20519                 :             :     PartitionSpec *newspec;
   20520                 :             :     ParseState *pstate;
   20521                 :             :     ParseNamespaceItem *nsitem;
   20522                 :             :     ListCell   *l;
   20523                 :             : 
   20524                 :        3668 :     newspec = makeNode(PartitionSpec);
   20525                 :             : 
   20526                 :        3668 :     newspec->strategy = partspec->strategy;
   20527                 :        3668 :     newspec->partParams = NIL;
   20528                 :        3668 :     newspec->location = partspec->location;
   20529                 :             : 
   20530                 :             :     /* Check valid number of columns for strategy */
   20531   [ +  +  +  + ]:        5306 :     if (partspec->strategy == PARTITION_STRATEGY_LIST &&
   20532                 :        1638 :         list_length(partspec->partParams) != 1)
   20533         [ +  - ]:           4 :         ereport(ERROR,
   20534                 :             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20535                 :             :                  errmsg("cannot use \"list\" partition strategy with more than one column")));
   20536                 :             : 
   20537                 :             :     /*
   20538                 :             :      * Create a dummy ParseState and insert the target relation as its sole
   20539                 :             :      * rangetable entry.  We need a ParseState for transformExpr.
   20540                 :             :      */
   20541                 :        3664 :     pstate = make_parsestate(NULL);
   20542                 :        3664 :     nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
   20543                 :             :                                            NULL, false, true);
   20544                 :        3664 :     addNSItemToQuery(pstate, nsitem, true, true, true);
   20545                 :             : 
   20546                 :             :     /* take care of any partition expressions */
   20547   [ +  -  +  +  :        7630 :     foreach(l, partspec->partParams)
                   +  + ]
   20548                 :             :     {
   20549                 :        3982 :         PartitionElem *pelem = lfirst_node(PartitionElem, l);
   20550                 :             : 
   20551         [ +  + ]:        3982 :         if (pelem->expr)
   20552                 :             :         {
   20553                 :             :             /* Copy, to avoid scribbling on the input */
   20554                 :         240 :             pelem = copyObject(pelem);
   20555                 :             : 
   20556                 :             :             /* Now do parse transformation of the expression */
   20557                 :         240 :             pelem->expr = transformExpr(pstate, pelem->expr,
   20558                 :             :                                         EXPR_KIND_PARTITION_EXPRESSION);
   20559                 :             : 
   20560                 :             :             /* we have to fix its collations too */
   20561                 :         224 :             assign_expr_collations(pstate, pelem->expr);
   20562                 :             :         }
   20563                 :             : 
   20564                 :        3966 :         newspec->partParams = lappend(newspec->partParams, pelem);
   20565                 :             :     }
   20566                 :             : 
   20567                 :        3648 :     return newspec;
   20568                 :             : }
   20569                 :             : 
   20570                 :             : /*
   20571                 :             :  * Compute per-partition-column information from a list of PartitionElems.
   20572                 :             :  * Expressions in the PartitionElems must be parse-analyzed already.
   20573                 :             :  */
   20574                 :             : static void
   20575                 :        3648 : ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
   20576                 :             :                       List **partexprs, Oid *partopclass, Oid *partcollation,
   20577                 :             :                       PartitionStrategy strategy)
   20578                 :             : {
   20579                 :             :     int         attn;
   20580                 :             :     ListCell   *lc;
   20581                 :             :     Oid         am_oid;
   20582                 :             : 
   20583                 :        3648 :     attn = 0;
   20584   [ +  -  +  +  :        7526 :     foreach(lc, partParams)
                   +  + ]
   20585                 :             :     {
   20586                 :        3966 :         PartitionElem *pelem = lfirst_node(PartitionElem, lc);
   20587                 :             :         Oid         atttype;
   20588                 :             :         Oid         attcollation;
   20589                 :             : 
   20590         [ +  + ]:        3966 :         if (pelem->name != NULL)
   20591                 :             :         {
   20592                 :             :             /* Simple attribute reference */
   20593                 :             :             HeapTuple   atttuple;
   20594                 :             :             Form_pg_attribute attform;
   20595                 :             : 
   20596                 :        3742 :             atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
   20597                 :        3742 :                                              pelem->name);
   20598         [ +  + ]:        3742 :             if (!HeapTupleIsValid(atttuple))
   20599         [ +  - ]:           8 :                 ereport(ERROR,
   20600                 :             :                         (errcode(ERRCODE_UNDEFINED_COLUMN),
   20601                 :             :                          errmsg("column \"%s\" named in partition key does not exist",
   20602                 :             :                                 pelem->name),
   20603                 :             :                          parser_errposition(pstate, pelem->location)));
   20604                 :        3734 :             attform = (Form_pg_attribute) GETSTRUCT(atttuple);
   20605                 :             : 
   20606         [ +  + ]:        3734 :             if (attform->attnum <= 0)
   20607         [ +  - ]:           4 :                 ereport(ERROR,
   20608                 :             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20609                 :             :                          errmsg("cannot use system column \"%s\" in partition key",
   20610                 :             :                                 pelem->name),
   20611                 :             :                          parser_errposition(pstate, pelem->location)));
   20612                 :             : 
   20613                 :             :             /*
   20614                 :             :              * Stored generated columns cannot work: They are computed after
   20615                 :             :              * BEFORE triggers, but partition routing is done before all
   20616                 :             :              * triggers.  Maybe virtual generated columns could be made to
   20617                 :             :              * work, but then they would need to be handled as an expression
   20618                 :             :              * below.
   20619                 :             :              */
   20620         [ +  + ]:        3730 :             if (attform->attgenerated)
   20621         [ +  - ]:           8 :                 ereport(ERROR,
   20622                 :             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20623                 :             :                          errmsg("cannot use generated column in partition key"),
   20624                 :             :                          errdetail("Column \"%s\" is a generated column.",
   20625                 :             :                                    pelem->name),
   20626                 :             :                          parser_errposition(pstate, pelem->location)));
   20627                 :             : 
   20628                 :        3722 :             partattrs[attn] = attform->attnum;
   20629                 :        3722 :             atttype = attform->atttypid;
   20630                 :        3722 :             attcollation = attform->attcollation;
   20631                 :        3722 :             ReleaseSysCache(atttuple);
   20632                 :             :         }
   20633                 :             :         else
   20634                 :             :         {
   20635                 :             :             /* Expression */
   20636                 :         224 :             Node       *expr = pelem->expr;
   20637                 :             :             char        partattname[16];
   20638                 :         224 :             Bitmapset  *expr_attrs = NULL;
   20639                 :             :             int         i;
   20640                 :             : 
   20641                 :             :             Assert(expr != NULL);
   20642                 :         224 :             atttype = exprType(expr);
   20643                 :         224 :             attcollation = exprCollation(expr);
   20644                 :             : 
   20645                 :             :             /*
   20646                 :             :              * The expression must be of a storable type (e.g., not RECORD).
   20647                 :             :              * The test is the same as for whether a table column is of a safe
   20648                 :             :              * type (which is why we needn't check for the non-expression
   20649                 :             :              * case).
   20650                 :             :              */
   20651                 :         224 :             snprintf(partattname, sizeof(partattname), "%d", attn + 1);
   20652                 :         224 :             CheckAttributeType(partattname,
   20653                 :             :                                atttype, attcollation,
   20654                 :             :                                NIL, CHKATYPE_IS_PARTKEY);
   20655                 :             : 
   20656                 :             :             /*
   20657                 :             :              * Strip any top-level COLLATE clause.  This ensures that we treat
   20658                 :             :              * "x COLLATE y" and "(x COLLATE y)" alike.
   20659                 :             :              */
   20660         [ -  + ]:         216 :             while (IsA(expr, CollateExpr))
   20661                 :           0 :                 expr = (Node *) ((CollateExpr *) expr)->arg;
   20662                 :             : 
   20663                 :             :             /*
   20664                 :             :              * Examine all the columns in the partition key expression. When
   20665                 :             :              * the whole-row reference is present, examine all the columns of
   20666                 :             :              * the partitioned table.
   20667                 :             :              */
   20668                 :         216 :             pull_varattnos(expr, 1, &expr_attrs);
   20669         [ +  + ]:         216 :             if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber, expr_attrs))
   20670                 :             :             {
   20671                 :          40 :                 expr_attrs = bms_add_range(expr_attrs,
   20672                 :             :                                            1 - FirstLowInvalidHeapAttributeNumber,
   20673                 :          20 :                                            RelationGetNumberOfAttributes(rel) - FirstLowInvalidHeapAttributeNumber);
   20674                 :          20 :                 expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
   20675                 :             :             }
   20676                 :             : 
   20677                 :         216 :             i = -1;
   20678         [ +  + ]:         473 :             while ((i = bms_next_member(expr_attrs, i)) >= 0)
   20679                 :             :             {
   20680                 :         289 :                 AttrNumber  attno = i + FirstLowInvalidHeapAttributeNumber;
   20681                 :             : 
   20682                 :             :                 Assert(attno != 0);
   20683                 :             : 
   20684                 :             :                 /*
   20685                 :             :                  * Cannot allow system column references, since that would
   20686                 :             :                  * make partition routing impossible: their values won't be
   20687                 :             :                  * known yet when we need to do that.
   20688                 :             :                  */
   20689         [ -  + ]:         289 :                 if (attno < 0)
   20690         [ #  # ]:           0 :                     ereport(ERROR,
   20691                 :             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20692                 :             :                              errmsg("partition key expressions cannot contain system column references")));
   20693                 :             : 
   20694                 :             :                 /*
   20695                 :             :                  * Stored generated columns cannot work: They are computed
   20696                 :             :                  * after BEFORE triggers, but partition routing is done before
   20697                 :             :                  * all triggers.  Virtual generated columns could probably
   20698                 :             :                  * work, but it would require more work elsewhere (for example
   20699                 :             :                  * SET EXPRESSION would need to check whether the column is
   20700                 :             :                  * used in partition keys).  Seems safer to prohibit for now.
   20701                 :             :                  */
   20702         [ +  + ]:         289 :                 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
   20703         [ +  - ]:          32 :                     ereport(ERROR,
   20704                 :             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20705                 :             :                              errmsg("cannot use generated column in partition key"),
   20706                 :             :                              errdetail("Column \"%s\" is a generated column.",
   20707                 :             :                                        get_attname(RelationGetRelid(rel), attno, false)),
   20708                 :             :                              parser_errposition(pstate, pelem->location)));
   20709                 :             :             }
   20710                 :             : 
   20711         [ +  + ]:         184 :             if (IsA(expr, Var) &&
   20712         [ +  + ]:           8 :                 ((Var *) expr)->varattno > 0)
   20713                 :             :             {
   20714                 :             : 
   20715                 :             :                 /*
   20716                 :             :                  * User wrote "(column)" or "(column COLLATE something)".
   20717                 :             :                  * Treat it like simple attribute anyway.
   20718                 :             :                  */
   20719                 :           4 :                 partattrs[attn] = ((Var *) expr)->varattno;
   20720                 :             :             }
   20721                 :             :             else
   20722                 :             :             {
   20723                 :         180 :                 partattrs[attn] = 0;    /* marks the column as expression */
   20724                 :         180 :                 *partexprs = lappend(*partexprs, expr);
   20725                 :             : 
   20726                 :             :                 /*
   20727                 :             :                  * transformPartitionSpec() should have already rejected
   20728                 :             :                  * subqueries, aggregates, window functions, and SRFs, based
   20729                 :             :                  * on the EXPR_KIND_ for partition expressions.
   20730                 :             :                  */
   20731                 :             : 
   20732                 :             :                 /*
   20733                 :             :                  * Preprocess the expression before checking for mutability.
   20734                 :             :                  * This is essential for the reasons described in
   20735                 :             :                  * contain_mutable_functions_after_planning.  However, we call
   20736                 :             :                  * expression_planner for ourselves rather than using that
   20737                 :             :                  * function, because if constant-folding reduces the
   20738                 :             :                  * expression to a constant, we'd like to know that so we can
   20739                 :             :                  * complain below.
   20740                 :             :                  *
   20741                 :             :                  * Like contain_mutable_functions_after_planning, assume that
   20742                 :             :                  * expression_planner won't scribble on its input, so this
   20743                 :             :                  * won't affect the partexprs entry we saved above.
   20744                 :             :                  */
   20745                 :         180 :                 expr = (Node *) expression_planner((Expr *) expr);
   20746                 :             : 
   20747                 :             :                 /*
   20748                 :             :                  * Partition expressions cannot contain mutable functions,
   20749                 :             :                  * because a given row must always map to the same partition
   20750                 :             :                  * as long as there is no change in the partition boundary
   20751                 :             :                  * structure.
   20752                 :             :                  */
   20753         [ +  + ]:         180 :                 if (contain_mutable_functions(expr))
   20754         [ +  - ]:           4 :                     ereport(ERROR,
   20755                 :             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20756                 :             :                              errmsg("functions in partition key expression must be marked IMMUTABLE")));
   20757                 :             : 
   20758                 :             :                 /*
   20759                 :             :                  * While it is not exactly *wrong* for a partition expression
   20760                 :             :                  * to be a constant, it seems better to reject such keys.
   20761                 :             :                  */
   20762         [ +  + ]:         176 :                 if (IsA(expr, Const))
   20763         [ +  - ]:           8 :                     ereport(ERROR,
   20764                 :             :                             (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   20765                 :             :                              errmsg("cannot use constant expression as partition key")));
   20766                 :             :             }
   20767                 :             :         }
   20768                 :             : 
   20769                 :             :         /*
   20770                 :             :          * Apply collation override if any
   20771                 :             :          */
   20772         [ +  + ]:        3894 :         if (pelem->collation)
   20773                 :          36 :             attcollation = get_collation_oid(pelem->collation, false);
   20774                 :             : 
   20775                 :             :         /*
   20776                 :             :          * Check we have a collation iff it's a collatable type.  The only
   20777                 :             :          * expected failures here are (1) COLLATE applied to a noncollatable
   20778                 :             :          * type, or (2) partition expression had an unresolved collation. But
   20779                 :             :          * we might as well code this to be a complete consistency check.
   20780                 :             :          */
   20781         [ +  + ]:        3894 :         if (type_is_collatable(atttype))
   20782                 :             :         {
   20783         [ -  + ]:         444 :             if (!OidIsValid(attcollation))
   20784         [ #  # ]:           0 :                 ereport(ERROR,
   20785                 :             :                         (errcode(ERRCODE_INDETERMINATE_COLLATION),
   20786                 :             :                          errmsg("could not determine which collation to use for partition expression"),
   20787                 :             :                          errhint("Use the COLLATE clause to set the collation explicitly.")));
   20788                 :             :         }
   20789                 :             :         else
   20790                 :             :         {
   20791         [ -  + ]:        3450 :             if (OidIsValid(attcollation))
   20792         [ #  # ]:           0 :                 ereport(ERROR,
   20793                 :             :                         (errcode(ERRCODE_DATATYPE_MISMATCH),
   20794                 :             :                          errmsg("collations are not supported by type %s",
   20795                 :             :                                 format_type_be(atttype))));
   20796                 :             :         }
   20797                 :             : 
   20798                 :        3894 :         partcollation[attn] = attcollation;
   20799                 :             : 
   20800                 :             :         /*
   20801                 :             :          * Identify the appropriate operator class.  For list and range
   20802                 :             :          * partitioning, we use a btree operator class; hash partitioning uses
   20803                 :             :          * a hash operator class.
   20804                 :             :          */
   20805         [ +  + ]:        3894 :         if (strategy == PARTITION_STRATEGY_HASH)
   20806                 :         215 :             am_oid = HASH_AM_OID;
   20807                 :             :         else
   20808                 :        3679 :             am_oid = BTREE_AM_OID;
   20809                 :             : 
   20810         [ +  + ]:        3894 :         if (!pelem->opclass)
   20811                 :             :         {
   20812                 :        3802 :             partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
   20813                 :             : 
   20814         [ +  + ]:        3802 :             if (!OidIsValid(partopclass[attn]))
   20815                 :             :             {
   20816         [ -  + ]:           8 :                 if (strategy == PARTITION_STRATEGY_HASH)
   20817         [ #  # ]:           0 :                     ereport(ERROR,
   20818                 :             :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
   20819                 :             :                              errmsg("data type %s has no default operator class for access method \"%s\"",
   20820                 :             :                                     format_type_be(atttype), "hash"),
   20821                 :             :                              errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
   20822                 :             :                 else
   20823         [ +  - ]:           8 :                     ereport(ERROR,
   20824                 :             :                             (errcode(ERRCODE_UNDEFINED_OBJECT),
   20825                 :             :                              errmsg("data type %s has no default operator class for access method \"%s\"",
   20826                 :             :                                     format_type_be(atttype), "btree"),
   20827                 :             :                              errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
   20828                 :             :             }
   20829                 :             :         }
   20830                 :             :         else
   20831         [ +  + ]:          92 :             partopclass[attn] = ResolveOpClass(pelem->opclass,
   20832                 :             :                                                atttype,
   20833                 :             :                                                am_oid == HASH_AM_OID ? "hash" : "btree",
   20834                 :             :                                                am_oid);
   20835                 :             : 
   20836                 :        3878 :         attn++;
   20837                 :             :     }
   20838                 :        3560 : }
   20839                 :             : 
   20840                 :             : /*
   20841                 :             :  * PartConstraintImpliedByRelConstraint
   20842                 :             :  *      Do scanrel's existing constraints imply the partition constraint?
   20843                 :             :  *
   20844                 :             :  * "Existing constraints" include its check constraints and column-level
   20845                 :             :  * not-null constraints.  partConstraint describes the partition constraint,
   20846                 :             :  * in implicit-AND form.
   20847                 :             :  */
   20848                 :             : bool
   20849                 :        2071 : PartConstraintImpliedByRelConstraint(Relation scanrel,
   20850                 :             :                                      List *partConstraint)
   20851                 :             : {
   20852                 :        2071 :     List       *existConstraint = NIL;
   20853                 :        2071 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   20854                 :             :     int         i;
   20855                 :             : 
   20856   [ +  +  +  + ]:        2071 :     if (constr && constr->has_not_null)
   20857                 :             :     {
   20858                 :         544 :         int         natts = scanrel->rd_att->natts;
   20859                 :             : 
   20860         [ +  + ]:        1886 :         for (i = 1; i <= natts; i++)
   20861                 :             :         {
   20862                 :        1342 :             CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
   20863                 :             : 
   20864                 :             :             /* invalid not-null constraint must be ignored here */
   20865   [ +  +  +  - ]:        1342 :             if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
   20866                 :             :             {
   20867                 :         747 :                 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
   20868                 :         747 :                 NullTest   *ntest = makeNode(NullTest);
   20869                 :             : 
   20870                 :         747 :                 ntest->arg = (Expr *) makeVar(1,
   20871                 :             :                                               i,
   20872                 :             :                                               wholeatt->atttypid,
   20873                 :             :                                               wholeatt->atttypmod,
   20874                 :             :                                               wholeatt->attcollation,
   20875                 :             :                                               0);
   20876                 :         747 :                 ntest->nulltesttype = IS_NOT_NULL;
   20877                 :             : 
   20878                 :             :                 /*
   20879                 :             :                  * argisrow=false is correct even for a composite column,
   20880                 :             :                  * because attnotnull does not represent a SQL-spec IS NOT
   20881                 :             :                  * NULL test in such a case, just IS DISTINCT FROM NULL.
   20882                 :             :                  */
   20883                 :         747 :                 ntest->argisrow = false;
   20884                 :         747 :                 ntest->location = -1;
   20885                 :         747 :                 existConstraint = lappend(existConstraint, ntest);
   20886                 :             :             }
   20887                 :             :         }
   20888                 :             :     }
   20889                 :             : 
   20890                 :        2071 :     return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
   20891                 :             : }
   20892                 :             : 
   20893                 :             : /*
   20894                 :             :  * ConstraintImpliedByRelConstraint
   20895                 :             :  *      Do scanrel's existing constraints imply the given constraint?
   20896                 :             :  *
   20897                 :             :  * testConstraint is the constraint to validate. provenConstraint is a
   20898                 :             :  * caller-provided list of conditions which this function may assume
   20899                 :             :  * to be true. Both provenConstraint and testConstraint must be in
   20900                 :             :  * implicit-AND form, must only contain immutable clauses, and must
   20901                 :             :  * contain only Vars with varno = 1.
   20902                 :             :  */
   20903                 :             : bool
   20904                 :        2895 : ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
   20905                 :             : {
   20906                 :        2895 :     List       *existConstraint = list_copy(provenConstraint);
   20907                 :        2895 :     TupleConstr *constr = RelationGetDescr(scanrel)->constr;
   20908                 :             :     int         num_check,
   20909                 :             :                 i;
   20910                 :             : 
   20911         [ +  + ]:        2895 :     num_check = (constr != NULL) ? constr->num_check : 0;
   20912         [ +  + ]:        3231 :     for (i = 0; i < num_check; i++)
   20913                 :             :     {
   20914                 :             :         Node       *cexpr;
   20915                 :             : 
   20916                 :             :         /*
   20917                 :             :          * If this constraint hasn't been fully validated yet, we must ignore
   20918                 :             :          * it here.
   20919                 :             :          */
   20920         [ +  + ]:         336 :         if (!constr->check[i].ccvalid)
   20921                 :          12 :             continue;
   20922                 :             : 
   20923                 :             :         /*
   20924                 :             :          * NOT ENFORCED constraints are always marked as invalid, which should
   20925                 :             :          * have been ignored.
   20926                 :             :          */
   20927                 :             :         Assert(constr->check[i].ccenforced);
   20928                 :             : 
   20929                 :         324 :         cexpr = stringToNode(constr->check[i].ccbin);
   20930                 :             : 
   20931                 :             :         /*
   20932                 :             :          * Run each expression through const-simplification and
   20933                 :             :          * canonicalization.  It is necessary, because we will be comparing it
   20934                 :             :          * to similarly-processed partition constraint expressions, and may
   20935                 :             :          * fail to detect valid matches without this.
   20936                 :             :          */
   20937                 :         324 :         cexpr = eval_const_expressions(NULL, cexpr);
   20938                 :         324 :         cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
   20939                 :             : 
   20940                 :         324 :         existConstraint = list_concat(existConstraint,
   20941                 :         324 :                                       make_ands_implicit((Expr *) cexpr));
   20942                 :             :     }
   20943                 :             : 
   20944                 :             :     /*
   20945                 :             :      * Try to make the proof.  Since we are comparing CHECK constraints, we
   20946                 :             :      * need to use weak implication, i.e., we assume existConstraint is
   20947                 :             :      * not-false and try to prove the same for testConstraint.
   20948                 :             :      *
   20949                 :             :      * Note that predicate_implied_by assumes its first argument is known
   20950                 :             :      * immutable.  That should always be true for both NOT NULL and partition
   20951                 :             :      * constraints, so we don't test it here.
   20952                 :             :      */
   20953                 :        2895 :     return predicate_implied_by(testConstraint, existConstraint, true);
   20954                 :             : }
   20955                 :             : 
   20956                 :             : /*
   20957                 :             :  * QueuePartitionConstraintValidation
   20958                 :             :  *
   20959                 :             :  * Add an entry to wqueue to have the given partition constraint validated by
   20960                 :             :  * Phase 3, for the given relation, and all its children.
   20961                 :             :  *
   20962                 :             :  * We first verify whether the given constraint is implied by pre-existing
   20963                 :             :  * relation constraints; if it is, there's no need to scan the table to
   20964                 :             :  * validate, so don't queue in that case.
   20965                 :             :  */
   20966                 :             : static void
   20967                 :        1742 : QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
   20968                 :             :                                    List *partConstraint,
   20969                 :             :                                    bool validate_default)
   20970                 :             : {
   20971                 :             :     /*
   20972                 :             :      * Based on the table's existing constraints, determine whether or not we
   20973                 :             :      * may skip scanning the table.
   20974                 :             :      */
   20975         [ +  + ]:        1742 :     if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
   20976                 :             :     {
   20977         [ +  + ]:          55 :         if (!validate_default)
   20978         [ +  + ]:          41 :             ereport(DEBUG1,
   20979                 :             :                     (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
   20980                 :             :                                      RelationGetRelationName(scanrel))));
   20981                 :             :         else
   20982         [ +  + ]:          14 :             ereport(DEBUG1,
   20983                 :             :                     (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
   20984                 :             :                                      RelationGetRelationName(scanrel))));
   20985                 :          55 :         return;
   20986                 :             :     }
   20987                 :             : 
   20988                 :             :     /*
   20989                 :             :      * Constraints proved insufficient. For plain relations, queue a
   20990                 :             :      * validation item now; for partitioned tables, recurse to process each
   20991                 :             :      * partition.
   20992                 :             :      */
   20993         [ +  + ]:        1687 :     if (scanrel->rd_rel->relkind == RELKIND_RELATION)
   20994                 :             :     {
   20995                 :             :         AlteredTableInfo *tab;
   20996                 :             : 
   20997                 :             :         /* Grab a work queue entry. */
   20998                 :        1412 :         tab = ATGetQueueEntry(wqueue, scanrel);
   20999                 :             :         Assert(tab->partition_constraint == NULL);
   21000                 :        1412 :         tab->partition_constraint = (Expr *) linitial(partConstraint);
   21001                 :        1412 :         tab->validate_default = validate_default;
   21002                 :             :     }
   21003         [ +  + ]:         275 :     else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   21004                 :             :     {
   21005                 :         244 :         PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
   21006                 :             :         int         i;
   21007                 :             : 
   21008         [ +  + ]:         524 :         for (i = 0; i < partdesc->nparts; i++)
   21009                 :             :         {
   21010                 :             :             Relation    part_rel;
   21011                 :             :             List       *thisPartConstraint;
   21012                 :             : 
   21013                 :             :             /*
   21014                 :             :              * This is the minimum lock we need to prevent deadlocks.
   21015                 :             :              */
   21016                 :         280 :             part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
   21017                 :             : 
   21018                 :             :             /*
   21019                 :             :              * Adjust the constraint for scanrel so that it matches this
   21020                 :             :              * partition's attribute numbers.
   21021                 :             :              */
   21022                 :             :             thisPartConstraint =
   21023                 :         280 :                 map_partition_varattnos(partConstraint, 1,
   21024                 :             :                                         part_rel, scanrel);
   21025                 :             : 
   21026                 :         280 :             QueuePartitionConstraintValidation(wqueue, part_rel,
   21027                 :             :                                                thisPartConstraint,
   21028                 :             :                                                validate_default);
   21029                 :         280 :             table_close(part_rel, NoLock);  /* keep lock till commit */
   21030                 :             :         }
   21031                 :             :     }
   21032                 :             : }
   21033                 :             : 
   21034                 :             : /*
   21035                 :             :  * attachPartitionTable: attach a new partition to the partitioned table
   21036                 :             :  *
   21037                 :             :  * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
   21038                 :             :  *   of an ALTER TABLE sequence.
   21039                 :             :  * rel: partitioned relation;
   21040                 :             :  * attachrel: relation of attached partition;
   21041                 :             :  * bound: bounds of attached relation.
   21042                 :             :  */
   21043                 :             : static void
   21044                 :        1985 : attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
   21045                 :             : {
   21046                 :             :     /*
   21047                 :             :      * Create an inheritance; the relevant checks are performed inside the
   21048                 :             :      * function.
   21049                 :             :      */
   21050                 :        1985 :     CreateInheritance(attachrel, rel, true);
   21051                 :             : 
   21052                 :             :     /* Update the pg_class entry. */
   21053                 :        1913 :     StorePartitionBound(attachrel, rel, bound);
   21054                 :             : 
   21055                 :             :     /* Ensure there exists a correct set of indexes in the partition. */
   21056                 :        1913 :     AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
   21057                 :             : 
   21058                 :             :     /* and triggers */
   21059                 :        1893 :     CloneRowTriggersToPartition(rel, attachrel);
   21060                 :             : 
   21061                 :             :     /*
   21062                 :             :      * Clone foreign key constraints.  Callee is responsible for setting up
   21063                 :             :      * for phase 3 constraint verification.
   21064                 :             :      */
   21065                 :        1889 :     CloneForeignKeyConstraints(wqueue, rel, attachrel);
   21066                 :        1877 : }
   21067                 :             : 
   21068                 :             : /*
   21069                 :             :  * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
   21070                 :             :  *
   21071                 :             :  * Return the address of the newly attached partition.
   21072                 :             :  */
   21073                 :             : static ObjectAddress
   21074                 :        1613 : ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
   21075                 :             :                       AlterTableUtilityContext *context)
   21076                 :             : {
   21077                 :             :     Relation    attachrel,
   21078                 :             :                 catalog;
   21079                 :             :     List       *attachrel_children;
   21080                 :             :     List       *partConstraint;
   21081                 :             :     SysScanDesc scan;
   21082                 :             :     ScanKeyData skey;
   21083                 :             :     AttrNumber  attno;
   21084                 :             :     int         natts;
   21085                 :             :     TupleDesc   tupleDesc;
   21086                 :             :     ObjectAddress address;
   21087                 :             :     const char *trigger_name;
   21088                 :             :     Oid         defaultPartOid;
   21089                 :             :     List       *partBoundConstraint;
   21090                 :        1613 :     List       *exceptpuboids = NIL;
   21091                 :        1613 :     ParseState *pstate = make_parsestate(NULL);
   21092                 :             : 
   21093                 :        1613 :     pstate->p_sourcetext = context->queryString;
   21094                 :             : 
   21095                 :             :     /*
   21096                 :             :      * We must lock the default partition if one exists, because attaching a
   21097                 :             :      * new partition will change its partition constraint.
   21098                 :             :      */
   21099                 :             :     defaultPartOid =
   21100                 :        1613 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   21101         [ +  + ]:        1613 :     if (OidIsValid(defaultPartOid))
   21102                 :         117 :         LockRelationOid(defaultPartOid, AccessExclusiveLock);
   21103                 :             : 
   21104                 :        1613 :     attachrel = table_openrv(cmd->name, AccessExclusiveLock);
   21105                 :             : 
   21106                 :             :     /*
   21107                 :             :      * XXX I think it'd be a good idea to grab locks on all tables referenced
   21108                 :             :      * by FKs at this point also.
   21109                 :             :      */
   21110                 :             : 
   21111                 :             :     /*
   21112                 :             :      * Must be owner of both parent and source table -- parent was checked by
   21113                 :             :      * ATSimplePermissions call in ATPrepCmd
   21114                 :             :      */
   21115                 :        1609 :     ATSimplePermissions(AT_AttachPartition, attachrel,
   21116                 :             :                         ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
   21117                 :             : 
   21118                 :             :     /* A partition can only have one parent */
   21119         [ +  + ]:        1605 :     if (attachrel->rd_rel->relispartition)
   21120         [ +  - ]:           4 :         ereport(ERROR,
   21121                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   21122                 :             :                  errmsg("\"%s\" is already a partition",
   21123                 :             :                         RelationGetRelationName(attachrel))));
   21124                 :             : 
   21125         [ +  + ]:        1601 :     if (OidIsValid(attachrel->rd_rel->reloftype))
   21126         [ +  - ]:           4 :         ereport(ERROR,
   21127                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   21128                 :             :                  errmsg("cannot attach a typed table as partition")));
   21129                 :             : 
   21130                 :             :     /*
   21131                 :             :      * Disallow attaching a partition if the table is referenced in a
   21132                 :             :      * publication EXCEPT clause. Changing the partition hierarchy could alter
   21133                 :             :      * the effective publication membership.
   21134                 :             :      */
   21135                 :        1597 :     exceptpuboids = GetRelationExcludedPublications(RelationGetRelid(attachrel));
   21136         [ +  + ]:        1597 :     if (exceptpuboids != NIL)
   21137                 :             :     {
   21138                 :           4 :         bool        first = true;
   21139                 :             :         StringInfoData pubnames;
   21140                 :             : 
   21141                 :           4 :         initStringInfo(&pubnames);
   21142                 :             : 
   21143   [ +  -  +  +  :          12 :         foreach_oid(pubid, exceptpuboids)
                   +  + ]
   21144                 :             :         {
   21145                 :           4 :             char       *pubname = get_publication_name(pubid, false);
   21146                 :             : 
   21147         [ +  - ]:           4 :             if (first)
   21148                 :           4 :                 appendStringInfo(&pubnames, _("\"%s\""), pubname);
   21149                 :             :             else
   21150                 :           0 :                 appendStringInfo(&pubnames, _(", \"%s\""), pubname);
   21151                 :           4 :             first = false;
   21152                 :             :         }
   21153                 :             : 
   21154         [ +  - ]:           4 :         ereport(ERROR,
   21155                 :             :                 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21156                 :             :                 errmsg_plural("cannot attach table \"%s\" as partition because it is referenced in publication %s EXCEPT clause",
   21157                 :             :                               "cannot attach table \"%s\" as partition because it is referenced in publications %s EXCEPT clause",
   21158                 :             :                               list_length(exceptpuboids),
   21159                 :             :                               RelationGetRelationName(attachrel),
   21160                 :             :                               pubnames.data),
   21161                 :             :                 errdetail("The publication EXCEPT clause cannot contain tables that are partitions."),
   21162                 :             :                 errhint("Change the publication's EXCEPT clause using ALTER PUBLICATION ... SET ALL TABLES."));
   21163                 :             :     }
   21164                 :             : 
   21165                 :        1593 :     list_free(exceptpuboids);
   21166                 :             : 
   21167                 :             :     /*
   21168                 :             :      * Table being attached should not already be part of inheritance; either
   21169                 :             :      * as a child table...
   21170                 :             :      */
   21171                 :        1593 :     catalog = table_open(InheritsRelationId, AccessShareLock);
   21172                 :        1593 :     ScanKeyInit(&skey,
   21173                 :             :                 Anum_pg_inherits_inhrelid,
   21174                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   21175                 :             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   21176                 :        1593 :     scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
   21177                 :             :                               NULL, 1, &skey);
   21178         [ +  + ]:        1593 :     if (HeapTupleIsValid(systable_getnext(scan)))
   21179         [ +  - ]:           4 :         ereport(ERROR,
   21180                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   21181                 :             :                  errmsg("cannot attach inheritance child as partition")));
   21182                 :        1589 :     systable_endscan(scan);
   21183                 :             : 
   21184                 :             :     /* ...or as a parent table (except the case when it is partitioned) */
   21185                 :        1589 :     ScanKeyInit(&skey,
   21186                 :             :                 Anum_pg_inherits_inhparent,
   21187                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   21188                 :             :                 ObjectIdGetDatum(RelationGetRelid(attachrel)));
   21189                 :        1589 :     scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
   21190                 :             :                               1, &skey);
   21191         [ +  + ]:        1589 :     if (HeapTupleIsValid(systable_getnext(scan)) &&
   21192         [ +  + ]:         184 :         attachrel->rd_rel->relkind == RELKIND_RELATION)
   21193         [ +  - ]:           4 :         ereport(ERROR,
   21194                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   21195                 :             :                  errmsg("cannot attach inheritance parent as partition")));
   21196                 :        1585 :     systable_endscan(scan);
   21197                 :        1585 :     table_close(catalog, AccessShareLock);
   21198                 :             : 
   21199                 :             :     /*
   21200                 :             :      * Prevent circularity by seeing if rel is a partition of attachrel. (In
   21201                 :             :      * particular, this disallows making a rel a partition of itself.)
   21202                 :             :      *
   21203                 :             :      * We do that by checking if rel is a member of the list of attachrel's
   21204                 :             :      * partitions provided the latter is partitioned at all.  We want to avoid
   21205                 :             :      * having to construct this list again, so we request the strongest lock
   21206                 :             :      * on all partitions.  We need the strongest lock, because we may decide
   21207                 :             :      * to scan them if we find out that the table being attached (or its leaf
   21208                 :             :      * partitions) may contain rows that violate the partition constraint. If
   21209                 :             :      * the table has a constraint that would prevent such rows, which by
   21210                 :             :      * definition is present in all the partitions, we need not scan the
   21211                 :             :      * table, nor its partitions.  But we cannot risk a deadlock by taking a
   21212                 :             :      * weaker lock now and the stronger one only when needed.
   21213                 :             :      */
   21214                 :        1585 :     attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
   21215                 :             :                                              AccessExclusiveLock, NULL);
   21216         [ +  + ]:        1585 :     if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
   21217         [ +  - ]:           8 :         ereport(ERROR,
   21218                 :             :                 (errcode(ERRCODE_DUPLICATE_TABLE),
   21219                 :             :                  errmsg("circular inheritance not allowed"),
   21220                 :             :                  errdetail("\"%s\" is already a child of \"%s\".",
   21221                 :             :                            RelationGetRelationName(rel),
   21222                 :             :                            RelationGetRelationName(attachrel))));
   21223                 :             : 
   21224                 :             :     /* If the parent is permanent, so must be all of its partitions. */
   21225         [ +  + ]:        1577 :     if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
   21226         [ +  + ]:        1549 :         attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
   21227         [ +  - ]:           4 :         ereport(ERROR,
   21228                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   21229                 :             :                  errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
   21230                 :             :                         RelationGetRelationName(rel))));
   21231                 :             : 
   21232                 :             :     /* Temp parent cannot have a partition that is itself not a temp */
   21233         [ +  + ]:        1573 :     if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
   21234         [ +  + ]:          28 :         attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
   21235         [ +  - ]:          12 :         ereport(ERROR,
   21236                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   21237                 :             :                  errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
   21238                 :             :                         RelationGetRelationName(rel))));
   21239                 :             : 
   21240                 :             :     /* If the parent is temp, it must belong to this session */
   21241   [ +  +  -  + ]:        1561 :     if (RELATION_IS_OTHER_TEMP(rel))
   21242         [ #  # ]:           0 :         ereport(ERROR,
   21243                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   21244                 :             :                  errmsg("cannot attach as partition of temporary relation of another session")));
   21245                 :             : 
   21246                 :             :     /* Ditto for the partition */
   21247   [ +  +  -  + ]:        1561 :     if (RELATION_IS_OTHER_TEMP(attachrel))
   21248         [ #  # ]:           0 :         ereport(ERROR,
   21249                 :             :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   21250                 :             :                  errmsg("cannot attach temporary relation of another session as partition")));
   21251                 :             : 
   21252                 :             :     /*
   21253                 :             :      * Check if attachrel has any identity columns or any columns that aren't
   21254                 :             :      * in the parent.
   21255                 :             :      */
   21256                 :        1561 :     tupleDesc = RelationGetDescr(attachrel);
   21257                 :        1561 :     natts = tupleDesc->natts;
   21258         [ +  + ]:        5409 :     for (attno = 1; attno <= natts; attno++)
   21259                 :             :     {
   21260                 :        3876 :         Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
   21261                 :        3876 :         char       *attributeName = NameStr(attribute->attname);
   21262                 :             : 
   21263                 :             :         /* Ignore dropped */
   21264         [ +  + ]:        3876 :         if (attribute->attisdropped)
   21265                 :         416 :             continue;
   21266                 :             : 
   21267         [ +  + ]:        3460 :         if (attribute->attidentity)
   21268         [ +  - ]:          16 :             ereport(ERROR,
   21269                 :             :                     errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21270                 :             :                     errmsg("table \"%s\" being attached contains an identity column \"%s\"",
   21271                 :             :                            RelationGetRelationName(attachrel), attributeName),
   21272                 :             :                     errdetail("The new partition may not contain an identity column."));
   21273                 :             : 
   21274                 :             :         /* Try to find the column in parent (matching on column name) */
   21275         [ +  + ]:        3444 :         if (!SearchSysCacheExists2(ATTNAME,
   21276                 :             :                                    ObjectIdGetDatum(RelationGetRelid(rel)),
   21277                 :             :                                    CStringGetDatum(attributeName)))
   21278         [ +  - ]:          12 :             ereport(ERROR,
   21279                 :             :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
   21280                 :             :                      errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
   21281                 :             :                             RelationGetRelationName(attachrel), attributeName,
   21282                 :             :                             RelationGetRelationName(rel)),
   21283                 :             :                      errdetail("The new partition may contain only the columns present in parent.")));
   21284                 :             :     }
   21285                 :             : 
   21286                 :             :     /*
   21287                 :             :      * If child_rel has row-level triggers with transition tables, we
   21288                 :             :      * currently don't allow it to become a partition.  See also prohibitions
   21289                 :             :      * in ATExecAddInherit() and CreateTrigger().
   21290                 :             :      */
   21291                 :        1533 :     trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
   21292         [ +  + ]:        1533 :     if (trigger_name != NULL)
   21293         [ +  - ]:           4 :         ereport(ERROR,
   21294                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   21295                 :             :                  errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
   21296                 :             :                         trigger_name, RelationGetRelationName(attachrel)),
   21297                 :             :                  errdetail("ROW triggers with transition tables are not supported on partitions.")));
   21298                 :             : 
   21299                 :             :     /*
   21300                 :             :      * Check that the new partition's bound is valid and does not overlap any
   21301                 :             :      * of existing partitions of the parent - note that it does not return on
   21302                 :             :      * error.
   21303                 :             :      */
   21304                 :        1529 :     check_new_partition_bound(RelationGetRelationName(attachrel), rel,
   21305                 :             :                               cmd->bound, pstate);
   21306                 :             : 
   21307                 :        1505 :     attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
   21308                 :             : 
   21309                 :             :     /*
   21310                 :             :      * Generate a partition constraint from the partition bound specification.
   21311                 :             :      * If the parent itself is a partition, make sure to include its
   21312                 :             :      * constraint as well.
   21313                 :             :      */
   21314                 :        1397 :     partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
   21315                 :             : 
   21316                 :             :     /*
   21317                 :             :      * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
   21318                 :             :      * since it's needed later to construct the constraint expression for
   21319                 :             :      * validating against the default partition, if any.
   21320                 :             :      */
   21321                 :        1397 :     partConstraint = list_concat_copy(partBoundConstraint,
   21322                 :        1397 :                                       RelationGetPartitionQual(rel));
   21323                 :             : 
   21324                 :             :     /* Skip validation if there are no constraints to validate. */
   21325         [ +  + ]:        1397 :     if (partConstraint)
   21326                 :             :     {
   21327                 :             :         /*
   21328                 :             :          * Run the partition quals through const-simplification similar to
   21329                 :             :          * check constraints.  We skip canonicalize_qual, though, because
   21330                 :             :          * partition quals should be in canonical form already.
   21331                 :             :          */
   21332                 :             :         partConstraint =
   21333                 :        1369 :             (List *) eval_const_expressions(NULL,
   21334                 :             :                                             (Node *) partConstraint);
   21335                 :             : 
   21336                 :             :         /* XXX this sure looks wrong */
   21337                 :        1369 :         partConstraint = list_make1(make_ands_explicit(partConstraint));
   21338                 :             : 
   21339                 :             :         /*
   21340                 :             :          * Adjust the generated constraint to match this partition's attribute
   21341                 :             :          * numbers.
   21342                 :             :          */
   21343                 :        1369 :         partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
   21344                 :             :                                                  rel);
   21345                 :             : 
   21346                 :             :         /* Validate partition constraints against the table being attached. */
   21347                 :        1369 :         QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
   21348                 :             :                                            false);
   21349                 :             :     }
   21350                 :             : 
   21351                 :             :     /*
   21352                 :             :      * If we're attaching a partition other than the default partition and a
   21353                 :             :      * default one exists, then that partition's partition constraint changes,
   21354                 :             :      * so add an entry to the work queue to validate it, too.  (We must not do
   21355                 :             :      * this when the partition being attached is the default one; we already
   21356                 :             :      * did it above!)
   21357                 :             :      */
   21358         [ +  + ]:        1397 :     if (OidIsValid(defaultPartOid))
   21359                 :             :     {
   21360                 :             :         Relation    defaultrel;
   21361                 :             :         List       *defPartConstraint;
   21362                 :             : 
   21363                 :             :         Assert(!cmd->bound->is_default);
   21364                 :             : 
   21365                 :             :         /* we already hold a lock on the default partition */
   21366                 :          93 :         defaultrel = table_open(defaultPartOid, NoLock);
   21367                 :             :         defPartConstraint =
   21368                 :          93 :             get_proposed_default_constraint(partBoundConstraint);
   21369                 :             : 
   21370                 :             :         /*
   21371                 :             :          * Map the Vars in the constraint expression from rel's attnos to
   21372                 :             :          * defaultrel's.
   21373                 :             :          */
   21374                 :             :         defPartConstraint =
   21375                 :          93 :             map_partition_varattnos(defPartConstraint,
   21376                 :             :                                     1, defaultrel, rel);
   21377                 :          93 :         QueuePartitionConstraintValidation(wqueue, defaultrel,
   21378                 :             :                                            defPartConstraint, true);
   21379                 :             : 
   21380                 :             :         /* keep our lock until commit. */
   21381                 :          93 :         table_close(defaultrel, NoLock);
   21382                 :             :     }
   21383                 :             : 
   21384                 :        1397 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
   21385                 :             : 
   21386                 :             :     /*
   21387                 :             :      * If the partition we just attached is partitioned itself, invalidate
   21388                 :             :      * relcache for all descendent partitions too to ensure that their
   21389                 :             :      * rd_partcheck expression trees are rebuilt; partitions already locked at
   21390                 :             :      * the beginning of this function.
   21391                 :             :      */
   21392         [ +  + ]:        1397 :     if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   21393                 :             :     {
   21394                 :             :         ListCell   *l;
   21395                 :             : 
   21396   [ +  -  +  +  :         711 :         foreach(l, attachrel_children)
                   +  + ]
   21397                 :             :         {
   21398                 :         482 :             CacheInvalidateRelcacheByRelid(lfirst_oid(l));
   21399                 :             :         }
   21400                 :             :     }
   21401                 :             : 
   21402                 :             :     /* keep our lock until commit */
   21403                 :        1397 :     table_close(attachrel, NoLock);
   21404                 :             : 
   21405                 :        1397 :     return address;
   21406                 :             : }
   21407                 :             : 
   21408                 :             : /*
   21409                 :             :  * AttachPartitionEnsureIndexes
   21410                 :             :  *      subroutine for ATExecAttachPartition to create/match indexes
   21411                 :             :  *
   21412                 :             :  * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
   21413                 :             :  * PARTITION: every partition must have an index attached to each index on the
   21414                 :             :  * partitioned table.
   21415                 :             :  */
   21416                 :             : static void
   21417                 :        1913 : AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
   21418                 :             : {
   21419                 :             :     List       *idxes;
   21420                 :             :     List       *attachRelIdxs;
   21421                 :             :     Relation   *attachrelIdxRels;
   21422                 :             :     IndexInfo **attachInfos;
   21423                 :             :     ListCell   *cell;
   21424                 :             :     MemoryContext cxt;
   21425                 :             :     MemoryContext oldcxt;
   21426                 :             : 
   21427                 :        1913 :     cxt = AllocSetContextCreate(CurrentMemoryContext,
   21428                 :             :                                 "AttachPartitionEnsureIndexes",
   21429                 :             :                                 ALLOCSET_DEFAULT_SIZES);
   21430                 :        1913 :     oldcxt = MemoryContextSwitchTo(cxt);
   21431                 :             : 
   21432                 :        1913 :     idxes = RelationGetIndexList(rel);
   21433                 :        1913 :     attachRelIdxs = RelationGetIndexList(attachrel);
   21434                 :        1913 :     attachrelIdxRels = palloc_array(Relation, list_length(attachRelIdxs));
   21435                 :        1913 :     attachInfos = palloc_array(IndexInfo *, list_length(attachRelIdxs));
   21436                 :             : 
   21437                 :             :     /* Build arrays of all existing indexes and their IndexInfos */
   21438   [ +  +  +  +  :        4095 :     foreach_oid(cldIdxId, attachRelIdxs)
                   +  + ]
   21439                 :             :     {
   21440                 :         269 :         int         i = foreach_current_index(cldIdxId);
   21441                 :             : 
   21442                 :         269 :         attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
   21443                 :         269 :         attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
   21444                 :             :     }
   21445                 :             : 
   21446                 :             :     /*
   21447                 :             :      * If we're attaching a foreign table, we must fail if any of the indexes
   21448                 :             :      * is a constraint index; otherwise, there's nothing to do here.  Do this
   21449                 :             :      * before starting work, to avoid wasting the effort of building a few
   21450                 :             :      * non-unique indexes before coming across a unique one.
   21451                 :             :      */
   21452         [ +  + ]:        1913 :     if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
   21453                 :             :     {
   21454   [ +  +  +  +  :          55 :         foreach(cell, idxes)
                   +  + ]
   21455                 :             :         {
   21456                 :          24 :             Oid         idx = lfirst_oid(cell);
   21457                 :          24 :             Relation    idxRel = index_open(idx, AccessShareLock);
   21458                 :             : 
   21459         [ +  + ]:          24 :             if (idxRel->rd_index->indisunique ||
   21460         [ -  + ]:          16 :                 idxRel->rd_index->indisprimary)
   21461         [ +  - ]:           8 :                 ereport(ERROR,
   21462                 :             :                         (errcode(ERRCODE_WRONG_OBJECT_TYPE),
   21463                 :             :                          errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
   21464                 :             :                                 RelationGetRelationName(attachrel),
   21465                 :             :                                 RelationGetRelationName(rel)),
   21466                 :             :                          errdetail("Partitioned table \"%s\" contains unique indexes.",
   21467                 :             :                                    RelationGetRelationName(rel))));
   21468                 :          16 :             index_close(idxRel, AccessShareLock);
   21469                 :             :         }
   21470                 :             : 
   21471                 :          31 :         goto out;
   21472                 :             :     }
   21473                 :             : 
   21474                 :             :     /*
   21475                 :             :      * For each index on the partitioned table, find a matching one in the
   21476                 :             :      * partition-to-be; if one is not found, create one.
   21477                 :             :      */
   21478   [ +  +  +  +  :        2355 :     foreach(cell, idxes)
                   +  + ]
   21479                 :             :     {
   21480                 :         493 :         Oid         idx = lfirst_oid(cell);
   21481                 :         493 :         Relation    idxRel = index_open(idx, AccessShareLock);
   21482                 :             :         IndexInfo  *info;
   21483                 :             :         AttrMap    *attmap;
   21484                 :         493 :         bool        found = false;
   21485                 :             :         Oid         constraintOid;
   21486                 :             : 
   21487                 :             :         /*
   21488                 :             :          * Ignore indexes in the partitioned table other than partitioned
   21489                 :             :          * indexes.
   21490                 :             :          */
   21491         [ -  + ]:         493 :         if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
   21492                 :             :         {
   21493                 :           0 :             index_close(idxRel, AccessShareLock);
   21494                 :           0 :             continue;
   21495                 :             :         }
   21496                 :             : 
   21497                 :             :         /* construct an indexinfo to compare existing indexes against */
   21498                 :         493 :         info = BuildIndexInfo(idxRel);
   21499                 :         493 :         attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
   21500                 :             :                                        RelationGetDescr(rel),
   21501                 :             :                                        false);
   21502                 :         493 :         constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
   21503                 :             : 
   21504                 :             :         /*
   21505                 :             :          * Scan the list of existing indexes in the partition-to-be, and mark
   21506                 :             :          * the first matching, valid, unattached one we find, if any, as
   21507                 :             :          * partition of the parent index.  If we find one, we're done.
   21508                 :             :          */
   21509         [ +  + ]:         533 :         for (int i = 0; i < list_length(attachRelIdxs); i++)
   21510                 :             :         {
   21511                 :         197 :             Oid         cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
   21512                 :         197 :             Oid         cldConstrOid = InvalidOid;
   21513                 :             : 
   21514                 :             :             /* does this index have a parent?  if so, can't use it */
   21515         [ +  + ]:         197 :             if (attachrelIdxRels[i]->rd_rel->relispartition)
   21516                 :           8 :                 continue;
   21517                 :             : 
   21518                 :             :             /* If this index is invalid, can't use it */
   21519         [ +  + ]:         189 :             if (!attachrelIdxRels[i]->rd_index->indisvalid)
   21520                 :           4 :                 continue;
   21521                 :             : 
   21522         [ +  + ]:         185 :             if (CompareIndexInfo(attachInfos[i], info,
   21523                 :         185 :                                  attachrelIdxRels[i]->rd_indcollation,
   21524                 :         185 :                                  idxRel->rd_indcollation,
   21525                 :         185 :                                  attachrelIdxRels[i]->rd_opfamily,
   21526                 :         185 :                                  idxRel->rd_opfamily,
   21527                 :             :                                  attmap))
   21528                 :             :             {
   21529                 :             :                 /*
   21530                 :             :                  * If this index is being created in the parent because of a
   21531                 :             :                  * constraint, then the child needs to have a constraint also,
   21532                 :             :                  * so look for one.  If there is no such constraint, this
   21533                 :             :                  * index is no good, so keep looking.
   21534                 :             :                  */
   21535         [ +  + ]:         161 :                 if (OidIsValid(constraintOid))
   21536                 :             :                 {
   21537                 :             :                     cldConstrOid =
   21538                 :          96 :                         get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
   21539                 :             :                                                         cldIdxId);
   21540                 :             :                     /* no dice */
   21541         [ +  + ]:          96 :                     if (!OidIsValid(cldConstrOid))
   21542                 :           4 :                         continue;
   21543                 :             : 
   21544                 :             :                     /* Ensure they're both the same type of constraint */
   21545         [ -  + ]:         184 :                     if (get_constraint_type(constraintOid) !=
   21546                 :          92 :                         get_constraint_type(cldConstrOid))
   21547                 :           0 :                         continue;
   21548                 :             :                 }
   21549                 :             : 
   21550                 :             :                 /* bingo. */
   21551                 :         157 :                 IndexSetParentIndex(attachrelIdxRels[i], idx);
   21552         [ +  + ]:         157 :                 if (OidIsValid(constraintOid))
   21553                 :          92 :                     ConstraintSetParentConstraint(cldConstrOid, constraintOid,
   21554                 :             :                                                   RelationGetRelid(attachrel));
   21555                 :         157 :                 found = true;
   21556                 :             : 
   21557                 :         157 :                 CommandCounterIncrement();
   21558                 :         157 :                 break;
   21559                 :             :             }
   21560                 :             :         }
   21561                 :             : 
   21562                 :             :         /*
   21563                 :             :          * If no suitable index was found in the partition-to-be, create one
   21564                 :             :          * now.  Note that if this is a PK, not-null constraints must already
   21565                 :             :          * exist.
   21566                 :             :          */
   21567         [ +  + ]:         493 :         if (!found)
   21568                 :             :         {
   21569                 :             :             IndexStmt  *stmt;
   21570                 :             :             Oid         conOid;
   21571                 :             : 
   21572                 :         336 :             stmt = generateClonedIndexStmt(NULL,
   21573                 :             :                                            idxRel, attmap,
   21574                 :             :                                            &conOid);
   21575                 :         336 :             DefineIndex(NULL,
   21576                 :             :                         RelationGetRelid(attachrel), stmt, InvalidOid,
   21577                 :             :                         RelationGetRelid(idxRel),
   21578                 :             :                         conOid,
   21579                 :             :                         -1,
   21580                 :             :                         true, false, false, false, false);
   21581                 :             :         }
   21582                 :             : 
   21583                 :         481 :         index_close(idxRel, AccessShareLock);
   21584                 :             :     }
   21585                 :             : 
   21586                 :        1893 : out:
   21587                 :             :     /* Clean up. */
   21588         [ +  + ]:        2154 :     for (int i = 0; i < list_length(attachRelIdxs); i++)
   21589                 :         261 :         index_close(attachrelIdxRels[i], AccessShareLock);
   21590                 :        1893 :     MemoryContextSwitchTo(oldcxt);
   21591                 :        1893 :     MemoryContextDelete(cxt);
   21592                 :        1893 : }
   21593                 :             : 
   21594                 :             : /*
   21595                 :             :  * CloneRowTriggersToPartition
   21596                 :             :  *      subroutine for ATExecAttachPartition/DefineRelation to create row
   21597                 :             :  *      triggers on partitions
   21598                 :             :  */
   21599                 :             : static void
   21600                 :        2153 : CloneRowTriggersToPartition(Relation parent, Relation partition)
   21601                 :             : {
   21602                 :             :     Relation    pg_trigger;
   21603                 :             :     ScanKeyData key;
   21604                 :             :     SysScanDesc scan;
   21605                 :             :     HeapTuple   tuple;
   21606                 :             :     MemoryContext perTupCxt;
   21607                 :             : 
   21608                 :        2153 :     ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   21609                 :             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
   21610                 :        2153 :     pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
   21611                 :        2153 :     scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
   21612                 :             :                               true, NULL, 1, &key);
   21613                 :             : 
   21614                 :        2153 :     perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
   21615                 :             :                                       "clone trig", ALLOCSET_SMALL_SIZES);
   21616                 :             : 
   21617         [ +  + ]:        3382 :     while (HeapTupleIsValid(tuple = systable_getnext(scan)))
   21618                 :             :     {
   21619                 :        1233 :         Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
   21620                 :             :         CreateTrigStmt *trigStmt;
   21621                 :        1233 :         Node       *qual = NULL;
   21622                 :             :         Datum       value;
   21623                 :             :         bool        isnull;
   21624                 :        1233 :         List       *cols = NIL;
   21625                 :        1233 :         List       *trigargs = NIL;
   21626                 :             :         MemoryContext oldcxt;
   21627                 :             : 
   21628                 :             :         /*
   21629                 :             :          * Ignore statement-level triggers; those are not cloned.
   21630                 :             :          */
   21631         [ +  + ]:        1233 :         if (!TRIGGER_FOR_ROW(trigForm->tgtype))
   21632                 :        1102 :             continue;
   21633                 :             : 
   21634                 :             :         /*
   21635                 :             :          * Don't clone internal triggers, because the constraint cloning code
   21636                 :             :          * will.
   21637                 :             :          */
   21638         [ +  + ]:        1205 :         if (trigForm->tgisinternal)
   21639                 :        1074 :             continue;
   21640                 :             : 
   21641                 :             :         /*
   21642                 :             :          * Complain if we find an unexpected trigger type.
   21643                 :             :          */
   21644         [ +  + ]:         131 :         if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
   21645         [ -  + ]:         107 :             !TRIGGER_FOR_AFTER(trigForm->tgtype))
   21646         [ #  # ]:           0 :             elog(ERROR, "unexpected trigger \"%s\" found",
   21647                 :             :                  NameStr(trigForm->tgname));
   21648                 :             : 
   21649                 :             :         /* Use short-lived context for CREATE TRIGGER */
   21650                 :         131 :         oldcxt = MemoryContextSwitchTo(perTupCxt);
   21651                 :             : 
   21652                 :             :         /*
   21653                 :             :          * If there is a WHEN clause, generate a 'cooked' version of it that's
   21654                 :             :          * appropriate for the partition.
   21655                 :             :          */
   21656                 :         131 :         value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
   21657                 :             :                              RelationGetDescr(pg_trigger), &isnull);
   21658         [ +  + ]:         131 :         if (!isnull)
   21659                 :             :         {
   21660                 :           4 :             qual = stringToNode(TextDatumGetCString(value));
   21661                 :           4 :             qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
   21662                 :             :                                                     partition, parent);
   21663                 :           4 :             qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
   21664                 :             :                                                     partition, parent);
   21665                 :             :         }
   21666                 :             : 
   21667                 :             :         /*
   21668                 :             :          * If there is a column list, transform it to a list of column names.
   21669                 :             :          * Note we don't need to map this list in any way ...
   21670                 :             :          */
   21671         [ +  + ]:         131 :         if (trigForm->tgattr.dim1 > 0)
   21672                 :             :         {
   21673                 :             :             int         i;
   21674                 :             : 
   21675         [ +  + ]:           8 :             for (i = 0; i < trigForm->tgattr.dim1; i++)
   21676                 :             :             {
   21677                 :             :                 Form_pg_attribute col;
   21678                 :             : 
   21679                 :           4 :                 col = TupleDescAttr(parent->rd_att,
   21680                 :           4 :                                     trigForm->tgattr.values[i] - 1);
   21681                 :           4 :                 cols = lappend(cols,
   21682                 :           4 :                                makeString(pstrdup(NameStr(col->attname))));
   21683                 :             :             }
   21684                 :             :         }
   21685                 :             : 
   21686                 :             :         /* Reconstruct trigger arguments list. */
   21687         [ +  + ]:         131 :         if (trigForm->tgnargs > 0)
   21688                 :             :         {
   21689                 :             :             char       *p;
   21690                 :             : 
   21691                 :          36 :             value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
   21692                 :             :                                  RelationGetDescr(pg_trigger), &isnull);
   21693         [ -  + ]:          36 :             if (isnull)
   21694         [ #  # ]:           0 :                 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
   21695                 :             :                      NameStr(trigForm->tgname), RelationGetRelationName(partition));
   21696                 :             : 
   21697                 :          36 :             p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
   21698                 :             : 
   21699         [ +  + ]:          80 :             for (int i = 0; i < trigForm->tgnargs; i++)
   21700                 :             :             {
   21701                 :          44 :                 trigargs = lappend(trigargs, makeString(pstrdup(p)));
   21702                 :          44 :                 p += strlen(p) + 1;
   21703                 :             :             }
   21704                 :             :         }
   21705                 :             : 
   21706                 :         131 :         trigStmt = makeNode(CreateTrigStmt);
   21707                 :         131 :         trigStmt->replace = false;
   21708                 :         131 :         trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
   21709                 :         131 :         trigStmt->trigname = NameStr(trigForm->tgname);
   21710                 :         131 :         trigStmt->relation = NULL;
   21711                 :         131 :         trigStmt->funcname = NULL;   /* passed separately */
   21712                 :         131 :         trigStmt->args = trigargs;
   21713                 :         131 :         trigStmt->row = true;
   21714                 :         131 :         trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
   21715                 :         131 :         trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
   21716                 :         131 :         trigStmt->columns = cols;
   21717                 :         131 :         trigStmt->whenClause = NULL; /* passed separately */
   21718                 :         131 :         trigStmt->transitionRels = NIL; /* not supported at present */
   21719                 :         131 :         trigStmt->deferrable = trigForm->tgdeferrable;
   21720                 :         131 :         trigStmt->initdeferred = trigForm->tginitdeferred;
   21721                 :         131 :         trigStmt->constrrel = NULL; /* passed separately */
   21722                 :             : 
   21723                 :         131 :         CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
   21724                 :             :                               trigForm->tgconstrrelid, InvalidOid, InvalidOid,
   21725                 :             :                               trigForm->tgfoid, trigForm->oid, qual,
   21726                 :         131 :                               false, true, trigForm->tgenabled);
   21727                 :             : 
   21728                 :         127 :         MemoryContextSwitchTo(oldcxt);
   21729                 :         127 :         MemoryContextReset(perTupCxt);
   21730                 :             :     }
   21731                 :             : 
   21732                 :        2149 :     MemoryContextDelete(perTupCxt);
   21733                 :             : 
   21734                 :        2149 :     systable_endscan(scan);
   21735                 :        2149 :     table_close(pg_trigger, RowExclusiveLock);
   21736                 :        2149 : }
   21737                 :             : 
   21738                 :             : /*
   21739                 :             :  * ALTER TABLE DETACH PARTITION
   21740                 :             :  *
   21741                 :             :  * Return the address of the relation that is no longer a partition of rel.
   21742                 :             :  *
   21743                 :             :  * If concurrent mode is requested, we run in two transactions.  A side-
   21744                 :             :  * effect is that this command cannot run in a multi-part ALTER TABLE.
   21745                 :             :  * Currently, that's enforced by the grammar.
   21746                 :             :  *
   21747                 :             :  * The strategy for concurrency is to first modify the partition's
   21748                 :             :  * pg_inherit catalog row to make it visible to everyone that the
   21749                 :             :  * partition is detached, lock the partition against writes, and commit
   21750                 :             :  * the transaction; anyone who requests the partition descriptor from
   21751                 :             :  * that point onwards has to ignore such a partition.  In a second
   21752                 :             :  * transaction, we wait until all transactions that could have seen the
   21753                 :             :  * partition as attached are gone, then we remove the rest of partition
   21754                 :             :  * metadata (pg_inherits and pg_class.relpartbounds).
   21755                 :             :  */
   21756                 :             : static ObjectAddress
   21757                 :         373 : ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
   21758                 :             :                       RangeVar *name, bool concurrent)
   21759                 :             : {
   21760                 :             :     Relation    partRel;
   21761                 :             :     ObjectAddress address;
   21762                 :             :     Oid         defaultPartOid;
   21763                 :             : 
   21764                 :             :     /*
   21765                 :             :      * We must lock the default partition, because detaching this partition
   21766                 :             :      * will change its partition constraint.
   21767                 :             :      */
   21768                 :             :     defaultPartOid =
   21769                 :         373 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   21770         [ +  + ]:         373 :     if (OidIsValid(defaultPartOid))
   21771                 :             :     {
   21772                 :             :         /*
   21773                 :             :          * Concurrent detaching when a default partition exists is not
   21774                 :             :          * supported. The main problem is that the default partition
   21775                 :             :          * constraint would change.  And there's a definitional problem: what
   21776                 :             :          * should happen to the tuples that are being inserted that belong to
   21777                 :             :          * the partition being detached?  Putting them on the partition being
   21778                 :             :          * detached would be wrong, since they'd become "lost" after the
   21779                 :             :          * detaching completes but we cannot put them in the default partition
   21780                 :             :          * either until we alter its partition constraint.
   21781                 :             :          *
   21782                 :             :          * I think we could solve this problem if we effected the constraint
   21783                 :             :          * change before committing the first transaction.  But the lock would
   21784                 :             :          * have to remain AEL and it would cause concurrent query planning to
   21785                 :             :          * be blocked, so changing it that way would be even worse.
   21786                 :             :          */
   21787         [ +  + ]:          74 :         if (concurrent)
   21788         [ +  - ]:           8 :             ereport(ERROR,
   21789                 :             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21790                 :             :                      errmsg("cannot detach partitions concurrently when a default partition exists")));
   21791                 :          66 :         LockRelationOid(defaultPartOid, AccessExclusiveLock);
   21792                 :             :     }
   21793                 :             : 
   21794                 :             :     /*
   21795                 :             :      * In concurrent mode, the partition is locked with share-update-exclusive
   21796                 :             :      * in the first transaction.  This allows concurrent transactions to be
   21797                 :             :      * doing DML to the partition.
   21798                 :             :      */
   21799         [ +  + ]:         365 :     partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
   21800                 :             :                            AccessExclusiveLock);
   21801                 :             : 
   21802                 :             :     /*
   21803                 :             :      * Check inheritance conditions and either delete the pg_inherits row (in
   21804                 :             :      * non-concurrent mode) or just set the inhdetachpending flag.
   21805                 :             :      */
   21806         [ +  + ]:         357 :     if (!concurrent)
   21807                 :         282 :         RemoveInheritance(partRel, rel, false);
   21808                 :             :     else
   21809                 :          75 :         MarkInheritDetached(partRel, rel);
   21810                 :             : 
   21811                 :             :     /*
   21812                 :             :      * Ensure that foreign keys still hold after this detach.  This keeps
   21813                 :             :      * locks on the referencing tables, which prevents concurrent transactions
   21814                 :             :      * from adding rows that we wouldn't see.  For this to work in concurrent
   21815                 :             :      * mode, it is critical that the partition appears as no longer attached
   21816                 :             :      * for the RI queries as soon as the first transaction commits.
   21817                 :             :      */
   21818                 :         344 :     ATDetachCheckNoForeignKeyRefs(partRel);
   21819                 :             : 
   21820                 :             :     /*
   21821                 :             :      * Concurrent mode has to work harder; first we add a new constraint to
   21822                 :             :      * the partition that matches the partition constraint.  Then we close our
   21823                 :             :      * existing transaction, and in a new one wait for all processes to catch
   21824                 :             :      * up on the catalog updates we've done so far; at that point we can
   21825                 :             :      * complete the operation.
   21826                 :             :      */
   21827         [ +  + ]:         322 :     if (concurrent)
   21828                 :             :     {
   21829                 :             :         Oid         partrelid,
   21830                 :             :                     parentrelid;
   21831                 :             :         LOCKTAG     tag;
   21832                 :             :         char       *parentrelname;
   21833                 :             :         char       *partrelname;
   21834                 :             : 
   21835                 :             :         /*
   21836                 :             :          * We're almost done now; the only traces that remain are the
   21837                 :             :          * pg_inherits tuple and the partition's relpartbounds.  Before we can
   21838                 :             :          * remove those, we need to wait until all transactions that know that
   21839                 :             :          * this is a partition are gone.
   21840                 :             :          */
   21841                 :             : 
   21842                 :             :         /*
   21843                 :             :          * Remember relation OIDs to re-acquire them later; and relation names
   21844                 :             :          * too, for error messages if something is dropped in between.
   21845                 :             :          */
   21846                 :          72 :         partrelid = RelationGetRelid(partRel);
   21847                 :          72 :         parentrelid = RelationGetRelid(rel);
   21848                 :          72 :         parentrelname = MemoryContextStrdup(PortalContext,
   21849                 :          72 :                                             RelationGetRelationName(rel));
   21850                 :          72 :         partrelname = MemoryContextStrdup(PortalContext,
   21851                 :          72 :                                           RelationGetRelationName(partRel));
   21852                 :             : 
   21853                 :             :         /* Invalidate relcache entries for the parent -- must be before close */
   21854                 :          72 :         CacheInvalidateRelcache(rel);
   21855                 :             : 
   21856                 :          72 :         table_close(partRel, NoLock);
   21857                 :          72 :         table_close(rel, NoLock);
   21858                 :          72 :         tab->rel = NULL;
   21859                 :             : 
   21860                 :             :         /* Make updated catalog entry visible */
   21861                 :          72 :         PopActiveSnapshot();
   21862                 :          72 :         CommitTransactionCommand();
   21863                 :             : 
   21864                 :          72 :         StartTransactionCommand();
   21865                 :             : 
   21866                 :             :         /*
   21867                 :             :          * Now wait.  This ensures that all queries that were planned
   21868                 :             :          * including the partition are finished before we remove the rest of
   21869                 :             :          * catalog entries.  We don't need or indeed want to acquire this
   21870                 :             :          * lock, though -- that would block later queries.
   21871                 :             :          *
   21872                 :             :          * We don't need to concern ourselves with waiting for a lock on the
   21873                 :             :          * partition itself, since we will acquire AccessExclusiveLock below.
   21874                 :             :          */
   21875                 :          72 :         SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
   21876                 :          72 :         WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
   21877                 :             : 
   21878                 :             :         /*
   21879                 :             :          * Now acquire locks in both relations again.  Note they may have been
   21880                 :             :          * removed in the meantime, so care is required.
   21881                 :             :          */
   21882                 :          47 :         rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
   21883                 :          47 :         partRel = try_relation_open(partrelid, AccessExclusiveLock);
   21884                 :             : 
   21885                 :             :         /* If the relations aren't there, something bad happened; bail out */
   21886         [ -  + ]:          47 :         if (rel == NULL)
   21887                 :             :         {
   21888         [ #  # ]:           0 :             if (partRel != NULL)    /* shouldn't happen */
   21889         [ #  # ]:           0 :                 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
   21890                 :             :                      partrelname);
   21891         [ #  # ]:           0 :             ereport(ERROR,
   21892                 :             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21893                 :             :                      errmsg("partitioned table \"%s\" was removed concurrently",
   21894                 :             :                             parentrelname)));
   21895                 :             :         }
   21896         [ -  + ]:          47 :         if (partRel == NULL)
   21897         [ #  # ]:           0 :             ereport(ERROR,
   21898                 :             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   21899                 :             :                      errmsg("partition \"%s\" was removed concurrently", partrelname)));
   21900                 :             : 
   21901                 :          47 :         tab->rel = rel;
   21902                 :             :     }
   21903                 :             : 
   21904                 :             :     /*
   21905                 :             :      * Detaching the partition might involve TOAST table access, so ensure we
   21906                 :             :      * have a valid snapshot.
   21907                 :             :      */
   21908                 :         297 :     PushActiveSnapshot(GetTransactionSnapshot());
   21909                 :             : 
   21910                 :             :     /* Do the final part of detaching */
   21911                 :         297 :     DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
   21912                 :             : 
   21913                 :         296 :     PopActiveSnapshot();
   21914                 :             : 
   21915                 :         296 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   21916                 :             : 
   21917                 :             :     /* keep our lock until commit */
   21918                 :         296 :     table_close(partRel, NoLock);
   21919                 :             : 
   21920                 :         296 :     return address;
   21921                 :             : }
   21922                 :             : 
   21923                 :             : /*
   21924                 :             :  * Second part of ALTER TABLE .. DETACH.
   21925                 :             :  *
   21926                 :             :  * This is separate so that it can be run independently when the second
   21927                 :             :  * transaction of the concurrent algorithm fails (crash or abort).
   21928                 :             :  */
   21929                 :             : static void
   21930                 :         741 : DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
   21931                 :             :                         Oid defaultPartOid)
   21932                 :             : {
   21933                 :             :     Relation    classRel;
   21934                 :             :     List       *fks;
   21935                 :             :     ListCell   *cell;
   21936                 :             :     List       *indexes;
   21937                 :             :     Datum       new_val[Natts_pg_class];
   21938                 :             :     bool        new_null[Natts_pg_class],
   21939                 :             :                 new_repl[Natts_pg_class];
   21940                 :             :     HeapTuple   tuple,
   21941                 :             :                 newtuple;
   21942                 :         741 :     Relation    trigrel = NULL;
   21943                 :         741 :     List       *fkoids = NIL;
   21944                 :             : 
   21945         [ +  + ]:         741 :     if (concurrent)
   21946                 :             :     {
   21947                 :             :         /*
   21948                 :             :          * We can remove the pg_inherits row now. (In the non-concurrent case,
   21949                 :             :          * this was already done).
   21950                 :             :          */
   21951                 :          54 :         RemoveInheritance(partRel, rel, true);
   21952                 :             :     }
   21953                 :             : 
   21954                 :             :     /* Drop any triggers that were cloned on creation/attach. */
   21955                 :         741 :     DropClonedTriggersFromPartition(RelationGetRelid(partRel));
   21956                 :             : 
   21957                 :             :     /*
   21958                 :             :      * Detach any foreign keys that are inherited.  This includes creating
   21959                 :             :      * additional action triggers.
   21960                 :             :      */
   21961                 :         741 :     fks = copyObject(RelationGetFKeyList(partRel));
   21962         [ +  + ]:         741 :     if (fks != NIL)
   21963                 :          60 :         trigrel = table_open(TriggerRelationId, RowExclusiveLock);
   21964                 :             : 
   21965                 :             :     /*
   21966                 :             :      * It's possible that the partition being detached has a foreign key that
   21967                 :             :      * references a partitioned table.  When that happens, there are multiple
   21968                 :             :      * pg_constraint rows for the partition: one points to the partitioned
   21969                 :             :      * table itself, while the others point to each of its partitions.  Only
   21970                 :             :      * the topmost one is to be considered here; the child constraints must be
   21971                 :             :      * left alone, because conceptually those aren't coming from our parent
   21972                 :             :      * partitioned table, but from this partition itself.
   21973                 :             :      *
   21974                 :             :      * We implement this by collecting all the constraint OIDs in a first scan
   21975                 :             :      * of the FK array, and skipping in the loop below those constraints whose
   21976                 :             :      * parents are listed here.
   21977                 :             :      */
   21978   [ +  +  +  +  :        1598 :     foreach_node(ForeignKeyCacheInfo, fk, fks)
                   +  + ]
   21979                 :         116 :         fkoids = lappend_oid(fkoids, fk->conoid);
   21980                 :             : 
   21981   [ +  +  +  +  :         857 :     foreach(cell, fks)
                   +  + ]
   21982                 :             :     {
   21983                 :         116 :         ForeignKeyCacheInfo *fk = lfirst(cell);
   21984                 :             :         HeapTuple   contup;
   21985                 :             :         Form_pg_constraint conform;
   21986                 :             : 
   21987                 :         116 :         contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
   21988         [ -  + ]:         116 :         if (!HeapTupleIsValid(contup))
   21989         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
   21990                 :         116 :         conform = (Form_pg_constraint) GETSTRUCT(contup);
   21991                 :             : 
   21992                 :             :         /*
   21993                 :             :          * Consider only inherited foreign keys, and only if their parents
   21994                 :             :          * aren't in the list.
   21995                 :             :          */
   21996         [ +  - ]:         116 :         if (conform->contype != CONSTRAINT_FOREIGN ||
   21997   [ +  +  +  + ]:         216 :             !OidIsValid(conform->conparentid) ||
   21998                 :         100 :             list_member_oid(fkoids, conform->conparentid))
   21999                 :             :         {
   22000                 :          44 :             ReleaseSysCache(contup);
   22001                 :          44 :             continue;
   22002                 :             :         }
   22003                 :             : 
   22004                 :             :         /*
   22005                 :             :          * The constraint on this table must be marked no longer a child of
   22006                 :             :          * the parent's constraint, as do its check triggers.
   22007                 :             :          */
   22008                 :          72 :         ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
   22009                 :             : 
   22010                 :             :         /*
   22011                 :             :          * Also, look up the partition's "check" triggers corresponding to the
   22012                 :             :          * ENFORCED constraint being detached and detach them from the parent
   22013                 :             :          * triggers. NOT ENFORCED constraints do not have these triggers;
   22014                 :             :          * therefore, this step is not needed.
   22015                 :             :          */
   22016         [ +  - ]:          72 :         if (fk->conenforced)
   22017                 :             :         {
   22018                 :             :             Oid         insertTriggerOid,
   22019                 :             :                         updateTriggerOid;
   22020                 :             : 
   22021                 :          72 :             GetForeignKeyCheckTriggers(trigrel,
   22022                 :             :                                        fk->conoid, fk->confrelid, fk->conrelid,
   22023                 :             :                                        &insertTriggerOid, &updateTriggerOid);
   22024                 :             :             Assert(OidIsValid(insertTriggerOid));
   22025                 :          72 :             TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
   22026                 :             :                                     RelationGetRelid(partRel));
   22027                 :             :             Assert(OidIsValid(updateTriggerOid));
   22028                 :          72 :             TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
   22029                 :             :                                     RelationGetRelid(partRel));
   22030                 :             :         }
   22031                 :             : 
   22032                 :             :         /*
   22033                 :             :          * Lastly, create the action triggers on the referenced table, using
   22034                 :             :          * addFkRecurseReferenced, which requires some elaborate setup (so put
   22035                 :             :          * it in a separate block).  While at it, if the table is partitioned,
   22036                 :             :          * that function will recurse to create the pg_constraint rows and
   22037                 :             :          * action triggers for each partition.
   22038                 :             :          *
   22039                 :             :          * Note there's no need to do addFkConstraint() here, because the
   22040                 :             :          * pg_constraint row already exists.
   22041                 :             :          */
   22042                 :             :         {
   22043                 :             :             Constraint *fkconstraint;
   22044                 :             :             int         numfks;
   22045                 :             :             AttrNumber  conkey[INDEX_MAX_KEYS];
   22046                 :             :             AttrNumber  confkey[INDEX_MAX_KEYS];
   22047                 :             :             Oid         conpfeqop[INDEX_MAX_KEYS];
   22048                 :             :             Oid         conppeqop[INDEX_MAX_KEYS];
   22049                 :             :             Oid         conffeqop[INDEX_MAX_KEYS];
   22050                 :             :             int         numfkdelsetcols;
   22051                 :             :             AttrNumber  confdelsetcols[INDEX_MAX_KEYS];
   22052                 :             :             Relation    refdRel;
   22053                 :             : 
   22054                 :          72 :             DeconstructFkConstraintRow(contup,
   22055                 :             :                                        &numfks,
   22056                 :             :                                        conkey,
   22057                 :             :                                        confkey,
   22058                 :             :                                        conpfeqop,
   22059                 :             :                                        conppeqop,
   22060                 :             :                                        conffeqop,
   22061                 :             :                                        &numfkdelsetcols,
   22062                 :             :                                        confdelsetcols);
   22063                 :             : 
   22064                 :             :             /* Create a synthetic node we'll use throughout */
   22065                 :          72 :             fkconstraint = makeNode(Constraint);
   22066                 :          72 :             fkconstraint->contype = CONSTRAINT_FOREIGN;
   22067                 :          72 :             fkconstraint->conname = pstrdup(NameStr(conform->conname));
   22068                 :          72 :             fkconstraint->deferrable = conform->condeferrable;
   22069                 :          72 :             fkconstraint->initdeferred = conform->condeferred;
   22070                 :          72 :             fkconstraint->is_enforced = conform->conenforced;
   22071                 :          72 :             fkconstraint->skip_validation = true;
   22072                 :          72 :             fkconstraint->initially_valid = conform->convalidated;
   22073                 :             :             /* a few irrelevant fields omitted here */
   22074                 :          72 :             fkconstraint->pktable = NULL;
   22075                 :          72 :             fkconstraint->fk_attrs = NIL;
   22076                 :          72 :             fkconstraint->pk_attrs = NIL;
   22077                 :          72 :             fkconstraint->fk_matchtype = conform->confmatchtype;
   22078                 :          72 :             fkconstraint->fk_upd_action = conform->confupdtype;
   22079                 :          72 :             fkconstraint->fk_del_action = conform->confdeltype;
   22080                 :          72 :             fkconstraint->fk_del_set_cols = NIL;
   22081                 :          72 :             fkconstraint->old_conpfeqop = NIL;
   22082                 :          72 :             fkconstraint->old_pktable_oid = InvalidOid;
   22083                 :          72 :             fkconstraint->location = -1;
   22084                 :             : 
   22085                 :             :             /* set up colnames, used to generate the constraint name */
   22086         [ +  + ]:         176 :             for (int i = 0; i < numfks; i++)
   22087                 :             :             {
   22088                 :             :                 Form_pg_attribute att;
   22089                 :             : 
   22090                 :         104 :                 att = TupleDescAttr(RelationGetDescr(partRel),
   22091                 :         104 :                                     conkey[i] - 1);
   22092                 :             : 
   22093                 :         104 :                 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
   22094                 :         104 :                                                  makeString(NameStr(att->attname)));
   22095                 :             :             }
   22096                 :             : 
   22097                 :          72 :             refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
   22098                 :             : 
   22099                 :          72 :             addFkRecurseReferenced(fkconstraint, partRel,
   22100                 :             :                                    refdRel,
   22101                 :             :                                    conform->conindid,
   22102                 :             :                                    fk->conoid,
   22103                 :             :                                    numfks,
   22104                 :             :                                    confkey,
   22105                 :             :                                    conkey,
   22106                 :             :                                    conpfeqop,
   22107                 :             :                                    conppeqop,
   22108                 :             :                                    conffeqop,
   22109                 :             :                                    numfkdelsetcols,
   22110                 :             :                                    confdelsetcols,
   22111                 :             :                                    true,
   22112                 :             :                                    InvalidOid, InvalidOid,
   22113                 :          72 :                                    conform->conperiod);
   22114                 :          72 :             table_close(refdRel, NoLock);   /* keep lock till end of xact */
   22115                 :             :         }
   22116                 :             : 
   22117                 :          72 :         ReleaseSysCache(contup);
   22118                 :             :     }
   22119                 :         741 :     list_free_deep(fks);
   22120         [ +  + ]:         741 :     if (trigrel)
   22121                 :          60 :         table_close(trigrel, RowExclusiveLock);
   22122                 :             : 
   22123                 :             :     /*
   22124                 :             :      * Any sub-constraints that are in the referenced-side of a larger
   22125                 :             :      * constraint have to be removed.  This partition is no longer part of the
   22126                 :             :      * key space of the constraint.
   22127                 :             :      */
   22128   [ +  +  +  +  :         796 :     foreach(cell, GetParentedForeignKeyRefs(partRel))
                   +  + ]
   22129                 :             :     {
   22130                 :          56 :         Oid         constrOid = lfirst_oid(cell);
   22131                 :             :         ObjectAddress constraint;
   22132                 :             : 
   22133                 :          56 :         ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   22134                 :          56 :         deleteDependencyRecordsForClass(ConstraintRelationId,
   22135                 :             :                                         constrOid,
   22136                 :             :                                         ConstraintRelationId,
   22137                 :             :                                         DEPENDENCY_INTERNAL);
   22138                 :          56 :         CommandCounterIncrement();
   22139                 :             : 
   22140                 :          56 :         ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
   22141                 :          56 :         performDeletion(&constraint, DROP_RESTRICT, 0);
   22142                 :             :     }
   22143                 :             : 
   22144                 :             :     /* Now we can detach indexes */
   22145                 :         740 :     indexes = RelationGetIndexList(partRel);
   22146   [ +  +  +  +  :        1047 :     foreach(cell, indexes)
                   +  + ]
   22147                 :             :     {
   22148                 :         307 :         Oid         idxid = lfirst_oid(cell);
   22149                 :             :         Oid         parentidx;
   22150                 :             :         Relation    idx;
   22151                 :             :         Oid         constrOid;
   22152                 :             :         Oid         parentConstrOid;
   22153                 :             : 
   22154         [ +  + ]:         307 :         if (!has_superclass(idxid))
   22155                 :           9 :             continue;
   22156                 :             : 
   22157                 :         298 :         parentidx = get_partition_parent(idxid, false);
   22158                 :             :         Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
   22159                 :             : 
   22160                 :         298 :         idx = index_open(idxid, AccessExclusiveLock);
   22161                 :         298 :         IndexSetParentIndex(idx, InvalidOid);
   22162                 :             : 
   22163                 :             :         /*
   22164                 :             :          * If there's a constraint associated with the index, detach it too.
   22165                 :             :          * Careful: it is possible for a constraint index in a partition to be
   22166                 :             :          * the child of a non-constraint index, so verify whether the parent
   22167                 :             :          * index does actually have a constraint.
   22168                 :             :          */
   22169                 :         298 :         constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
   22170                 :             :                                                     idxid);
   22171                 :         298 :         parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
   22172                 :             :                                                           parentidx);
   22173   [ +  +  +  - ]:         298 :         if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
   22174                 :         135 :             ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
   22175                 :             : 
   22176                 :         298 :         index_close(idx, NoLock);
   22177                 :             :     }
   22178                 :             : 
   22179                 :             :     /* Update pg_class tuple */
   22180                 :         740 :     classRel = table_open(RelationRelationId, RowExclusiveLock);
   22181                 :         740 :     tuple = SearchSysCacheCopy1(RELOID,
   22182                 :             :                                 ObjectIdGetDatum(RelationGetRelid(partRel)));
   22183         [ -  + ]:         740 :     if (!HeapTupleIsValid(tuple))
   22184         [ #  # ]:           0 :         elog(ERROR, "cache lookup failed for relation %u",
   22185                 :             :              RelationGetRelid(partRel));
   22186                 :             :     Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
   22187                 :             : 
   22188                 :             :     /* Clear relpartbound and reset relispartition */
   22189                 :         740 :     memset(new_val, 0, sizeof(new_val));
   22190                 :         740 :     memset(new_null, false, sizeof(new_null));
   22191                 :         740 :     memset(new_repl, false, sizeof(new_repl));
   22192                 :         740 :     new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
   22193                 :         740 :     new_null[Anum_pg_class_relpartbound - 1] = true;
   22194                 :         740 :     new_repl[Anum_pg_class_relpartbound - 1] = true;
   22195                 :         740 :     newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
   22196                 :             :                                  new_val, new_null, new_repl);
   22197                 :             : 
   22198                 :         740 :     ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
   22199                 :         740 :     CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
   22200                 :         740 :     heap_freetuple(newtuple);
   22201                 :         740 :     table_close(classRel, RowExclusiveLock);
   22202                 :             : 
   22203                 :             :     /*
   22204                 :             :      * Drop identity property from all identity columns of partition.
   22205                 :             :      */
   22206         [ +  + ]:        2328 :     for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
   22207                 :             :     {
   22208                 :        1588 :         Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
   22209                 :             : 
   22210   [ +  +  +  + ]:        1588 :         if (!attr->attisdropped && attr->attidentity)
   22211                 :          20 :             ATExecDropIdentity(partRel, NameStr(attr->attname), false,
   22212                 :             :                                AccessExclusiveLock, true, true);
   22213                 :             :     }
   22214                 :             : 
   22215         [ +  + ]:         740 :     if (OidIsValid(defaultPartOid))
   22216                 :             :     {
   22217                 :             :         /*
   22218                 :             :          * If the relation being detached is the default partition itself,
   22219                 :             :          * remove it from the parent's pg_partitioned_table entry.
   22220                 :             :          *
   22221                 :             :          * If not, we must invalidate default partition's relcache entry, as
   22222                 :             :          * in StorePartitionBound: its partition constraint depends on every
   22223                 :             :          * other partition's partition constraint.
   22224                 :             :          */
   22225         [ +  + ]:         167 :         if (RelationGetRelid(partRel) == defaultPartOid)
   22226                 :          33 :             update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
   22227                 :             :         else
   22228                 :         134 :             CacheInvalidateRelcacheByRelid(defaultPartOid);
   22229                 :             :     }
   22230                 :             : 
   22231                 :             :     /*
   22232                 :             :      * Invalidate the parent's relcache so that the partition is no longer
   22233                 :             :      * included in its partition descriptor.
   22234                 :             :      */
   22235                 :         740 :     CacheInvalidateRelcache(rel);
   22236                 :             : 
   22237                 :             :     /*
   22238                 :             :      * If the partition we just detached is partitioned itself, invalidate
   22239                 :             :      * relcache for all descendent partitions too to ensure that their
   22240                 :             :      * rd_partcheck expression trees are rebuilt; must lock partitions before
   22241                 :             :      * doing so, using the same lockmode as what partRel has been locked with
   22242                 :             :      * by the caller.
   22243                 :             :      */
   22244         [ +  + ]:         740 :     if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
   22245                 :             :     {
   22246                 :             :         List       *children;
   22247                 :             : 
   22248                 :          41 :         children = find_all_inheritors(RelationGetRelid(partRel),
   22249                 :             :                                        AccessExclusiveLock, NULL);
   22250   [ +  -  +  +  :         135 :         foreach(cell, children)
                   +  + ]
   22251                 :             :         {
   22252                 :          94 :             CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
   22253                 :             :         }
   22254                 :             :     }
   22255                 :         740 : }
   22256                 :             : 
   22257                 :             : /*
   22258                 :             :  * ALTER TABLE ... DETACH PARTITION ... FINALIZE
   22259                 :             :  *
   22260                 :             :  * To use when a DETACH PARTITION command previously did not run to
   22261                 :             :  * completion; this completes the detaching process.
   22262                 :             :  */
   22263                 :             : static ObjectAddress
   22264                 :           7 : ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
   22265                 :             : {
   22266                 :             :     Relation    partRel;
   22267                 :             :     ObjectAddress address;
   22268                 :           7 :     Snapshot    snap = GetActiveSnapshot();
   22269                 :             : 
   22270                 :           7 :     partRel = table_openrv(name, AccessExclusiveLock);
   22271                 :             : 
   22272                 :             :     /*
   22273                 :             :      * Wait until existing snapshots are gone.  This is important if the
   22274                 :             :      * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
   22275                 :             :      * user could immediately run DETACH FINALIZE without actually waiting for
   22276                 :             :      * existing transactions.  We must not complete the detach action until
   22277                 :             :      * all such queries are complete (otherwise we would present them with an
   22278                 :             :      * inconsistent view of catalogs).
   22279                 :             :      */
   22280                 :           7 :     WaitForOlderSnapshots(snap->xmin, false);
   22281                 :             : 
   22282                 :           7 :     DetachPartitionFinalize(rel, partRel, true, InvalidOid);
   22283                 :             : 
   22284                 :           7 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
   22285                 :             : 
   22286                 :           7 :     table_close(partRel, NoLock);
   22287                 :             : 
   22288                 :           7 :     return address;
   22289                 :             : }
   22290                 :             : 
   22291                 :             : /*
   22292                 :             :  * DropClonedTriggersFromPartition
   22293                 :             :  *      subroutine for ATExecDetachPartition to remove any triggers that were
   22294                 :             :  *      cloned to the partition when it was created-as-partition or attached.
   22295                 :             :  *      This undoes what CloneRowTriggersToPartition did.
   22296                 :             :  */
   22297                 :             : static void
   22298                 :         741 : DropClonedTriggersFromPartition(Oid partitionId)
   22299                 :             : {
   22300                 :             :     ScanKeyData skey;
   22301                 :             :     SysScanDesc scan;
   22302                 :             :     HeapTuple   trigtup;
   22303                 :             :     Relation    tgrel;
   22304                 :             :     ObjectAddresses *objects;
   22305                 :             : 
   22306                 :         741 :     objects = new_object_addresses();
   22307                 :             : 
   22308                 :             :     /*
   22309                 :             :      * Scan pg_trigger to search for all triggers on this rel.
   22310                 :             :      */
   22311                 :         741 :     ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
   22312                 :             :                 F_OIDEQ, ObjectIdGetDatum(partitionId));
   22313                 :         741 :     tgrel = table_open(TriggerRelationId, RowExclusiveLock);
   22314                 :         741 :     scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
   22315                 :             :                               true, NULL, 1, &skey);
   22316         [ +  + ]:        1077 :     while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
   22317                 :             :     {
   22318                 :         336 :         Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
   22319                 :             :         ObjectAddress trig;
   22320                 :             : 
   22321                 :             :         /* Ignore triggers that weren't cloned */
   22322         [ +  + ]:         336 :         if (!OidIsValid(pg_trigger->tgparentid))
   22323                 :         296 :             continue;
   22324                 :             : 
   22325                 :             :         /*
   22326                 :             :          * Ignore internal triggers that are implementation objects of foreign
   22327                 :             :          * keys, because these will be detached when the foreign keys
   22328                 :             :          * themselves are.
   22329                 :             :          */
   22330         [ +  + ]:         280 :         if (OidIsValid(pg_trigger->tgconstrrelid))
   22331                 :         240 :             continue;
   22332                 :             : 
   22333                 :             :         /*
   22334                 :             :          * This is ugly, but necessary: remove the dependency markings on the
   22335                 :             :          * trigger so that it can be removed.
   22336                 :             :          */
   22337                 :          40 :         deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   22338                 :             :                                         TriggerRelationId,
   22339                 :             :                                         DEPENDENCY_PARTITION_PRI);
   22340                 :          40 :         deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
   22341                 :             :                                         RelationRelationId,
   22342                 :             :                                         DEPENDENCY_PARTITION_SEC);
   22343                 :             : 
   22344                 :             :         /* remember this trigger to remove it below */
   22345                 :          40 :         ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
   22346                 :          40 :         add_exact_object_address(&trig, objects);
   22347                 :             :     }
   22348                 :             : 
   22349                 :             :     /* make the dependency removal visible to the deletion below */
   22350                 :         741 :     CommandCounterIncrement();
   22351                 :         741 :     performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
   22352                 :             : 
   22353                 :             :     /* done */
   22354                 :         741 :     free_object_addresses(objects);
   22355                 :         741 :     systable_endscan(scan);
   22356                 :         741 :     table_close(tgrel, RowExclusiveLock);
   22357                 :         741 : }
   22358                 :             : 
   22359                 :             : /*
   22360                 :             :  * Before acquiring lock on an index, acquire the same lock on the owning
   22361                 :             :  * table.
   22362                 :             :  */
   22363                 :             : struct AttachIndexCallbackState
   22364                 :             : {
   22365                 :             :     Oid         partitionOid;
   22366                 :             :     Oid         parentTblOid;
   22367                 :             :     bool        lockedParentTbl;
   22368                 :             : };
   22369                 :             : 
   22370                 :             : static void
   22371                 :         293 : RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
   22372                 :             :                                void *arg)
   22373                 :             : {
   22374                 :             :     struct AttachIndexCallbackState *state;
   22375                 :             :     Form_pg_class classform;
   22376                 :             :     HeapTuple   tuple;
   22377                 :             : 
   22378                 :         293 :     state = (struct AttachIndexCallbackState *) arg;
   22379                 :             : 
   22380         [ +  + ]:         293 :     if (!state->lockedParentTbl)
   22381                 :             :     {
   22382                 :         284 :         LockRelationOid(state->parentTblOid, AccessShareLock);
   22383                 :         284 :         state->lockedParentTbl = true;
   22384                 :             :     }
   22385                 :             : 
   22386                 :             :     /*
   22387                 :             :      * If we previously locked some other heap, and the name we're looking up
   22388                 :             :      * no longer refers to an index on that relation, release the now-useless
   22389                 :             :      * lock.  XXX maybe we should do *after* we verify whether the index does
   22390                 :             :      * not actually belong to the same relation ...
   22391                 :             :      */
   22392   [ +  +  -  + ]:         293 :     if (relOid != oldRelOid && OidIsValid(state->partitionOid))
   22393                 :             :     {
   22394                 :           0 :         UnlockRelationOid(state->partitionOid, AccessShareLock);
   22395                 :           0 :         state->partitionOid = InvalidOid;
   22396                 :             :     }
   22397                 :             : 
   22398                 :             :     /* Didn't find a relation, so no need for locking or permission checks. */
   22399         [ +  + ]:         293 :     if (!OidIsValid(relOid))
   22400                 :           4 :         return;
   22401                 :             : 
   22402                 :         289 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
   22403         [ -  + ]:         289 :     if (!HeapTupleIsValid(tuple))
   22404                 :           0 :         return;                 /* concurrently dropped, so nothing to do */
   22405                 :         289 :     classform = (Form_pg_class) GETSTRUCT(tuple);
   22406         [ +  + ]:         289 :     if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
   22407         [ +  + ]:         223 :         classform->relkind != RELKIND_INDEX)
   22408         [ +  - ]:           4 :         ereport(ERROR,
   22409                 :             :                 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   22410                 :             :                  errmsg("\"%s\" is not an index", rv->relname)));
   22411                 :         285 :     ReleaseSysCache(tuple);
   22412                 :             : 
   22413                 :             :     /*
   22414                 :             :      * Since we need only examine the heap's tupledesc, an access share lock
   22415                 :             :      * on it (preventing any DDL) is sufficient.
   22416                 :             :      */
   22417                 :         285 :     state->partitionOid = IndexGetRelation(relOid, false);
   22418                 :         285 :     LockRelationOid(state->partitionOid, AccessShareLock);
   22419                 :             : }
   22420                 :             : 
   22421                 :             : /*
   22422                 :             :  * ALTER INDEX i1 ATTACH PARTITION i2
   22423                 :             :  */
   22424                 :             : static ObjectAddress
   22425                 :         284 : ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
   22426                 :             : {
   22427                 :             :     Relation    partIdx;
   22428                 :             :     Relation    partTbl;
   22429                 :             :     Relation    parentTbl;
   22430                 :             :     ObjectAddress address;
   22431                 :             :     Oid         partIdxId;
   22432                 :             :     Oid         currParent;
   22433                 :             :     struct AttachIndexCallbackState state;
   22434                 :             : 
   22435                 :             :     /*
   22436                 :             :      * We need to obtain lock on the index 'name' to modify it, but we also
   22437                 :             :      * need to read its owning table's tuple descriptor -- so we need to lock
   22438                 :             :      * both.  To avoid deadlocks, obtain lock on the table before doing so on
   22439                 :             :      * the index.  Furthermore, we need to examine the parent table of the
   22440                 :             :      * partition, so lock that one too.
   22441                 :             :      */
   22442                 :         284 :     state.partitionOid = InvalidOid;
   22443                 :         284 :     state.parentTblOid = parentIdx->rd_index->indrelid;
   22444                 :         284 :     state.lockedParentTbl = false;
   22445                 :             :     partIdxId =
   22446                 :         284 :         RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
   22447                 :             :                                  RangeVarCallbackForAttachIndex,
   22448                 :             :                                  &state);
   22449                 :             :     /* Not there? */
   22450         [ -  + ]:         276 :     if (!OidIsValid(partIdxId))
   22451         [ #  # ]:           0 :         ereport(ERROR,
   22452                 :             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
   22453                 :             :                  errmsg("index \"%s\" does not exist", name->relname)));
   22454                 :             : 
   22455                 :             :     /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
   22456                 :         276 :     partIdx = relation_open(partIdxId, AccessExclusiveLock);
   22457                 :             : 
   22458                 :             :     /* we already hold locks on both tables, so this is safe: */
   22459                 :         276 :     parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
   22460                 :         276 :     partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
   22461                 :             : 
   22462                 :         276 :     ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
   22463                 :             : 
   22464                 :             :     /*
   22465                 :             :      * Check if the index is already attached to the correct parent,
   22466                 :             :      * ultimately attempting one round of validation if already the case.
   22467                 :             :      */
   22468                 :         552 :     currParent = partIdx->rd_rel->relispartition ?
   22469         [ +  + ]:         276 :         get_partition_parent(partIdxId, false) : InvalidOid;
   22470         [ +  + ]:         276 :     if (currParent != RelationGetRelid(parentIdx))
   22471                 :             :     {
   22472                 :             :         IndexInfo  *childInfo;
   22473                 :             :         IndexInfo  *parentInfo;
   22474                 :             :         AttrMap    *attmap;
   22475                 :             :         bool        found;
   22476                 :             :         int         i;
   22477                 :             :         PartitionDesc partDesc;
   22478                 :             :         Oid         constraintOid,
   22479                 :         252 :                     cldConstrId = InvalidOid;
   22480                 :             : 
   22481                 :             :         /*
   22482                 :             :          * If this partition already has an index attached, refuse the
   22483                 :             :          * operation.
   22484                 :             :          */
   22485                 :         252 :         refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
   22486                 :             : 
   22487         [ -  + ]:         248 :         if (OidIsValid(currParent))
   22488         [ #  # ]:           0 :             ereport(ERROR,
   22489                 :             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   22490                 :             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   22491                 :             :                             RelationGetRelationName(partIdx),
   22492                 :             :                             RelationGetRelationName(parentIdx)),
   22493                 :             :                      errdetail("Index \"%s\" is already attached to another index.",
   22494                 :             :                                RelationGetRelationName(partIdx))));
   22495                 :             : 
   22496                 :             :         /* Make sure it indexes a partition of the other index's table */
   22497                 :         248 :         partDesc = RelationGetPartitionDesc(parentTbl, true);
   22498                 :         248 :         found = false;
   22499         [ +  + ]:         373 :         for (i = 0; i < partDesc->nparts; i++)
   22500                 :             :         {
   22501         [ +  + ]:         369 :             if (partDesc->oids[i] == state.partitionOid)
   22502                 :             :             {
   22503                 :         244 :                 found = true;
   22504                 :         244 :                 break;
   22505                 :             :             }
   22506                 :             :         }
   22507         [ +  + ]:         248 :         if (!found)
   22508         [ +  - ]:           4 :             ereport(ERROR,
   22509                 :             :                     (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   22510                 :             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   22511                 :             :                             RelationGetRelationName(partIdx),
   22512                 :             :                             RelationGetRelationName(parentIdx)),
   22513                 :             :                      errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
   22514                 :             :                                RelationGetRelationName(partIdx),
   22515                 :             :                                RelationGetRelationName(parentTbl))));
   22516                 :             : 
   22517                 :             :         /* Ensure the indexes are compatible */
   22518                 :         244 :         childInfo = BuildIndexInfo(partIdx);
   22519                 :         244 :         parentInfo = BuildIndexInfo(parentIdx);
   22520                 :         244 :         attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
   22521                 :             :                                        RelationGetDescr(parentTbl),
   22522                 :             :                                        false);
   22523         [ +  + ]:         244 :         if (!CompareIndexInfo(childInfo, parentInfo,
   22524                 :         244 :                               partIdx->rd_indcollation,
   22525                 :         244 :                               parentIdx->rd_indcollation,
   22526                 :         244 :                               partIdx->rd_opfamily,
   22527                 :         244 :                               parentIdx->rd_opfamily,
   22528                 :             :                               attmap))
   22529         [ +  - ]:          32 :             ereport(ERROR,
   22530                 :             :                     (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   22531                 :             :                      errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   22532                 :             :                             RelationGetRelationName(partIdx),
   22533                 :             :                             RelationGetRelationName(parentIdx)),
   22534                 :             :                      errdetail("The index definitions do not match.")));
   22535                 :             : 
   22536                 :             :         /*
   22537                 :             :          * If there is a constraint in the parent, make sure there is one in
   22538                 :             :          * the child too.
   22539                 :             :          */
   22540                 :         212 :         constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
   22541                 :             :                                                         RelationGetRelid(parentIdx));
   22542                 :             : 
   22543         [ +  + ]:         212 :         if (OidIsValid(constraintOid))
   22544                 :             :         {
   22545                 :          76 :             cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
   22546                 :             :                                                           partIdxId);
   22547         [ +  + ]:          76 :             if (!OidIsValid(cldConstrId))
   22548         [ +  - ]:           4 :                 ereport(ERROR,
   22549                 :             :                         (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
   22550                 :             :                          errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   22551                 :             :                                 RelationGetRelationName(partIdx),
   22552                 :             :                                 RelationGetRelationName(parentIdx)),
   22553                 :             :                          errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
   22554                 :             :                                    RelationGetRelationName(parentIdx),
   22555                 :             :                                    RelationGetRelationName(parentTbl),
   22556                 :             :                                    RelationGetRelationName(partIdx))));
   22557                 :             :         }
   22558                 :             : 
   22559                 :             :         /*
   22560                 :             :          * If it's a primary key, make sure the columns in the partition are
   22561                 :             :          * NOT NULL.
   22562                 :             :          */
   22563         [ +  + ]:         208 :         if (parentIdx->rd_index->indisprimary)
   22564                 :          55 :             verifyPartitionIndexNotNull(childInfo, partTbl);
   22565                 :             : 
   22566                 :             :         /* All good -- do it */
   22567                 :         208 :         IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
   22568         [ +  + ]:         208 :         if (OidIsValid(constraintOid))
   22569                 :          72 :             ConstraintSetParentConstraint(cldConstrId, constraintOid,
   22570                 :             :                                           RelationGetRelid(partTbl));
   22571                 :             : 
   22572                 :         208 :         free_attrmap(attmap);
   22573                 :             : 
   22574                 :         208 :         validatePartitionedIndex(parentIdx, parentTbl);
   22575                 :             :     }
   22576         [ +  + ]:          24 :     else if (!parentIdx->rd_index->indisvalid)
   22577                 :             :     {
   22578                 :             :         /*
   22579                 :             :          * The index is attached, but the parent is still invalid; see if it
   22580                 :             :          * can be validated now.
   22581                 :             :          */
   22582                 :          12 :         validatePartitionedIndex(parentIdx, parentTbl);
   22583                 :             :     }
   22584                 :             : 
   22585                 :         232 :     relation_close(parentTbl, AccessShareLock);
   22586                 :             :     /* keep these locks till commit */
   22587                 :         232 :     relation_close(partTbl, NoLock);
   22588                 :         232 :     relation_close(partIdx, NoLock);
   22589                 :             : 
   22590                 :         232 :     return address;
   22591                 :             : }
   22592                 :             : 
   22593                 :             : /*
   22594                 :             :  * Verify whether the given partition already contains an index attached
   22595                 :             :  * to the given partitioned index.  If so, raise an error.
   22596                 :             :  */
   22597                 :             : static void
   22598                 :         252 : refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
   22599                 :             : {
   22600                 :             :     Oid         existingIdx;
   22601                 :             : 
   22602                 :         252 :     existingIdx = index_get_partition(partitionTbl,
   22603                 :             :                                       RelationGetRelid(parentIdx));
   22604         [ +  + ]:         252 :     if (OidIsValid(existingIdx))
   22605         [ +  - ]:           4 :         ereport(ERROR,
   22606                 :             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
   22607                 :             :                  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
   22608                 :             :                         RelationGetRelationName(partIdx),
   22609                 :             :                         RelationGetRelationName(parentIdx)),
   22610                 :             :                  errdetail("Another index \"%s\" is already attached for partition \"%s\".",
   22611                 :             :                            get_rel_name(existingIdx),
   22612                 :             :                            RelationGetRelationName(partitionTbl))));
   22613                 :         248 : }
   22614                 :             : 
   22615                 :             : /*
   22616                 :             :  * Verify whether the set of attached partition indexes to a parent index on
   22617                 :             :  * a partitioned table is complete.  If it is, mark the parent index valid.
   22618                 :             :  *
   22619                 :             :  * This should be called each time a partition index is attached.
   22620                 :             :  */
   22621                 :             : static void
   22622                 :         252 : validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
   22623                 :             : {
   22624                 :             :     Relation    inheritsRel;
   22625                 :             :     SysScanDesc scan;
   22626                 :             :     ScanKeyData key;
   22627                 :         252 :     int         tuples = 0;
   22628                 :             :     HeapTuple   inhTup;
   22629                 :         252 :     bool        updated = false;
   22630                 :             : 
   22631                 :             :     Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
   22632                 :             : 
   22633                 :             :     /*
   22634                 :             :      * Scan pg_inherits for this parent index.  Count each valid index we find
   22635                 :             :      * (verifying the pg_index entry for each), and if we reach the total
   22636                 :             :      * amount we expect, we can mark this parent index as valid.
   22637                 :             :      */
   22638                 :         252 :     inheritsRel = table_open(InheritsRelationId, AccessShareLock);
   22639                 :         252 :     ScanKeyInit(&key, Anum_pg_inherits_inhparent,
   22640                 :             :                 BTEqualStrategyNumber, F_OIDEQ,
   22641                 :             :                 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   22642                 :         252 :     scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
   22643                 :             :                               NULL, 1, &key);
   22644         [ +  + ]:         639 :     while ((inhTup = systable_getnext(scan)) != NULL)
   22645                 :             :     {
   22646                 :         387 :         Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
   22647                 :             :         HeapTuple   indTup;
   22648                 :             :         Form_pg_index indexForm;
   22649                 :             : 
   22650                 :         387 :         indTup = SearchSysCache1(INDEXRELID,
   22651                 :             :                                  ObjectIdGetDatum(inhForm->inhrelid));
   22652         [ -  + ]:         387 :         if (!HeapTupleIsValid(indTup))
   22653         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
   22654                 :         387 :         indexForm = (Form_pg_index) GETSTRUCT(indTup);
   22655         [ +  + ]:         387 :         if (indexForm->indisvalid)
   22656                 :         321 :             tuples += 1;
   22657                 :         387 :         ReleaseSysCache(indTup);
   22658                 :             :     }
   22659                 :             : 
   22660                 :             :     /* Done with pg_inherits */
   22661                 :         252 :     systable_endscan(scan);
   22662                 :         252 :     table_close(inheritsRel, AccessShareLock);
   22663                 :             : 
   22664                 :             :     /*
   22665                 :             :      * If we found as many inherited indexes as the partitioned table has
   22666                 :             :      * partitions, we're good; update pg_index to set indisvalid.
   22667                 :             :      */
   22668         [ +  + ]:         252 :     if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
   22669                 :             :     {
   22670                 :             :         Relation    idxRel;
   22671                 :             :         HeapTuple   indTup;
   22672                 :             :         Form_pg_index indexForm;
   22673                 :             : 
   22674                 :         121 :         idxRel = table_open(IndexRelationId, RowExclusiveLock);
   22675                 :         121 :         indTup = SearchSysCacheCopy1(INDEXRELID,
   22676                 :             :                                      ObjectIdGetDatum(RelationGetRelid(partedIdx)));
   22677         [ -  + ]:         121 :         if (!HeapTupleIsValid(indTup))
   22678         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for index %u",
   22679                 :             :                  RelationGetRelid(partedIdx));
   22680                 :         121 :         indexForm = (Form_pg_index) GETSTRUCT(indTup);
   22681                 :             : 
   22682                 :         121 :         indexForm->indisvalid = true;
   22683                 :         121 :         updated = true;
   22684                 :             : 
   22685                 :         121 :         CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
   22686                 :             : 
   22687                 :         121 :         table_close(idxRel, RowExclusiveLock);
   22688                 :         121 :         heap_freetuple(indTup);
   22689                 :             :     }
   22690                 :             : 
   22691                 :             :     /*
   22692                 :             :      * If this index is in turn a partition of a larger index, validating it
   22693                 :             :      * might cause the parent to become valid also.  Try that.
   22694                 :             :      */
   22695   [ +  +  +  + ]:         252 :     if (updated && partedIdx->rd_rel->relispartition)
   22696                 :             :     {
   22697                 :             :         Oid         parentIdxId,
   22698                 :             :                     parentTblId;
   22699                 :             :         Relation    parentIdx,
   22700                 :             :                     parentTbl;
   22701                 :             : 
   22702                 :             :         /* make sure we see the validation we just did */
   22703                 :          32 :         CommandCounterIncrement();
   22704                 :             : 
   22705                 :          32 :         parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
   22706                 :          32 :         parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
   22707                 :          32 :         parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
   22708                 :          32 :         parentTbl = relation_open(parentTblId, AccessExclusiveLock);
   22709                 :             :         Assert(!parentIdx->rd_index->indisvalid);
   22710                 :             : 
   22711                 :          32 :         validatePartitionedIndex(parentIdx, parentTbl);
   22712                 :             : 
   22713                 :          32 :         relation_close(parentIdx, AccessExclusiveLock);
   22714                 :          32 :         relation_close(parentTbl, AccessExclusiveLock);
   22715                 :             :     }
   22716                 :         252 : }
   22717                 :             : 
   22718                 :             : /*
   22719                 :             :  * When attaching an index as a partition of a partitioned index which is a
   22720                 :             :  * primary key, verify that all the columns in the partition are marked NOT
   22721                 :             :  * NULL.
   22722                 :             :  */
   22723                 :             : static void
   22724                 :          55 : verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
   22725                 :             : {
   22726         [ +  + ]:         111 :     for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
   22727                 :             :     {
   22728                 :          56 :         Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
   22729                 :          56 :                                               iinfo->ii_IndexAttrNumbers[i] - 1);
   22730                 :             : 
   22731         [ -  + ]:          56 :         if (!att->attnotnull)
   22732         [ #  # ]:           0 :             ereport(ERROR,
   22733                 :             :                     errcode(ERRCODE_INVALID_TABLE_DEFINITION),
   22734                 :             :                     errmsg("invalid primary key definition"),
   22735                 :             :                     errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
   22736                 :             :                               NameStr(att->attname),
   22737                 :             :                               RelationGetRelationName(partition)));
   22738                 :             :     }
   22739                 :          55 : }
   22740                 :             : 
   22741                 :             : /*
   22742                 :             :  * Return an OID list of constraints that reference the given relation
   22743                 :             :  * that are marked as having a parent constraints.
   22744                 :             :  */
   22745                 :             : static List *
   22746                 :        1085 : GetParentedForeignKeyRefs(Relation partition)
   22747                 :             : {
   22748                 :             :     Relation    pg_constraint;
   22749                 :             :     HeapTuple   tuple;
   22750                 :             :     SysScanDesc scan;
   22751                 :             :     ScanKeyData key[2];
   22752                 :        1085 :     List       *constraints = NIL;
   22753                 :             : 
   22754                 :             :     /*
   22755                 :             :      * If no indexes, or no columns are referenceable by FKs, we can avoid the
   22756                 :             :      * scan.
   22757                 :             :      */
   22758   [ +  +  +  + ]:        1517 :     if (RelationGetIndexList(partition) == NIL ||
   22759                 :         432 :         bms_is_empty(RelationGetIndexAttrBitmap(partition,
   22760                 :             :                                                 INDEX_ATTR_BITMAP_KEY)))
   22761                 :         826 :         return NIL;
   22762                 :             : 
   22763                 :             :     /* Search for constraints referencing this table */
   22764                 :         259 :     pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
   22765                 :         259 :     ScanKeyInit(&key[0],
   22766                 :             :                 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
   22767                 :             :                 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
   22768                 :         259 :     ScanKeyInit(&key[1],
   22769                 :             :                 Anum_pg_constraint_contype, BTEqualStrategyNumber,
   22770                 :             :                 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
   22771                 :             : 
   22772                 :             :     /* XXX This is a seqscan, as we don't have a usable index */
   22773                 :         259 :     scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
   22774         [ +  + ]:         380 :     while ((tuple = systable_getnext(scan)) != NULL)
   22775                 :             :     {
   22776                 :         121 :         Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   22777                 :             : 
   22778                 :             :         /*
   22779                 :             :          * We only need to process constraints that are part of larger ones.
   22780                 :             :          */
   22781         [ -  + ]:         121 :         if (!OidIsValid(constrForm->conparentid))
   22782                 :           0 :             continue;
   22783                 :             : 
   22784                 :         121 :         constraints = lappend_oid(constraints, constrForm->oid);
   22785                 :             :     }
   22786                 :             : 
   22787                 :         259 :     systable_endscan(scan);
   22788                 :         259 :     table_close(pg_constraint, AccessShareLock);
   22789                 :             : 
   22790                 :         259 :     return constraints;
   22791                 :             : }
   22792                 :             : 
   22793                 :             : /*
   22794                 :             :  * During DETACH PARTITION, verify that any foreign keys pointing to the
   22795                 :             :  * partitioned table would not become invalid.  An error is raised if any
   22796                 :             :  * referenced values exist.
   22797                 :             :  */
   22798                 :             : static void
   22799                 :         344 : ATDetachCheckNoForeignKeyRefs(Relation partition)
   22800                 :             : {
   22801                 :             :     List       *constraints;
   22802                 :             :     ListCell   *cell;
   22803                 :             : 
   22804                 :         344 :     constraints = GetParentedForeignKeyRefs(partition);
   22805                 :             : 
   22806   [ +  +  +  +  :         387 :     foreach(cell, constraints)
                   +  + ]
   22807                 :             :     {
   22808                 :          65 :         Oid         constrOid = lfirst_oid(cell);
   22809                 :             :         HeapTuple   tuple;
   22810                 :             :         Form_pg_constraint constrForm;
   22811                 :             :         Relation    rel;
   22812                 :          65 :         Trigger     trig = {0};
   22813                 :             : 
   22814                 :          65 :         tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
   22815         [ -  + ]:          65 :         if (!HeapTupleIsValid(tuple))
   22816         [ #  # ]:           0 :             elog(ERROR, "cache lookup failed for constraint %u", constrOid);
   22817                 :          65 :         constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
   22818                 :             : 
   22819                 :             :         Assert(OidIsValid(constrForm->conparentid));
   22820                 :             :         Assert(constrForm->confrelid == RelationGetRelid(partition));
   22821                 :             : 
   22822                 :             :         /* prevent data changes into the referencing table until commit */
   22823                 :          65 :         rel = table_open(constrForm->conrelid, ShareLock);
   22824                 :             : 
   22825                 :          65 :         trig.tgoid = InvalidOid;
   22826                 :          65 :         trig.tgname = NameStr(constrForm->conname);
   22827                 :          65 :         trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
   22828                 :          65 :         trig.tgisinternal = true;
   22829                 :          65 :         trig.tgconstrrelid = RelationGetRelid(partition);
   22830                 :          65 :         trig.tgconstrindid = constrForm->conindid;
   22831                 :          65 :         trig.tgconstraint = constrForm->oid;
   22832                 :          65 :         trig.tgdeferrable = false;
   22833                 :          65 :         trig.tginitdeferred = false;
   22834                 :             :         /* we needn't fill in remaining fields */
   22835                 :             : 
   22836                 :          65 :         RI_PartitionRemove_Check(&trig, rel, partition);
   22837                 :             : 
   22838                 :          43 :         ReleaseSysCache(tuple);
   22839                 :             : 
   22840                 :          43 :         table_close(rel, NoLock);
   22841                 :             :     }
   22842                 :         322 : }
   22843                 :             : 
   22844                 :             : /*
   22845                 :             :  * resolve column compression specification to compression method.
   22846                 :             :  */
   22847                 :             : static char
   22848                 :      171658 : GetAttributeCompression(Oid atttypid, const char *compression)
   22849                 :             : {
   22850                 :             :     char        cmethod;
   22851                 :             : 
   22852   [ +  +  +  + ]:      171658 :     if (compression == NULL || strcmp(compression, "default") == 0)
   22853                 :      171519 :         return InvalidCompressionMethod;
   22854                 :             : 
   22855                 :             :     /*
   22856                 :             :      * To specify a nondefault method, the column data type must be toastable.
   22857                 :             :      * Note this says nothing about whether the column's attstorage setting
   22858                 :             :      * permits compression; we intentionally allow attstorage and
   22859                 :             :      * attcompression to be independent.  But with a non-toastable type,
   22860                 :             :      * attstorage could not be set to a value that would permit compression.
   22861                 :             :      *
   22862                 :             :      * We don't actually need to enforce this, since nothing bad would happen
   22863                 :             :      * if attcompression were non-default; it would never be consulted.  But
   22864                 :             :      * it seems more user-friendly to complain about a certainly-useless
   22865                 :             :      * attempt to set the property.
   22866                 :             :      */
   22867         [ +  + ]:         139 :     if (!TypeIsToastable(atttypid))
   22868         [ +  - ]:           4 :         ereport(ERROR,
   22869                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   22870                 :             :                  errmsg("column data type %s does not support compression",
   22871                 :             :                         format_type_be(atttypid))));
   22872                 :             : 
   22873                 :         135 :     cmethod = CompressionNameToMethod(compression);
   22874         [ +  + ]:         135 :     if (!CompressionMethodIsValid(cmethod))
   22875         [ +  - ]:           8 :         ereport(ERROR,
   22876                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   22877                 :             :                  errmsg("invalid compression method \"%s\"", compression)));
   22878                 :             : 
   22879                 :         127 :     return cmethod;
   22880                 :             : }
   22881                 :             : 
   22882                 :             : /*
   22883                 :             :  * resolve column storage specification
   22884                 :             :  */
   22885                 :             : static char
   22886                 :         213 : GetAttributeStorage(Oid atttypid, const char *storagemode)
   22887                 :             : {
   22888                 :         213 :     char        cstorage = 0;
   22889                 :             : 
   22890         [ +  + ]:         213 :     if (pg_strcasecmp(storagemode, "plain") == 0)
   22891                 :          38 :         cstorage = TYPSTORAGE_PLAIN;
   22892         [ +  + ]:         175 :     else if (pg_strcasecmp(storagemode, "external") == 0)
   22893                 :         112 :         cstorage = TYPSTORAGE_EXTERNAL;
   22894         [ +  + ]:          63 :     else if (pg_strcasecmp(storagemode, "extended") == 0)
   22895                 :          26 :         cstorage = TYPSTORAGE_EXTENDED;
   22896         [ +  + ]:          37 :     else if (pg_strcasecmp(storagemode, "main") == 0)
   22897                 :          33 :         cstorage = TYPSTORAGE_MAIN;
   22898         [ +  - ]:           4 :     else if (pg_strcasecmp(storagemode, "default") == 0)
   22899                 :           4 :         cstorage = get_typstorage(atttypid);
   22900                 :             :     else
   22901         [ #  # ]:           0 :         ereport(ERROR,
   22902                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   22903                 :             :                  errmsg("invalid storage type \"%s\"",
   22904                 :             :                         storagemode)));
   22905                 :             : 
   22906                 :             :     /*
   22907                 :             :      * safety check: do not allow toasted storage modes unless column datatype
   22908                 :             :      * is TOAST-aware.
   22909                 :             :      */
   22910   [ +  +  +  + ]:         213 :     if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
   22911         [ +  - ]:           4 :         ereport(ERROR,
   22912                 :             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   22913                 :             :                  errmsg("column data type %s can only have storage PLAIN",
   22914                 :             :                         format_type_be(atttypid))));
   22915                 :             : 
   22916                 :         209 :     return cstorage;
   22917                 :             : }
   22918                 :             : 
   22919                 :             : /*
   22920                 :             :  * buildExpressionExecutionStates: build the needed expression execution states
   22921                 :             :  * for new partition (newPartRel) checks and initialize expressions for
   22922                 :             :  * generated columns. All expressions should be created in "tab"
   22923                 :             :  * (AlteredTableInfo structure).
   22924                 :             :  */
   22925                 :             : static void
   22926                 :         480 : buildExpressionExecutionStates(AlteredTableInfo *tab, Relation newPartRel, EState *estate)
   22927                 :             : {
   22928                 :             :     /*
   22929                 :             :      * Build the needed expression execution states. Here, we expect only NOT
   22930                 :             :      * NULL and CHECK constraint.
   22931                 :             :      */
   22932   [ +  +  +  +  :         976 :     foreach_ptr(NewConstraint, con, tab->constraints)
                   +  + ]
   22933                 :             :     {
   22934      [ +  -  - ]:          16 :         switch (con->contype)
   22935                 :             :         {
   22936                 :          16 :             case CONSTR_CHECK:
   22937                 :             : 
   22938                 :             :                 /*
   22939                 :             :                  * We already expanded virtual expression in
   22940                 :             :                  * createTableConstraints.
   22941                 :             :                  */
   22942                 :          16 :                 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
   22943                 :          16 :                 break;
   22944                 :           0 :             case CONSTR_NOTNULL:
   22945                 :             :                 /* Nothing to do here. */
   22946                 :           0 :                 break;
   22947                 :           0 :             default:
   22948         [ #  # ]:           0 :                 elog(ERROR, "unrecognized constraint type: %d",
   22949                 :             :                      (int) con->contype);
   22950                 :             :         }
   22951                 :             :     }
   22952                 :             : 
   22953                 :             :     /* Expression already planned in createTableConstraints */
   22954   [ +  +  +  +  :        1004 :     foreach_ptr(NewColumnValue, ex, tab->newvals)
                   +  + ]
   22955                 :          44 :         ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
   22956                 :         480 : }
   22957                 :             : 
   22958                 :             : /*
   22959                 :             :  * evaluateGeneratedExpressionsAndCheckConstraints: evaluate any generated
   22960                 :             :  * expressions for "tab" (AlteredTableInfo structure) whose inputs come from
   22961                 :             :  * the new tuple (insertslot) of the new partition (newPartRel).
   22962                 :             :  */
   22963                 :             : static void
   22964                 :         912 : evaluateGeneratedExpressionsAndCheckConstraints(AlteredTableInfo *tab,
   22965                 :             :                                                 Relation newPartRel,
   22966                 :             :                                                 TupleTableSlot *insertslot,
   22967                 :             :                                                 ExprContext *econtext)
   22968                 :             : {
   22969                 :         912 :     econtext->ecxt_scantuple = insertslot;
   22970                 :             : 
   22971   [ +  +  +  +  :        1888 :     foreach_ptr(NewColumnValue, ex, tab->newvals)
                   +  + ]
   22972                 :             :     {
   22973         [ -  + ]:          64 :         if (!ex->is_generated)
   22974                 :           0 :             continue;
   22975                 :             : 
   22976                 :          64 :         insertslot->tts_values[ex->attnum - 1]
   22977                 :          64 :             = ExecEvalExpr(ex->exprstate,
   22978                 :             :                            econtext,
   22979                 :          64 :                            &insertslot->tts_isnull[ex->attnum - 1]);
   22980                 :             :     }
   22981                 :             : 
   22982   [ +  +  +  +  :        1848 :     foreach_ptr(NewConstraint, con, tab->constraints)
                   +  + ]
   22983                 :             :     {
   22984      [ +  -  - ]:          24 :         switch (con->contype)
   22985                 :             :         {
   22986                 :          24 :             case CONSTR_CHECK:
   22987         [ -  + ]:          24 :                 if (!ExecCheck(con->qualstate, econtext))
   22988         [ #  # ]:           0 :                     ereport(ERROR,
   22989                 :             :                             errcode(ERRCODE_CHECK_VIOLATION),
   22990                 :             :                             errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
   22991                 :             :                                    con->name, RelationGetRelationName(newPartRel)),
   22992                 :             :                             errtableconstraint(newPartRel, con->name));
   22993                 :          24 :                 break;
   22994                 :           0 :             case CONSTR_NOTNULL:
   22995                 :             :             case CONSTR_FOREIGN:
   22996                 :             :                 /* Nothing to do here */
   22997                 :           0 :                 break;
   22998                 :           0 :             default:
   22999         [ #  # ]:           0 :                 elog(ERROR, "unrecognized constraint type: %d",
   23000                 :             :                      (int) con->contype);
   23001                 :             :         }
   23002                 :             :     }
   23003                 :         912 : }
   23004                 :             : 
   23005                 :             : /*
   23006                 :             :  * getAttributesList: build a list of columns (ColumnDef) based on parent_rel
   23007                 :             :  */
   23008                 :             : static List *
   23009                 :         508 : getAttributesList(Relation parent_rel)
   23010                 :             : {
   23011                 :             :     AttrNumber  parent_attno;
   23012                 :             :     TupleDesc   modelDesc;
   23013                 :         508 :     List       *colList = NIL;
   23014                 :             : 
   23015                 :         508 :     modelDesc = RelationGetDescr(parent_rel);
   23016                 :             : 
   23017         [ +  + ]:        1721 :     for (parent_attno = 1; parent_attno <= modelDesc->natts;
   23018                 :        1213 :          parent_attno++)
   23019                 :             :     {
   23020                 :        1213 :         Form_pg_attribute attribute = TupleDescAttr(modelDesc,
   23021                 :             :                                                     parent_attno - 1);
   23022                 :             :         ColumnDef  *def;
   23023                 :             : 
   23024                 :             :         /* Ignore dropped columns in the parent. */
   23025         [ -  + ]:        1213 :         if (attribute->attisdropped)
   23026                 :           0 :             continue;
   23027                 :             : 
   23028                 :        1213 :         def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
   23029                 :             :                             attribute->atttypmod, attribute->attcollation);
   23030                 :             : 
   23031                 :        1213 :         def->is_not_null = attribute->attnotnull;
   23032                 :             : 
   23033                 :             :         /* Copy identity. */
   23034                 :        1213 :         def->identity = attribute->attidentity;
   23035                 :             : 
   23036                 :             :         /* Copy attgenerated. */
   23037                 :        1213 :         def->generated = attribute->attgenerated;
   23038                 :             : 
   23039                 :        1213 :         def->storage = attribute->attstorage;
   23040                 :             : 
   23041                 :             :         /* Likewise, copy compression. */
   23042         [ +  + ]:        1213 :         if (CompressionMethodIsValid(attribute->attcompression))
   23043                 :          12 :             def->compression =
   23044                 :          12 :                 pstrdup(GetCompressionMethodName(attribute->attcompression));
   23045                 :             :         else
   23046                 :        1201 :             def->compression = NULL;
   23047                 :             : 
   23048                 :             :         /* Add to column list. */
   23049                 :        1213 :         colList = lappend(colList, def);
   23050                 :             :     }
   23051                 :             : 
   23052                 :         508 :     return colList;
   23053                 :             : }
   23054                 :             : 
   23055                 :             : /*
   23056                 :             :  * createTableConstraints:
   23057                 :             :  * create check constraints, default values, and generated values for newRel
   23058                 :             :  * based on parent_rel.  tab is pending-work queue for newRel, we may need it in
   23059                 :             :  * MergePartitionsMoveRows.
   23060                 :             :  */
   23061                 :             : static void
   23062                 :         480 : createTableConstraints(List **wqueue, AlteredTableInfo *tab,
   23063                 :             :                        Relation parent_rel, Relation newRel)
   23064                 :             : {
   23065                 :             :     TupleDesc   tupleDesc;
   23066                 :             :     TupleConstr *constr;
   23067                 :             :     AttrMap    *attmap;
   23068                 :             :     AttrNumber  parent_attno;
   23069                 :             :     int         ccnum;
   23070                 :         480 :     List       *constraints = NIL;
   23071                 :         480 :     List       *cookedConstraints = NIL;
   23072                 :             : 
   23073                 :         480 :     tupleDesc = RelationGetDescr(parent_rel);
   23074                 :         480 :     constr = tupleDesc->constr;
   23075                 :             : 
   23076         [ +  + ]:         480 :     if (!constr)
   23077                 :         324 :         return;
   23078                 :             : 
   23079                 :             :     /*
   23080                 :             :      * Construct a map from the parent relation's attnos to the child rel's.
   23081                 :             :      * This re-checks type match, etc, although it shouldn't be possible to
   23082                 :             :      * have a failure since both tables are locked.
   23083                 :             :      */
   23084                 :         156 :     attmap = build_attrmap_by_name(RelationGetDescr(newRel),
   23085                 :             :                                    tupleDesc,
   23086                 :             :                                    false);
   23087                 :             : 
   23088                 :             :     /* Cycle for default values. */
   23089         [ +  + ]:         592 :     for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
   23090                 :             :     {
   23091                 :         436 :         Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
   23092                 :             :                                                     parent_attno - 1);
   23093                 :             : 
   23094                 :             :         /* Ignore dropped columns in the parent. */
   23095         [ -  + ]:         436 :         if (attribute->attisdropped)
   23096                 :           0 :             continue;
   23097                 :             : 
   23098                 :             :         /* Copy the default, if present, and it should be copied. */
   23099         [ +  + ]:         436 :         if (attribute->atthasdef)
   23100                 :             :         {
   23101                 :         100 :             Node       *this_default = NULL;
   23102                 :             :             bool        found_whole_row;
   23103                 :             :             AttrNumber  num;
   23104                 :             :             Node       *def;
   23105                 :             :             NewColumnValue *newval;
   23106                 :             : 
   23107         [ +  + ]:         100 :             if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
   23108                 :           4 :                 this_default = build_generation_expression(parent_rel, attribute->attnum);
   23109                 :             :             else
   23110                 :             :             {
   23111                 :          96 :                 this_default = TupleDescGetDefault(tupleDesc, attribute->attnum);
   23112         [ -  + ]:          96 :                 if (this_default == NULL)
   23113         [ #  # ]:           0 :                     elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
   23114                 :             :                          attribute->attnum, RelationGetRelationName(parent_rel));
   23115                 :             :             }
   23116                 :             : 
   23117                 :         100 :             num = attmap->attnums[parent_attno - 1];
   23118                 :         100 :             def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
   23119                 :             : 
   23120   [ -  +  -  - ]:         100 :             if (found_whole_row && attribute->attgenerated != '\0')
   23121         [ #  # ]:           0 :                 elog(ERROR, "cannot convert whole-row table reference");
   23122                 :             : 
   23123                 :             :             /* Add a pre-cooked default expression. */
   23124                 :         100 :             StoreAttrDefault(newRel, num, def, false);
   23125                 :             : 
   23126                 :             :             /*
   23127                 :             :              * Stored generated column expressions in parent_rel might
   23128                 :             :              * reference the tableoid.  newRel, parent_rel tableoid clear is
   23129                 :             :              * not the same. If so, these stored generated columns require
   23130                 :             :              * recomputation for newRel within MergePartitionsMoveRows.
   23131                 :             :              */
   23132         [ +  + ]:         100 :             if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
   23133                 :             :             {
   23134                 :          44 :                 newval = palloc0_object(NewColumnValue);
   23135                 :          44 :                 newval->attnum = num;
   23136                 :          44 :                 newval->expr = expression_planner((Expr *) def);
   23137                 :          44 :                 newval->is_generated = (attribute->attgenerated != '\0');
   23138                 :          44 :                 tab->newvals = lappend(tab->newvals, newval);
   23139                 :             :             }
   23140                 :             :         }
   23141                 :             :     }
   23142                 :             : 
   23143                 :             :     /* Cycle for CHECK constraints. */
   23144         [ +  + ]:         224 :     for (ccnum = 0; ccnum < constr->num_check; ccnum++)
   23145                 :             :     {
   23146                 :          68 :         char       *ccname = constr->check[ccnum].ccname;
   23147                 :          68 :         char       *ccbin = constr->check[ccnum].ccbin;
   23148                 :          68 :         bool        ccenforced = constr->check[ccnum].ccenforced;
   23149                 :          68 :         bool        ccnoinherit = constr->check[ccnum].ccnoinherit;
   23150                 :          68 :         bool        ccvalid = constr->check[ccnum].ccvalid;
   23151                 :             :         Node       *ccbin_node;
   23152                 :             :         bool        found_whole_row;
   23153                 :             :         Constraint *con;
   23154                 :             : 
   23155                 :             :         /*
   23156                 :             :          * The partitioned table can not have a NO INHERIT check constraint
   23157                 :             :          * (see StoreRelCheck function for details).
   23158                 :             :          */
   23159                 :             :         Assert(!ccnoinherit);
   23160                 :             : 
   23161                 :          68 :         ccbin_node = map_variable_attnos(stringToNode(ccbin),
   23162                 :             :                                          1, 0,
   23163                 :             :                                          attmap,
   23164                 :             :                                          InvalidOid, &found_whole_row);
   23165                 :             : 
   23166                 :             :         /*
   23167                 :             :          * For the moment we have to reject whole-row variables (as for CREATE
   23168                 :             :          * TABLE LIKE and inheritances).
   23169                 :             :          */
   23170         [ -  + ]:          68 :         if (found_whole_row)
   23171         [ #  # ]:           0 :             elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
   23172                 :             :                  ccname,
   23173                 :             :                  RelationGetRelationName(parent_rel));
   23174                 :             : 
   23175                 :          68 :         con = makeNode(Constraint);
   23176                 :          68 :         con->contype = CONSTR_CHECK;
   23177                 :          68 :         con->conname = pstrdup(ccname);
   23178                 :          68 :         con->deferrable = false;
   23179                 :          68 :         con->initdeferred = false;
   23180                 :          68 :         con->is_enforced = ccenforced;
   23181                 :          68 :         con->skip_validation = !ccvalid;
   23182                 :          68 :         con->initially_valid = ccvalid;
   23183                 :          68 :         con->is_no_inherit = ccnoinherit;
   23184                 :          68 :         con->raw_expr = NULL;
   23185                 :          68 :         con->cooked_expr = nodeToString(ccbin_node);
   23186                 :          68 :         con->location = -1;
   23187                 :          68 :         constraints = lappend(constraints, con);
   23188                 :             :     }
   23189                 :             : 
   23190                 :             :     /* Install all CHECK constraints. */
   23191                 :         156 :     cookedConstraints = AddRelationNewConstraints(newRel, NIL, constraints,
   23192                 :             :                                                   false, true, false, NULL);
   23193                 :             : 
   23194                 :             :     /* Make the additional catalog changes visible. */
   23195                 :         156 :     CommandCounterIncrement();
   23196                 :             : 
   23197                 :             :     /*
   23198                 :             :      * parent_rel check constraint expression may reference tableoid, so later
   23199                 :             :      * in MergePartitionsMoveRows, we need to evaluate the check constraint
   23200                 :             :      * again for the newRel. We can check whether the check constraint
   23201                 :             :      * contains a tableoid reference via pull_varattnos.
   23202                 :             :      */
   23203   [ +  +  +  +  :         380 :     foreach_ptr(CookedConstraint, ccon, cookedConstraints)
                   +  + ]
   23204                 :             :     {
   23205         [ +  + ]:          68 :         if (!ccon->skip_validation)
   23206                 :             :         {
   23207                 :             :             Node       *qual;
   23208                 :          44 :             Bitmapset  *attnums = NULL;
   23209                 :             : 
   23210                 :             :             Assert(ccon->contype == CONSTR_CHECK);
   23211                 :          44 :             qual = expand_generated_columns_in_expr(ccon->expr, newRel, 1);
   23212                 :          44 :             pull_varattnos(qual, 1, &attnums);
   23213                 :             : 
   23214                 :             :             /*
   23215                 :             :              * Add a check only if it contains a tableoid
   23216                 :             :              * (TableOidAttributeNumber).
   23217                 :             :              */
   23218         [ +  + ]:          44 :             if (bms_is_member(TableOidAttributeNumber - FirstLowInvalidHeapAttributeNumber,
   23219                 :             :                               attnums))
   23220                 :             :             {
   23221                 :             :                 NewConstraint *newcon;
   23222                 :             : 
   23223                 :          16 :                 newcon = palloc0_object(NewConstraint);
   23224                 :          16 :                 newcon->name = ccon->name;
   23225                 :          16 :                 newcon->contype = CONSTR_CHECK;
   23226                 :          16 :                 newcon->qual = qual;
   23227                 :             : 
   23228                 :          16 :                 tab->constraints = lappend(tab->constraints, newcon);
   23229                 :             :             }
   23230                 :             :         }
   23231                 :             :     }
   23232                 :             : 
   23233                 :             :     /* Don't need the cookedConstraints anymore. */
   23234                 :         156 :     list_free_deep(cookedConstraints);
   23235                 :             : 
   23236                 :             :     /* Reproduce not-null constraints. */
   23237         [ +  + ]:         156 :     if (constr->has_not_null)
   23238                 :             :     {
   23239                 :             :         List       *nnconstraints;
   23240                 :             : 
   23241                 :             :         /*
   23242                 :             :          * The "include_noinh" argument is false because a partitioned table
   23243                 :             :          * can't have NO INHERIT constraint.
   23244                 :             :          */
   23245                 :         108 :         nnconstraints = RelationGetNotNullConstraints(RelationGetRelid(parent_rel),
   23246                 :             :                                                       false, false);
   23247                 :             : 
   23248                 :             :         Assert(list_length(nnconstraints) > 0);
   23249                 :             : 
   23250                 :             :         /*
   23251                 :             :          * We already set pg_attribute.attnotnull in createPartitionTable. No
   23252                 :             :          * need call set_attnotnull again.
   23253                 :             :          */
   23254                 :         108 :         AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, false, NULL);
   23255                 :             :     }
   23256                 :             : }
   23257                 :             : 
   23258                 :             : /*
   23259                 :             :  * createPartitionTable:
   23260                 :             :  *
   23261                 :             :  * Create a new partition (newPartName) for the partitioned table (parent_rel).
   23262                 :             :  * ownerId is determined by the partition on which the operation is performed,
   23263                 :             :  * so it is passed separately.  The new partition will inherit the access method
   23264                 :             :  * and persistence type from the parent table.
   23265                 :             :  *
   23266                 :             :  * Returns the created relation (locked in AccessExclusiveLock mode).
   23267                 :             :  */
   23268                 :             : static Relation
   23269                 :         508 : createPartitionTable(List **wqueue, RangeVar *newPartName,
   23270                 :             :                      Relation parent_rel, Oid ownerId)
   23271                 :             : {
   23272                 :             :     Relation    newRel;
   23273                 :             :     Oid         newRelId;
   23274                 :             :     Oid         existingRelid;
   23275                 :             :     Oid         tablespaceId;
   23276                 :             :     TupleDesc   descriptor;
   23277                 :         508 :     List       *colList = NIL;
   23278                 :             :     Oid         relamId;
   23279                 :             :     Oid         namespaceId;
   23280                 :             :     AlteredTableInfo *new_partrel_tab;
   23281                 :         508 :     Form_pg_class parent_relform = parent_rel->rd_rel;
   23282                 :             : 
   23283                 :             :     /* If the existing rel is temp, it must belong to this session. */
   23284   [ +  +  -  + ]:         508 :     if (RELATION_IS_OTHER_TEMP(parent_rel))
   23285         [ #  # ]:           0 :         ereport(ERROR,
   23286                 :             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   23287                 :             :                 errmsg("cannot create as partition of temporary relation of another session"));
   23288                 :             : 
   23289                 :             :     /* Look up inheritance ancestors and generate the relation schema. */
   23290                 :         508 :     colList = getAttributesList(parent_rel);
   23291                 :             : 
   23292                 :             :     /* Create a tuple descriptor from the relation schema. */
   23293                 :         508 :     descriptor = BuildDescForRelation(colList);
   23294                 :             : 
   23295                 :             :     /* Look up the access method for the new relation. */
   23296         [ +  + ]:         508 :     relamId = (parent_relform->relam != InvalidOid) ? parent_relform->relam : HEAP_TABLE_AM_OID;
   23297                 :             : 
   23298                 :             :     /* Look up the namespace in which we are supposed to create the relation. */
   23299                 :             :     namespaceId =
   23300                 :         508 :         RangeVarGetAndCheckCreationNamespace(newPartName, NoLock, &existingRelid);
   23301         [ -  + ]:         508 :     if (OidIsValid(existingRelid))
   23302         [ #  # ]:           0 :         ereport(ERROR,
   23303                 :             :                 errcode(ERRCODE_DUPLICATE_TABLE),
   23304                 :             :                 errmsg("relation \"%s\" already exists", newPartName->relname));
   23305                 :             : 
   23306                 :             :     /*
   23307                 :             :      * We intended to create the partition with the same persistence as the
   23308                 :             :      * parent table, but we still need to recheck because that might be
   23309                 :             :      * affected by the search_path.  If the parent is permanent, so must be
   23310                 :             :      * all of its partitions.
   23311                 :             :      */
   23312         [ +  + ]:         508 :     if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
   23313         [ +  + ]:         472 :         newPartName->relpersistence == RELPERSISTENCE_TEMP)
   23314         [ +  - ]:           8 :         ereport(ERROR,
   23315                 :             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   23316                 :             :                 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
   23317                 :             :                        RelationGetRelationName(parent_rel)));
   23318                 :             : 
   23319                 :             :     /* Permanent rels cannot be partitions belonging to a temporary parent. */
   23320         [ +  + ]:         500 :     if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
   23321         [ +  + ]:         476 :         parent_relform->relpersistence == RELPERSISTENCE_TEMP)
   23322         [ +  - ]:          12 :         ereport(ERROR,
   23323                 :             :                 errcode(ERRCODE_WRONG_OBJECT_TYPE),
   23324                 :             :                 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
   23325                 :             :                        RelationGetRelationName(parent_rel)));
   23326                 :             : 
   23327                 :             :     /*
   23328                 :             :      * Select the tablespace for the new partition.  Mirror the logic that
   23329                 :             :      * CREATE TABLE foo PARTITION OF ... uses in DefineRelation: take the
   23330                 :             :      * partitioned parent's explicit tablespace if it has one, otherwise take
   23331                 :             :      * default_tablespace into account, and finally use the database default.
   23332                 :             :      */
   23333                 :         488 :     tablespaceId = parent_relform->reltablespace;
   23334         [ +  + ]:         488 :     if (!OidIsValid(tablespaceId))
   23335                 :         464 :         tablespaceId = GetDefaultTablespace(newPartName->relpersistence, false);
   23336                 :             : 
   23337                 :             :     /* Check permissions except when using database's default */
   23338   [ +  +  +  - ]:         488 :     if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
   23339                 :             :     {
   23340                 :             :         AclResult   aclresult;
   23341                 :             : 
   23342                 :          44 :         aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId,
   23343                 :             :                                     GetUserId(), ACL_CREATE);
   23344         [ -  + ]:          44 :         if (aclresult != ACLCHECK_OK)
   23345                 :           0 :             aclcheck_error(aclresult, OBJECT_TABLESPACE,
   23346                 :           0 :                            get_tablespace_name(tablespaceId));
   23347                 :             :     }
   23348                 :             : 
   23349                 :             :     /* In all cases disallow placing user relations in pg_global */
   23350         [ +  + ]:         488 :     if (tablespaceId == GLOBALTABLESPACE_OID)
   23351         [ +  - ]:           8 :         ereport(ERROR,
   23352                 :             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
   23353                 :             :                  errmsg("only shared relations can be placed in pg_global tablespace")));
   23354                 :             : 
   23355                 :             :     /* Create the relation. */
   23356                 :         480 :     newRelId = heap_create_with_catalog(newPartName->relname,
   23357                 :             :                                         namespaceId,
   23358                 :             :                                         tablespaceId,
   23359                 :             :                                         InvalidOid,
   23360                 :             :                                         InvalidOid,
   23361                 :             :                                         InvalidOid,
   23362                 :             :                                         ownerId,
   23363                 :             :                                         relamId,
   23364                 :             :                                         descriptor,
   23365                 :             :                                         NIL,
   23366                 :             :                                         RELKIND_RELATION,
   23367                 :         480 :                                         newPartName->relpersistence,
   23368                 :             :                                         false,
   23369                 :             :                                         false,
   23370                 :             :                                         ONCOMMIT_NOOP,
   23371                 :             :                                         (Datum) 0,
   23372                 :             :                                         true,
   23373                 :             :                                         allowSystemTableMods,
   23374                 :             :                                         false,  /* is_internal */
   23375                 :             :                                         InvalidOid,
   23376                 :             :                                         NULL);
   23377                 :             : 
   23378                 :             :     /*
   23379                 :             :      * We must bump the command counter to make the newly-created relation
   23380                 :             :      * tuple visible for opening.
   23381                 :             :      */
   23382                 :         480 :     CommandCounterIncrement();
   23383                 :             : 
   23384                 :             :     /*
   23385                 :             :      * Create a TOAST table if the table needs one.  MERGE/SPLIT PARTITION
   23386                 :             :      * moves rows from existing partition(s) into new partition(s), which may
   23387                 :             :      * carry out-of-line varlena values that the new relation must be able to
   23388                 :             :      * store.  Also, the new partition must be able to receive out-of-line
   23389                 :             :      * varlena values after the DDL operation is complete.
   23390                 :             :      */
   23391                 :         480 :     NewRelationCreateToastTable(newRelId, (Datum) 0);
   23392                 :             : 
   23393                 :             :     /*
   23394                 :             :      * Open the new partition with no lock, because we already have an
   23395                 :             :      * AccessExclusiveLock placed there after creation.
   23396                 :             :      */
   23397                 :         480 :     newRel = table_open(newRelId, NoLock);
   23398                 :             : 
   23399                 :             :     /* Find or create a work queue entry for the newly created table. */
   23400                 :         480 :     new_partrel_tab = ATGetQueueEntry(wqueue, newRel);
   23401                 :             : 
   23402                 :             :     /* Create constraints, default values, and generated values. */
   23403                 :         480 :     createTableConstraints(wqueue, new_partrel_tab, parent_rel, newRel);
   23404                 :             : 
   23405                 :             :     /*
   23406                 :             :      * Need to call CommandCounterIncrement, so a fresh relcache entry has
   23407                 :             :      * newly installed constraint info.
   23408                 :             :      */
   23409                 :         480 :     CommandCounterIncrement();
   23410                 :             : 
   23411                 :         480 :     return newRel;
   23412                 :             : }
   23413                 :             : 
   23414                 :             : /*
   23415                 :             :  * MergePartitionsMoveRows: scan partitions to be merged (mergingPartitions)
   23416                 :             :  * of the partitioned table and move rows into the new partition
   23417                 :             :  * (newPartRel). We also verify check constraints against these rows.
   23418                 :             :  */
   23419                 :             : static void
   23420                 :         106 : MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
   23421                 :             : {
   23422                 :             :     CommandId   mycid;
   23423                 :             :     EState     *estate;
   23424                 :             :     AlteredTableInfo *tab;
   23425                 :             :     ListCell   *ltab;
   23426                 :             : 
   23427                 :             :     /* The FSM is empty, so don't bother using it. */
   23428                 :         106 :     uint32      ti_options = TABLE_INSERT_SKIP_FSM;
   23429                 :             :     BulkInsertState bistate;    /* state of bulk inserts for partition */
   23430                 :             :     TupleTableSlot *dstslot;
   23431                 :             : 
   23432                 :             :     /* Find the work queue entry for the new partition table: newPartRel. */
   23433                 :         106 :     tab = ATGetQueueEntry(wqueue, newPartRel);
   23434                 :             : 
   23435                 :             :     /* Generate the constraint and default execution states. */
   23436                 :         106 :     estate = CreateExecutorState();
   23437                 :             : 
   23438                 :         106 :     buildExpressionExecutionStates(tab, newPartRel, estate);
   23439                 :             : 
   23440                 :         106 :     mycid = GetCurrentCommandId(true);
   23441                 :             : 
   23442                 :             :     /* Prepare a BulkInsertState for table_tuple_insert. */
   23443                 :         106 :     bistate = GetBulkInsertState();
   23444                 :             : 
   23445                 :             :     /* Create the necessary tuple slot. */
   23446                 :         106 :     dstslot = table_slot_create(newPartRel, NULL);
   23447                 :             : 
   23448   [ +  -  +  +  :         452 :     foreach_oid(merging_oid, mergingPartitions)
                   +  + ]
   23449                 :             :     {
   23450                 :             :         ExprContext *econtext;
   23451                 :             :         TupleTableSlot *srcslot;
   23452                 :             :         TupleConversionMap *tuple_map;
   23453                 :             :         TableScanDesc scan;
   23454                 :             :         MemoryContext oldCxt;
   23455                 :             :         Snapshot    snapshot;
   23456                 :             :         Relation    mergingPartition;
   23457                 :             : 
   23458         [ +  + ]:         240 :         econtext = GetPerTupleExprContext(estate);
   23459                 :             : 
   23460                 :             :         /*
   23461                 :             :          * Partition is already locked in the transformPartitionCmdForMerge
   23462                 :             :          * function.
   23463                 :             :          */
   23464                 :         240 :         mergingPartition = table_open(merging_oid, NoLock);
   23465                 :             : 
   23466                 :             :         /* Create a source tuple slot for the partition being merged. */
   23467                 :         240 :         srcslot = table_slot_create(mergingPartition, NULL);
   23468                 :             : 
   23469                 :             :         /*
   23470                 :             :          * Map computing for moving attributes of the merged partition to the
   23471                 :             :          * new partition.
   23472                 :             :          */
   23473                 :         240 :         tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
   23474                 :             :                                            RelationGetDescr(newPartRel));
   23475                 :             : 
   23476                 :             :         /* Scan through the rows. */
   23477                 :         240 :         snapshot = RegisterSnapshot(GetLatestSnapshot());
   23478                 :         240 :         scan = table_beginscan(mergingPartition, snapshot, 0, NULL,
   23479                 :             :                                SO_NONE);
   23480                 :             : 
   23481                 :             :         /*
   23482                 :             :          * Switch to per-tuple memory context and reset it for each tuple
   23483                 :             :          * produced, so we don't leak memory.
   23484                 :             :          */
   23485         [ +  - ]:         240 :         oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
   23486                 :             : 
   23487         [ +  + ]:         605 :         while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
   23488                 :             :         {
   23489                 :             :             TupleTableSlot *insertslot;
   23490                 :             : 
   23491         [ -  + ]:         365 :             CHECK_FOR_INTERRUPTS();
   23492                 :             : 
   23493         [ +  + ]:         365 :             if (tuple_map)
   23494                 :             :             {
   23495                 :             :                 /* Need to use a map to copy attributes. */
   23496                 :          28 :                 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
   23497                 :             :             }
   23498                 :             :             else
   23499                 :             :             {
   23500                 :         337 :                 slot_getallattrs(srcslot);
   23501                 :             : 
   23502                 :             :                 /* Copy attributes directly. */
   23503                 :         337 :                 insertslot = dstslot;
   23504                 :             : 
   23505                 :         337 :                 ExecClearTuple(insertslot);
   23506                 :             : 
   23507                 :         337 :                 memcpy(insertslot->tts_values, srcslot->tts_values,
   23508                 :         337 :                        sizeof(Datum) * srcslot->tts_nvalid);
   23509                 :         337 :                 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
   23510                 :         337 :                        sizeof(bool) * srcslot->tts_nvalid);
   23511                 :             : 
   23512                 :         337 :                 ExecStoreVirtualTuple(insertslot);
   23513                 :             :             }
   23514                 :             : 
   23515                 :             :             /*
   23516                 :             :              * Constraints and GENERATED expressions might reference the
   23517                 :             :              * tableoid column, so fill tts_tableOid with the desired value.
   23518                 :             :              * (We must do this each time, because it gets overwritten with
   23519                 :             :              * newrel's OID during storing.)
   23520                 :             :              */
   23521                 :         365 :             insertslot->tts_tableOid = RelationGetRelid(newPartRel);
   23522                 :             : 
   23523                 :             :             /*
   23524                 :             :              * Now, evaluate any generated expressions whose inputs come from
   23525                 :             :              * the new tuple.  We assume these columns won't reference each
   23526                 :             :              * other, so that there's no ordering dependency.
   23527                 :             :              */
   23528                 :         365 :             evaluateGeneratedExpressionsAndCheckConstraints(tab, newPartRel,
   23529                 :             :                                                             insertslot, econtext);
   23530                 :             : 
   23531                 :             :             /* Write the tuple out to the new relation. */
   23532                 :         365 :             table_tuple_insert(newPartRel, insertslot, mycid,
   23533                 :             :                                ti_options, bistate);
   23534                 :             : 
   23535                 :         365 :             ResetExprContext(econtext);
   23536                 :             :         }
   23537                 :             : 
   23538                 :         240 :         MemoryContextSwitchTo(oldCxt);
   23539                 :         240 :         table_endscan(scan);
   23540                 :         240 :         UnregisterSnapshot(snapshot);
   23541                 :             : 
   23542         [ +  + ]:         240 :         if (tuple_map)
   23543                 :          20 :             free_conversion_map(tuple_map);
   23544                 :             : 
   23545                 :         240 :         ExecDropSingleTupleTableSlot(srcslot);
   23546                 :         240 :         table_close(mergingPartition, NoLock);
   23547                 :             :     }
   23548                 :             : 
   23549                 :         106 :     FreeExecutorState(estate);
   23550                 :         106 :     ExecDropSingleTupleTableSlot(dstslot);
   23551                 :         106 :     FreeBulkInsertState(bistate);
   23552                 :             : 
   23553                 :         106 :     table_finish_bulk_insert(newPartRel, ti_options);
   23554                 :             : 
   23555                 :             :     /*
   23556                 :             :      * We don't need to process this newPartRel since we already processed it
   23557                 :             :      * here, so delete the ALTER TABLE queue for it.
   23558                 :             :      */
   23559   [ +  -  +  -  :         212 :     foreach(ltab, *wqueue)
                   +  - ]
   23560                 :             :     {
   23561                 :         212 :         tab = (AlteredTableInfo *) lfirst(ltab);
   23562         [ +  + ]:         212 :         if (tab->relid == RelationGetRelid(newPartRel))
   23563                 :             :         {
   23564                 :         106 :             *wqueue = list_delete_cell(*wqueue, ltab);
   23565                 :         106 :             break;
   23566                 :             :         }
   23567                 :             :     }
   23568                 :         106 : }
   23569                 :             : 
   23570                 :             : /*
   23571                 :             :  * detachPartitionTable: detach partition "child_rel" from partitioned table
   23572                 :             :  * "parent_rel" with default partition identifier "defaultPartOid"
   23573                 :             :  */
   23574                 :             : static void
   23575                 :         437 : detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
   23576                 :             : {
   23577                 :             :     /* Remove the pg_inherits row first. */
   23578                 :         437 :     RemoveInheritance(child_rel, parent_rel, false);
   23579                 :             : 
   23580                 :             :     /*
   23581                 :             :      * Detaching the partition might involve TOAST table access, so ensure we
   23582                 :             :      * have a valid snapshot.
   23583                 :             :      */
   23584                 :         437 :     PushActiveSnapshot(GetTransactionSnapshot());
   23585                 :             : 
   23586                 :             :     /* Do the final part of detaching. */
   23587                 :         437 :     DetachPartitionFinalize(parent_rel, child_rel, false, defaultPartOid);
   23588                 :             : 
   23589                 :         437 :     PopActiveSnapshot();
   23590                 :         437 : }
   23591                 :             : 
   23592                 :             : /*
   23593                 :             :  * equal_oid_lists: return true if two OID lists, each sorted in ascending
   23594                 :             :  * order, contain the same OIDs in the same order.
   23595                 :             :  */
   23596                 :             : static bool
   23597                 :          86 : equal_oid_lists(const List *a, const List *b)
   23598                 :             : {
   23599                 :             :     ListCell   *la,
   23600                 :             :                *lb;
   23601                 :             : 
   23602         [ +  + ]:          86 :     if (list_length(a) != list_length(b))
   23603                 :           4 :         return false;
   23604                 :             : 
   23605   [ +  +  +  +  :          86 :     forboth(la, a, lb, b)
          +  +  +  +  +  
             +  +  -  +  
                      + ]
   23606                 :             :     {
   23607         [ +  + ]:           6 :         if (lfirst_oid(la) != lfirst_oid(lb))
   23608                 :           2 :             return false;
   23609                 :             :     }
   23610                 :          80 :     return true;
   23611                 :             : }
   23612                 :             : 
   23613                 :             : /*
   23614                 :             :  * Comparator for list_sort() on a list of PartitionIndexExtDepEntry *.
   23615                 :             :  * Orders by parentIndexOid, then by indexOid as a tiebreaker so conflict
   23616                 :             :  * reports for different parent indexes are deterministic.
   23617                 :             :  */
   23618                 :             : static int
   23619                 :         145 : cmp_partition_index_ext_dep(const ListCell *a, const ListCell *b)
   23620                 :             : {
   23621                 :         145 :     const PartitionIndexExtDepEntry *ea = lfirst(a);
   23622                 :         145 :     const PartitionIndexExtDepEntry *eb = lfirst(b);
   23623                 :             : 
   23624         [ +  + ]:         145 :     if (ea->parentIndexOid != eb->parentIndexOid)
   23625                 :          53 :         return pg_cmp_u32(ea->parentIndexOid, eb->parentIndexOid);
   23626                 :          92 :     return pg_cmp_u32(ea->indexOid, eb->indexOid);
   23627                 :             : }
   23628                 :             : 
   23629                 :             : /*
   23630                 :             :  * collectPartitionIndexExtDeps: collect extension dependencies from indexes
   23631                 :             :  * on the given partitions.
   23632                 :             :  *
   23633                 :             :  * For each partition index that has a parent partitioned index, we collect
   23634                 :             :  * extension dependencies. All source partition indexes sharing the same
   23635                 :             :  * parent partitioned index must depend on exactly the same set of
   23636                 :             :  * extensions; otherwise an error is raised so that we neither silently drop
   23637                 :             :  * nor silently add dependencies on the merged partition's index.
   23638                 :             :  *
   23639                 :             :  * Indexes that don't have a parent partitioned index (i.e., indexes created
   23640                 :             :  * directly on a partition without a corresponding parent index) are skipped.
   23641                 :             :  *
   23642                 :             :  * The returned list is sorted by parentIndexOid with exactly one entry per
   23643                 :             :  * parent partitioned index, so applyPartitionIndexExtDeps() can scan it
   23644                 :             :  * linearly.
   23645                 :             :  */
   23646                 :             : static List *
   23647                 :         285 : collectPartitionIndexExtDeps(List *partitionOids)
   23648                 :             : {
   23649                 :         285 :     List       *collected = NIL;
   23650                 :         285 :     List       *result = NIL;
   23651                 :         285 :     PartitionIndexExtDepEntry *prev = NULL;
   23652                 :             : 
   23653                 :             :     /*
   23654                 :             :      * Phase 1: collect one entry per (partition index -> parent index) pair,
   23655                 :             :      * with its extension dependency OIDs sorted ascending.
   23656                 :             :      */
   23657   [ +  -  +  +  :        1019 :     foreach_oid(partOid, partitionOids)
                   +  + ]
   23658                 :             :     {
   23659                 :             :         Relation    partRel;
   23660                 :             :         List       *indexList;
   23661                 :             : 
   23662                 :             :         /*
   23663                 :             :          * Use NoLock since the caller already holds AccessExclusiveLock on
   23664                 :             :          * these partitions.
   23665                 :             :          */
   23666                 :         449 :         partRel = table_open(partOid, NoLock);
   23667                 :         449 :         indexList = RelationGetIndexList(partRel);
   23668                 :             : 
   23669   [ +  +  +  +  :        1097 :         foreach_oid(indexOid, indexList)
                   +  + ]
   23670                 :             :         {
   23671                 :             :             Oid         parentIndexOid;
   23672                 :             :             PartitionIndexExtDepEntry *entry;
   23673                 :             : 
   23674         [ +  + ]:         199 :             if (!get_rel_relispartition(indexOid))
   23675                 :           1 :                 continue;
   23676                 :             : 
   23677                 :         198 :             parentIndexOid = get_partition_parent(indexOid, true);
   23678         [ -  + ]:         198 :             if (!OidIsValid(parentIndexOid))
   23679                 :           0 :                 continue;
   23680                 :             : 
   23681                 :         198 :             entry = palloc(sizeof(PartitionIndexExtDepEntry));
   23682                 :         198 :             entry->parentIndexOid = parentIndexOid;
   23683                 :         198 :             entry->indexOid = indexOid;
   23684                 :         198 :             entry->extensionOids = getAutoExtensionsOfObject(RelationRelationId,
   23685                 :             :                                                              indexOid);
   23686                 :         198 :             list_sort(entry->extensionOids, list_oid_cmp);
   23687                 :             : 
   23688                 :         198 :             collected = lappend(collected, entry);
   23689                 :             :         }
   23690                 :             : 
   23691                 :         449 :         list_free(indexList);
   23692                 :         449 :         table_close(partRel, NoLock);
   23693                 :             :     }
   23694                 :             : 
   23695                 :             :     /*
   23696                 :             :      * Phase 2: sort by parentIndexOid so entries sharing a parent index sit
   23697                 :             :      * adjacent.
   23698                 :             :      */
   23699                 :         285 :     list_sort(collected, cmp_partition_index_ext_dep);
   23700                 :             : 
   23701                 :             :     /*
   23702                 :             :      * Phase 3: single linear pass verifying that adjacent entries sharing a
   23703                 :             :      * parent index have identical extension dependencies, and keeping one
   23704                 :             :      * representative entry per parent index.
   23705                 :             :      */
   23706   [ +  +  +  +  :         744 :     foreach_ptr(PartitionIndexExtDepEntry, entry, collected)
                   +  + ]
   23707                 :             :     {
   23708   [ +  +  +  + ]:         186 :         if (prev != NULL && prev->parentIndexOid == entry->parentIndexOid)
   23709                 :             :         {
   23710         [ +  + ]:          86 :             if (!equal_oid_lists(prev->extensionOids, entry->extensionOids))
   23711         [ +  - ]:           6 :                 ereport(ERROR,
   23712                 :             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   23713                 :             :                          errmsg("cannot merge partitions with conflicting extension dependencies"),
   23714                 :             :                          errdetail("Partition indexes \"%s\" and \"%s\" depend on different extensions.",
   23715                 :             :                                    get_rel_name(prev->indexOid),
   23716                 :             :                                    get_rel_name(entry->indexOid))));
   23717                 :             : 
   23718                 :             :             /* Duplicate entry for the same parent index; discard. */
   23719                 :          80 :             list_free(entry->extensionOids);
   23720                 :          80 :             pfree(entry);
   23721                 :          80 :             continue;
   23722                 :             :         }
   23723                 :             : 
   23724                 :         100 :         result = lappend(result, entry);
   23725                 :         100 :         prev = entry;
   23726                 :             :     }
   23727                 :             : 
   23728                 :         279 :     list_free(collected);
   23729                 :             : 
   23730                 :         279 :     return result;
   23731                 :             : }
   23732                 :             : 
   23733                 :             : /*
   23734                 :             :  * applyPartitionIndexExtDeps: apply collected extension dependencies to
   23735                 :             :  * indexes on a new partition.
   23736                 :             :  *
   23737                 :             :  * For each index on the new partition, look up its parent index in the
   23738                 :             :  * extDepState list. If found, record extension dependencies on the new index.
   23739                 :             :  * extDepState is sorted by parentIndexOid, so the inner scan can bail out
   23740                 :             :  * as soon as it passes the target OID.
   23741                 :             :  */
   23742                 :             : static void
   23743                 :         480 : applyPartitionIndexExtDeps(Oid newPartOid, List *extDepState)
   23744                 :             : {
   23745                 :             :     Relation    partRel;
   23746                 :             :     List       *indexList;
   23747                 :             : 
   23748         [ +  + ]:         480 :     if (extDepState == NIL)
   23749                 :         332 :         return;
   23750                 :             : 
   23751                 :             :     /*
   23752                 :             :      * Use NoLock since the caller already holds AccessExclusiveLock on the
   23753                 :             :      * new partition.
   23754                 :             :      */
   23755                 :         148 :     partRel = table_open(newPartOid, NoLock);
   23756                 :         148 :     indexList = RelationGetIndexList(partRel);
   23757                 :             : 
   23758   [ +  -  +  +  :         468 :     foreach_oid(indexOid, indexList)
                   +  + ]
   23759                 :             :     {
   23760                 :             :         Oid         parentIdxOid;
   23761                 :             : 
   23762         [ -  + ]:         172 :         if (!get_rel_relispartition(indexOid))
   23763                 :           0 :             continue;
   23764                 :             : 
   23765                 :         172 :         parentIdxOid = get_partition_parent(indexOid, true);
   23766         [ -  + ]:         172 :         if (!OidIsValid(parentIdxOid))
   23767                 :           0 :             continue;
   23768                 :             : 
   23769   [ +  -  +  -  :         368 :         foreach_ptr(PartitionIndexExtDepEntry, entry, extDepState)
                   +  + ]
   23770                 :             :         {
   23771                 :             :             ObjectAddress indexAddr;
   23772                 :             : 
   23773         [ -  + ]:         196 :             if (entry->parentIndexOid > parentIdxOid)
   23774                 :         172 :                 break;
   23775         [ +  + ]:         196 :             if (entry->parentIndexOid < parentIdxOid)
   23776                 :          24 :                 continue;
   23777                 :             : 
   23778                 :         172 :             ObjectAddressSet(indexAddr, RelationRelationId, indexOid);
   23779                 :             : 
   23780   [ +  +  +  +  :         352 :             foreach_oid(extOid, entry->extensionOids)
                   +  + ]
   23781                 :             :             {
   23782                 :             :                 ObjectAddress extAddr;
   23783                 :             : 
   23784                 :           8 :                 ObjectAddressSet(extAddr, ExtensionRelationId, extOid);
   23785                 :           8 :                 recordDependencyOn(&indexAddr, &extAddr,
   23786                 :             :                                    DEPENDENCY_AUTO_EXTENSION);
   23787                 :             :             }
   23788                 :         172 :             break;
   23789                 :             :         }
   23790                 :             :     }
   23791                 :             : 
   23792                 :         148 :     list_free(indexList);
   23793                 :         148 :     table_close(partRel, NoLock);
   23794                 :             : }
   23795                 :             : 
   23796                 :             : /*
   23797                 :             :  * freePartitionIndexExtDeps: free memory allocated by collectPartitionIndexExtDeps.
   23798                 :             :  */
   23799                 :             : static void
   23800                 :         247 : freePartitionIndexExtDeps(List *extDepState)
   23801                 :             : {
   23802   [ +  +  +  +  :         588 :     foreach_ptr(PartitionIndexExtDepEntry, entry, extDepState)
                   +  + ]
   23803                 :             :     {
   23804                 :          94 :         list_free(entry->extensionOids);
   23805                 :          94 :         pfree(entry);
   23806                 :             :     }
   23807                 :         247 :     list_free(extDepState);
   23808                 :         247 : }
   23809                 :             : 
   23810                 :             : /*
   23811                 :             :  * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
   23812                 :             :  */
   23813                 :             : static void
   23814                 :         144 : ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
   23815                 :             :                       PartitionCmd *cmd, AlterTableUtilityContext *context)
   23816                 :             : {
   23817                 :             :     Relation    newPartRel;
   23818                 :         144 :     List       *mergingPartitions = NIL;
   23819                 :         144 :     List       *extDepState = NIL;
   23820                 :             :     Oid         defaultPartOid;
   23821                 :             :     Oid         existingRelid;
   23822                 :         144 :     Oid         ownerId = InvalidOid;
   23823                 :             :     Oid         save_userid;
   23824                 :             :     int         save_sec_context;
   23825                 :             :     int         save_nestlevel;
   23826                 :             : 
   23827                 :             :     /*
   23828                 :             :      * Check ownership of merged partitions - partitions with different owners
   23829                 :             :      * cannot be merged. Also, collect the OIDs of these partitions during the
   23830                 :             :      * check.
   23831                 :             :      */
   23832   [ +  -  +  +  :         600 :     foreach_node(RangeVar, name, cmd->partlist)
                   +  + ]
   23833                 :             :     {
   23834                 :             :         Relation    mergingPartition;
   23835                 :             : 
   23836                 :             :         /*
   23837                 :             :          * We are going to detach and remove this partition.  We already took
   23838                 :             :          * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
   23839                 :             :          * NoLock is fine.
   23840                 :             :          */
   23841                 :         320 :         mergingPartition = table_openrv_extended(name, NoLock, false);
   23842                 :             :         Assert(CheckRelationLockedByMe(mergingPartition, AccessExclusiveLock, false));
   23843                 :             : 
   23844         [ +  + ]:         320 :         if (OidIsValid(ownerId))
   23845                 :             :         {
   23846                 :             :             /* Do the partitions being merged have different owners? */
   23847         [ +  + ]:         176 :             if (ownerId != mergingPartition->rd_rel->relowner)
   23848         [ +  - ]:           4 :                 ereport(ERROR,
   23849                 :             :                         errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
   23850                 :             :                         errmsg("partitions being merged have different owners"));
   23851                 :             :         }
   23852                 :             :         else
   23853                 :         144 :             ownerId = mergingPartition->rd_rel->relowner;
   23854                 :             : 
   23855                 :             :         /* Store the next merging partition into the list. */
   23856                 :         316 :         mergingPartitions = lappend_oid(mergingPartitions,
   23857                 :             :                                         RelationGetRelid(mergingPartition));
   23858                 :             : 
   23859                 :         316 :         table_close(mergingPartition, NoLock);
   23860                 :             :     }
   23861                 :             : 
   23862                 :             :     /* Look up the existing relation by the new partition name. */
   23863                 :         140 :     RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, &existingRelid);
   23864                 :             : 
   23865                 :             :     /*
   23866                 :             :      * Check if this name is already taken.  This helps us to detect the
   23867                 :             :      * situation when one of the merging partitions has the same name as the
   23868                 :             :      * new partition.  Otherwise, this would fail later on anyway, but
   23869                 :             :      * catching this here allows us to emit a nicer error message.
   23870                 :             :      */
   23871         [ +  + ]:         140 :     if (OidIsValid(existingRelid))
   23872                 :             :     {
   23873         [ +  + ]:          17 :         if (list_member_oid(mergingPartitions, existingRelid))
   23874                 :             :         {
   23875                 :             :             /*
   23876                 :             :              * The new partition has the same name as one of the merging
   23877                 :             :              * partitions.
   23878                 :             :              */
   23879                 :             :             char        tmpRelName[NAMEDATALEN];
   23880                 :             : 
   23881                 :             :             /* Generate a temporary name. */
   23882                 :          13 :             sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
   23883                 :             : 
   23884                 :             :             /*
   23885                 :             :              * Rename the existing partition with a temporary name, leaving it
   23886                 :             :              * free for the new partition.  We don't need to care about this
   23887                 :             :              * in the future because we're going to eventually drop the
   23888                 :             :              * existing partition anyway.
   23889                 :             :              */
   23890                 :          13 :             RenameRelationInternal(existingRelid, tmpRelName, true, false);
   23891                 :             : 
   23892                 :             :             /*
   23893                 :             :              * We must bump the command counter to make the new partition
   23894                 :             :              * tuple visible for rename.
   23895                 :             :              */
   23896                 :          13 :             CommandCounterIncrement();
   23897                 :             :         }
   23898                 :             :         else
   23899                 :             :         {
   23900         [ +  - ]:           4 :             ereport(ERROR,
   23901                 :             :                     errcode(ERRCODE_DUPLICATE_TABLE),
   23902                 :             :                     errmsg("relation \"%s\" already exists", cmd->name->relname));
   23903                 :             :         }
   23904                 :             :     }
   23905                 :             : 
   23906                 :             :     defaultPartOid =
   23907                 :         136 :         get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   23908                 :             : 
   23909                 :             :     /*
   23910                 :             :      * Collect extension dependencies from indexes on the merging partitions.
   23911                 :             :      * We must do this before detaching them, so we can restore the
   23912                 :             :      * dependencies on the new partition's indexes later.
   23913                 :             :      */
   23914                 :         136 :     extDepState = collectPartitionIndexExtDeps(mergingPartitions);
   23915                 :             : 
   23916                 :             :     /* Detach all merging partitions. */
   23917   [ +  -  +  +  :         548 :     foreach_oid(mergingPartitionOid, mergingPartitions)
                   +  + ]
   23918                 :             :     {
   23919                 :             :         Relation    child_rel;
   23920                 :             : 
   23921                 :         288 :         child_rel = table_open(mergingPartitionOid, NoLock);
   23922                 :             : 
   23923                 :         288 :         detachPartitionTable(rel, child_rel, defaultPartOid);
   23924                 :             : 
   23925                 :         288 :         table_close(child_rel, NoLock);
   23926                 :             :     }
   23927                 :             : 
   23928                 :             :     /*
   23929                 :             :      * Perform a preliminary check to determine whether it's safe to drop all
   23930                 :             :      * merging partitions before we actually do so later. After merging rows
   23931                 :             :      * into the new partitions via MergePartitionsMoveRows, all old partitions
   23932                 :             :      * need to be dropped. However, since the drop behavior is DROP_RESTRICT
   23933                 :             :      * and the merge process (MergePartitionsMoveRows) can be time-consuming,
   23934                 :             :      * performing an early check on the drop eligibility of old partitions is
   23935                 :             :      * preferable.
   23936                 :             :      */
   23937   [ +  -  +  +  :         536 :     foreach_oid(mergingPartitionOid, mergingPartitions)
                   +  + ]
   23938                 :             :     {
   23939                 :             :         ObjectAddress object;
   23940                 :             : 
   23941                 :             :         /* Get oid of the later to be dropped relation. */
   23942                 :         284 :         object.objectId = mergingPartitionOid;
   23943                 :         284 :         object.classId = RelationRelationId;
   23944                 :         284 :         object.objectSubId = 0;
   23945                 :             : 
   23946                 :         284 :         performDeletionCheck(&object, DROP_RESTRICT, 0);
   23947                 :             :     }
   23948                 :             : 
   23949                 :             :     /*
   23950                 :             :      * Create a table for the new partition, using the partitioned table as a
   23951                 :             :      * model.
   23952                 :             :      */
   23953                 :             :     Assert(OidIsValid(ownerId));
   23954                 :         126 :     newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
   23955                 :             : 
   23956                 :             :     /*
   23957                 :             :      * Switch to the table owner's userid, so that any index functions are run
   23958                 :             :      * as that user.  Also, lockdown security-restricted operations and
   23959                 :             :      * arrange to make GUC variable changes local to this command.
   23960                 :             :      *
   23961                 :             :      * Need to do it after determining the namespace in the
   23962                 :             :      * createPartitionTable() call.
   23963                 :             :      */
   23964                 :         106 :     GetUserIdAndSecContext(&save_userid, &save_sec_context);
   23965                 :         106 :     SetUserIdAndSecContext(ownerId,
   23966                 :             :                            save_sec_context | SECURITY_RESTRICTED_OPERATION);
   23967                 :         106 :     save_nestlevel = NewGUCNestLevel();
   23968                 :         106 :     RestrictSearchPath();
   23969                 :             : 
   23970                 :             :     /* Copy data from merged partitions to the new partition. */
   23971                 :         106 :     MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel);
   23972                 :             : 
   23973                 :             :     /* Drop the current partitions before attaching the new one. */
   23974   [ +  -  +  +  :         452 :     foreach_oid(mergingPartitionOid, mergingPartitions)
                   +  + ]
   23975                 :             :     {
   23976                 :             :         ObjectAddress object;
   23977                 :             : 
   23978                 :         240 :         object.objectId = mergingPartitionOid;
   23979                 :         240 :         object.classId = RelationRelationId;
   23980                 :         240 :         object.objectSubId = 0;
   23981                 :             : 
   23982                 :         240 :         performDeletion(&object, DROP_RESTRICT, 0);
   23983                 :             :     }
   23984                 :             : 
   23985                 :         106 :     list_free(mergingPartitions);
   23986                 :             : 
   23987                 :             :     /*
   23988                 :             :      * Attach a new partition to the partitioned table. wqueue = NULL:
   23989                 :             :      * verification for each cloned constraint is not needed.
   23990                 :             :      */
   23991                 :         106 :     attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
   23992                 :             : 
   23993                 :             :     /*
   23994                 :             :      * Apply extension dependencies to the new partition's indexes. This
   23995                 :             :      * preserves any "DEPENDS ON EXTENSION" settings from the merged
   23996                 :             :      * partitions.
   23997                 :             :      */
   23998                 :         106 :     applyPartitionIndexExtDeps(RelationGetRelid(newPartRel), extDepState);
   23999                 :             : 
   24000                 :         106 :     freePartitionIndexExtDeps(extDepState);
   24001                 :             : 
   24002                 :             :     /* Keep the lock until commit. */
   24003                 :         106 :     table_close(newPartRel, NoLock);
   24004                 :             : 
   24005                 :             :     /* Roll back any GUC changes executed by index functions. */
   24006                 :         106 :     AtEOXact_GUC(false, save_nestlevel);
   24007                 :             : 
   24008                 :             :     /* Restore the userid and security context. */
   24009                 :         106 :     SetUserIdAndSecContext(save_userid, save_sec_context);
   24010                 :         106 : }
   24011                 :             : 
   24012                 :             : /*
   24013                 :             :  * Struct with the context of the new partition for inserting rows from the
   24014                 :             :  * split partition.
   24015                 :             :  */
   24016                 :             : typedef struct SplitPartitionContext
   24017                 :             : {
   24018                 :             :     ExprState  *partqualstate;  /* expression for checking a slot for a
   24019                 :             :                                  * partition (NULL for DEFAULT partition) */
   24020                 :             :     BulkInsertState bistate;    /* state of bulk inserts for partition */
   24021                 :             :     TupleTableSlot *dstslot;    /* slot for inserting row into partition */
   24022                 :             :     AlteredTableInfo *tab;      /* structure with generated column expressions
   24023                 :             :                                  * and check constraint expressions. */
   24024                 :             :     Relation    partRel;        /* relation for partition */
   24025                 :             : } SplitPartitionContext;
   24026                 :             : 
   24027                 :             : /*
   24028                 :             :  * createSplitPartitionContext: create context for partition and fill it
   24029                 :             :  */
   24030                 :             : static SplitPartitionContext *
   24031                 :         374 : createSplitPartitionContext(Relation partRel)
   24032                 :             : {
   24033                 :             :     SplitPartitionContext *pc;
   24034                 :             : 
   24035                 :         374 :     pc = palloc0_object(SplitPartitionContext);
   24036                 :         374 :     pc->partRel = partRel;
   24037                 :             : 
   24038                 :             :     /*
   24039                 :             :      * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
   24040                 :             :      * don't bother using it.
   24041                 :             :      */
   24042                 :         374 :     pc->bistate = GetBulkInsertState();
   24043                 :             : 
   24044                 :             :     /* Create a destination tuple slot for the new partition. */
   24045                 :         374 :     pc->dstslot = table_slot_create(pc->partRel, NULL);
   24046                 :             : 
   24047                 :         374 :     return pc;
   24048                 :             : }
   24049                 :             : 
   24050                 :             : /*
   24051                 :             :  * deleteSplitPartitionContext: delete context for partition
   24052                 :             :  */
   24053                 :             : static void
   24054                 :         374 : deleteSplitPartitionContext(SplitPartitionContext *pc, List **wqueue, uint32 ti_options)
   24055                 :             : {
   24056                 :             :     ListCell   *ltab;
   24057                 :             : 
   24058                 :         374 :     ExecDropSingleTupleTableSlot(pc->dstslot);
   24059                 :         374 :     FreeBulkInsertState(pc->bistate);
   24060                 :             : 
   24061                 :         374 :     table_finish_bulk_insert(pc->partRel, ti_options);
   24062                 :             : 
   24063                 :             :     /*
   24064                 :             :      * We don't need to process this pc->partRel so delete the ALTER TABLE
   24065                 :             :      * queue of it.
   24066                 :             :      */
   24067   [ +  -  +  -  :         748 :     foreach(ltab, *wqueue)
                   +  - ]
   24068                 :             :     {
   24069                 :         748 :         AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
   24070                 :             : 
   24071         [ +  + ]:         748 :         if (tab->relid == RelationGetRelid(pc->partRel))
   24072                 :             :         {
   24073                 :         374 :             *wqueue = list_delete_cell(*wqueue, ltab);
   24074                 :         374 :             break;
   24075                 :             :         }
   24076                 :             :     }
   24077                 :             : 
   24078                 :         374 :     pfree(pc);
   24079                 :         374 : }
   24080                 :             : 
   24081                 :             : /*
   24082                 :             :  * SplitPartitionMoveRows: scan split partition (splitRel) of partitioned table
   24083                 :             :  * (rel) and move rows into new partitions.
   24084                 :             :  *
   24085                 :             :  * New partitions description:
   24086                 :             :  * partlist: list of pointers to SinglePartitionSpec structures.  It contains
   24087                 :             :  * the partition specification details for all new partitions.
   24088                 :             :  * newPartRels: list of Relations, new partitions created in
   24089                 :             :  * ATExecSplitPartition.
   24090                 :             :  */
   24091                 :             : static void
   24092                 :         141 : SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel,
   24093                 :             :                        List *partlist, List *newPartRels)
   24094                 :             : {
   24095                 :             :     /* The FSM is empty, so don't bother using it. */
   24096                 :         141 :     uint32      ti_options = TABLE_INSERT_SKIP_FSM;
   24097                 :             :     CommandId   mycid;
   24098                 :             :     EState     *estate;
   24099                 :             :     ListCell   *listptr,
   24100                 :             :                *listptr2;
   24101                 :             :     TupleTableSlot *srcslot;
   24102                 :             :     ExprContext *econtext;
   24103                 :             :     TableScanDesc scan;
   24104                 :             :     Snapshot    snapshot;
   24105                 :             :     MemoryContext oldCxt;
   24106                 :         141 :     List       *partContexts = NIL;
   24107                 :             :     TupleConversionMap *tuple_map;
   24108                 :         141 :     SplitPartitionContext *defaultPartCtx = NULL,
   24109                 :             :                *pc;
   24110                 :             : 
   24111                 :         141 :     mycid = GetCurrentCommandId(true);
   24112                 :             : 
   24113                 :         141 :     estate = CreateExecutorState();
   24114                 :             : 
   24115   [ +  -  +  +  :         515 :     forboth(listptr, partlist, listptr2, newPartRels)
          +  -  +  +  +  
             +  +  -  +  
                      + ]
   24116                 :             :     {
   24117                 :         374 :         SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   24118                 :             : 
   24119                 :         374 :         pc = createSplitPartitionContext((Relation) lfirst(listptr2));
   24120                 :             : 
   24121                 :             :         /* Find the work queue entry for the new partition table: newPartRel. */
   24122                 :         374 :         pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
   24123                 :             : 
   24124                 :         374 :         buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
   24125                 :             : 
   24126         [ +  + ]:         374 :         if (sps->bound->is_default)
   24127                 :             :         {
   24128                 :             :             /*
   24129                 :             :              * We should not create a structure to check the partition
   24130                 :             :              * constraint for the new DEFAULT partition.
   24131                 :             :              */
   24132                 :          32 :             defaultPartCtx = pc;
   24133                 :             :         }
   24134                 :             :         else
   24135                 :             :         {
   24136                 :             :             List       *partConstraint;
   24137                 :             : 
   24138                 :             :             /* Build expression execution states for partition check quals. */
   24139                 :         342 :             partConstraint = get_qual_from_partbound(rel, sps->bound);
   24140                 :             :             partConstraint =
   24141                 :         342 :                 (List *) eval_const_expressions(NULL,
   24142                 :             :                                                 (Node *) partConstraint);
   24143                 :             :             /* Make a boolean expression for ExecCheck(). */
   24144                 :         342 :             partConstraint = list_make1(make_ands_explicit(partConstraint));
   24145                 :             : 
   24146                 :             :             /*
   24147                 :             :              * Map the vars in the constraint expression from rel's attnos to
   24148                 :             :              * splitRel's.
   24149                 :             :              */
   24150                 :         342 :             partConstraint = map_partition_varattnos(partConstraint,
   24151                 :             :                                                      1, splitRel, rel);
   24152                 :             : 
   24153                 :         342 :             pc->partqualstate =
   24154                 :         342 :                 ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
   24155                 :             :             Assert(pc->partqualstate != NULL);
   24156                 :             :         }
   24157                 :             : 
   24158                 :             :         /* Store partition context into a list. */
   24159                 :         374 :         partContexts = lappend(partContexts, pc);
   24160                 :             :     }
   24161                 :             : 
   24162         [ -  + ]:         141 :     econtext = GetPerTupleExprContext(estate);
   24163                 :             : 
   24164                 :             :     /* Create the necessary tuple slot. */
   24165                 :         141 :     srcslot = table_slot_create(splitRel, NULL);
   24166                 :             : 
   24167                 :             :     /*
   24168                 :             :      * Map computing for moving attributes of the split partition to the new
   24169                 :             :      * partition (for the first new partition, but other new partitions can
   24170                 :             :      * use the same map).
   24171                 :             :      */
   24172                 :         141 :     pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
   24173                 :         141 :     tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
   24174                 :         141 :                                        RelationGetDescr(pc->partRel));
   24175                 :             : 
   24176                 :             :     /* Scan through the rows. */
   24177                 :         141 :     snapshot = RegisterSnapshot(GetLatestSnapshot());
   24178                 :         141 :     scan = table_beginscan(splitRel, snapshot, 0, NULL,
   24179                 :             :                            SO_NONE);
   24180                 :             : 
   24181                 :             :     /*
   24182                 :             :      * Switch to per-tuple memory context and reset it for each tuple
   24183                 :             :      * produced, so we don't leak memory.
   24184                 :             :      */
   24185         [ +  - ]:         141 :     oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
   24186                 :             : 
   24187         [ +  + ]:         688 :     while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
   24188                 :             :     {
   24189                 :         547 :         bool        found = false;
   24190                 :             :         TupleTableSlot *insertslot;
   24191                 :             : 
   24192         [ -  + ]:         547 :         CHECK_FOR_INTERRUPTS();
   24193                 :             : 
   24194                 :         547 :         econtext->ecxt_scantuple = srcslot;
   24195                 :             : 
   24196                 :             :         /* Search partition for the current slot, srcslot. */
   24197   [ +  -  +  +  :        1322 :         foreach(listptr, partContexts)
                   +  + ]
   24198                 :             :         {
   24199                 :        1246 :             pc = (SplitPartitionContext *) lfirst(listptr);
   24200                 :             : 
   24201                 :             :             /* skip DEFAULT partition */
   24202   [ +  +  +  + ]:        1246 :             if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
   24203                 :             :             {
   24204                 :         471 :                 found = true;
   24205                 :         471 :                 break;
   24206                 :             :             }
   24207                 :             :         }
   24208         [ +  + ]:         547 :         if (!found)
   24209                 :             :         {
   24210                 :             :             /* Use the DEFAULT partition if it exists. */
   24211         [ +  - ]:          76 :             if (defaultPartCtx)
   24212                 :          76 :                 pc = defaultPartCtx;
   24213                 :             :             else
   24214         [ #  # ]:           0 :                 ereport(ERROR,
   24215                 :             :                         errcode(ERRCODE_CHECK_VIOLATION),
   24216                 :             :                         errmsg("cannot find partition for split partition row"),
   24217                 :             :                         errtable(splitRel));
   24218                 :             :         }
   24219                 :             : 
   24220         [ +  + ]:         547 :         if (tuple_map)
   24221                 :             :         {
   24222                 :             :             /* Need to use a map to copy attributes. */
   24223                 :          16 :             insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
   24224                 :             :         }
   24225                 :             :         else
   24226                 :             :         {
   24227                 :             :             /* Extract data from the old tuple. */
   24228                 :         531 :             slot_getallattrs(srcslot);
   24229                 :             : 
   24230                 :             :             /* Copy attributes directly. */
   24231                 :         531 :             insertslot = pc->dstslot;
   24232                 :             : 
   24233                 :         531 :             ExecClearTuple(insertslot);
   24234                 :             : 
   24235                 :         531 :             memcpy(insertslot->tts_values, srcslot->tts_values,
   24236                 :         531 :                    sizeof(Datum) * srcslot->tts_nvalid);
   24237                 :         531 :             memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
   24238                 :         531 :                    sizeof(bool) * srcslot->tts_nvalid);
   24239                 :             : 
   24240                 :         531 :             ExecStoreVirtualTuple(insertslot);
   24241                 :             :         }
   24242                 :             : 
   24243                 :             :         /*
   24244                 :             :          * Constraints and GENERATED expressions might reference the tableoid
   24245                 :             :          * column, so fill tts_tableOid with the desired value. (We must do
   24246                 :             :          * this each time, because it gets overwritten with newrel's OID
   24247                 :             :          * during storing.)
   24248                 :             :          */
   24249                 :         547 :         insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
   24250                 :             : 
   24251                 :             :         /*
   24252                 :             :          * Now, evaluate any generated expressions whose inputs come from the
   24253                 :             :          * new tuple.  We assume these columns won't reference each other, so
   24254                 :             :          * that there's no ordering dependency.
   24255                 :             :          */
   24256                 :         547 :         evaluateGeneratedExpressionsAndCheckConstraints(pc->tab, pc->partRel,
   24257                 :             :                                                         insertslot, econtext);
   24258                 :             : 
   24259                 :             :         /* Write the tuple out to the new relation. */
   24260                 :         547 :         table_tuple_insert(pc->partRel, insertslot, mycid,
   24261                 :         547 :                            ti_options, pc->bistate);
   24262                 :             : 
   24263                 :         547 :         ResetExprContext(econtext);
   24264                 :             :     }
   24265                 :             : 
   24266                 :         141 :     MemoryContextSwitchTo(oldCxt);
   24267                 :             : 
   24268                 :         141 :     table_endscan(scan);
   24269                 :         141 :     UnregisterSnapshot(snapshot);
   24270                 :             : 
   24271         [ +  + ]:         141 :     if (tuple_map)
   24272                 :           4 :         free_conversion_map(tuple_map);
   24273                 :             : 
   24274                 :         141 :     ExecDropSingleTupleTableSlot(srcslot);
   24275                 :             : 
   24276                 :         141 :     FreeExecutorState(estate);
   24277                 :             : 
   24278   [ +  -  +  +  :         656 :     foreach_ptr(SplitPartitionContext, spc, partContexts)
                   +  + ]
   24279                 :         374 :         deleteSplitPartitionContext(spc, wqueue, ti_options);
   24280                 :         141 : }
   24281                 :             : 
   24282                 :             : /*
   24283                 :             :  * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
   24284                 :             :  */
   24285                 :             : static void
   24286                 :         153 : ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
   24287                 :             :                      PartitionCmd *cmd, AlterTableUtilityContext *context)
   24288                 :             : {
   24289                 :             :     Relation    splitRel;
   24290                 :             :     Oid         splitRelOid;
   24291                 :             :     ListCell   *listptr,
   24292                 :             :                *listptr2;
   24293                 :         153 :     bool        isSameName = false;
   24294                 :             :     char        tmpRelName[NAMEDATALEN];
   24295                 :         153 :     List       *newPartRels = NIL;
   24296                 :         153 :     List       *extDepState = NIL;
   24297                 :             :     ObjectAddress object;
   24298                 :             :     Oid         defaultPartOid;
   24299                 :             :     Oid         save_userid;
   24300                 :             :     int         save_sec_context;
   24301                 :             :     int         save_nestlevel;
   24302                 :             :     List       *splitPartList;
   24303                 :             : 
   24304                 :         153 :     defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
   24305                 :             : 
   24306                 :             :     /*
   24307                 :             :      * Partition is already locked in the transformPartitionCmdForSplit
   24308                 :             :      * function.
   24309                 :             :      */
   24310                 :         153 :     splitRel = table_openrv(cmd->name, NoLock);
   24311                 :             : 
   24312                 :         153 :     splitRelOid = RelationGetRelid(splitRel);
   24313                 :             : 
   24314                 :             :     /* Check descriptions of new partitions. */
   24315   [ +  -  +  +  :         692 :     foreach_node(SinglePartitionSpec, sps, cmd->partlist)
                   +  + ]
   24316                 :             :     {
   24317                 :             :         Oid         existingRelid;
   24318                 :             : 
   24319                 :             :         /* Look up the existing relation by the new partition name. */
   24320                 :         394 :         RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, &existingRelid);
   24321                 :             : 
   24322                 :             :         /*
   24323                 :             :          * This would fail later on anyway if the relation already exists. But
   24324                 :             :          * by catching it here, we can emit a nicer error message.
   24325                 :             :          */
   24326   [ +  +  +  - ]:         394 :         if (existingRelid == splitRelOid && !isSameName)
   24327                 :             :             /* One new partition can have the same name as a split partition. */
   24328                 :          29 :             isSameName = true;
   24329         [ +  + ]:         365 :         else if (OidIsValid(existingRelid))
   24330         [ +  - ]:           4 :             ereport(ERROR,
   24331                 :             :                     errcode(ERRCODE_DUPLICATE_TABLE),
   24332                 :             :                     errmsg("relation \"%s\" already exists", sps->name->relname));
   24333                 :             :     }
   24334                 :             : 
   24335                 :             :     /*
   24336                 :             :      * Collect extension dependencies from indexes on the split partition. We
   24337                 :             :      * must do this before detaching it, so we can restore the dependencies on
   24338                 :             :      * the new partitions' indexes later.
   24339                 :             :      */
   24340                 :         149 :     splitPartList = list_make1_oid(splitRelOid);
   24341                 :             : 
   24342                 :         149 :     extDepState = collectPartitionIndexExtDeps(splitPartList);
   24343                 :         149 :     list_free(splitPartList);
   24344                 :             : 
   24345                 :             :     /* Detach the split partition. */
   24346                 :         149 :     detachPartitionTable(rel, splitRel, defaultPartOid);
   24347                 :             : 
   24348                 :             :     /*
   24349                 :             :      * Perform a preliminary check to determine whether it's safe to drop the
   24350                 :             :      * split partition before we actually do so later. After merging rows into
   24351                 :             :      * the new partitions via SplitPartitionMoveRows, all old partitions need
   24352                 :             :      * to be dropped. However, since the drop behavior is DROP_RESTRICT and
   24353                 :             :      * the merge process (SplitPartitionMoveRows) can be time-consuming,
   24354                 :             :      * performing an early check on the drop eligibility of old partitions is
   24355                 :             :      * preferable.
   24356                 :             :      */
   24357                 :         149 :     object.objectId = splitRelOid;
   24358                 :         149 :     object.classId = RelationRelationId;
   24359                 :         149 :     object.objectSubId = 0;
   24360                 :         149 :     performDeletionCheck(&object, DROP_RESTRICT, 0);
   24361                 :             : 
   24362                 :             :     /*
   24363                 :             :      * If a new partition has the same name as the split partition, then we
   24364                 :             :      * should rename the split partition to reuse its name.
   24365                 :             :      */
   24366         [ +  + ]:         149 :     if (isSameName)
   24367                 :             :     {
   24368                 :             :         /*
   24369                 :             :          * We must bump the command counter to make the split partition tuple
   24370                 :             :          * visible for renaming.
   24371                 :             :          */
   24372                 :          29 :         CommandCounterIncrement();
   24373                 :             :         /* Rename partition. */
   24374                 :          29 :         sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
   24375                 :          29 :         RenameRelationInternal(splitRelOid, tmpRelName, true, false);
   24376                 :             : 
   24377                 :             :         /*
   24378                 :             :          * We must bump the command counter to make the split partition tuple
   24379                 :             :          * visible after renaming.
   24380                 :             :          */
   24381                 :          29 :         CommandCounterIncrement();
   24382                 :             :     }
   24383                 :             : 
   24384                 :             :     /* Create new partitions (like a split partition), without indexes. */
   24385   [ +  -  +  +  :         664 :     foreach_node(SinglePartitionSpec, sps, cmd->partlist)
                   +  + ]
   24386                 :             :     {
   24387                 :             :         Relation    newPartRel;
   24388                 :             : 
   24389                 :         382 :         newPartRel = createPartitionTable(wqueue, sps->name, rel,
   24390                 :         382 :                                           splitRel->rd_rel->relowner);
   24391                 :         374 :         newPartRels = lappend(newPartRels, newPartRel);
   24392                 :             :     }
   24393                 :             : 
   24394                 :             :     /*
   24395                 :             :      * Switch to the table owner's userid, so that any index functions are run
   24396                 :             :      * as that user.  Also, lockdown security-restricted operations and
   24397                 :             :      * arrange to make GUC variable changes local to this command.
   24398                 :             :      *
   24399                 :             :      * Need to do it after determining the namespace in the
   24400                 :             :      * createPartitionTable() call.
   24401                 :             :      */
   24402                 :         141 :     GetUserIdAndSecContext(&save_userid, &save_sec_context);
   24403                 :         141 :     SetUserIdAndSecContext(splitRel->rd_rel->relowner,
   24404                 :             :                            save_sec_context | SECURITY_RESTRICTED_OPERATION);
   24405                 :         141 :     save_nestlevel = NewGUCNestLevel();
   24406                 :         141 :     RestrictSearchPath();
   24407                 :             : 
   24408                 :             :     /* Copy data from the split partition to the new partitions. */
   24409                 :         141 :     SplitPartitionMoveRows(wqueue, rel, splitRel, cmd->partlist, newPartRels);
   24410                 :             :     /* Keep the lock until commit. */
   24411                 :         141 :     table_close(splitRel, NoLock);
   24412                 :             : 
   24413                 :             :     /* Attach new partitions to the partitioned table. */
   24414   [ +  -  +  +  :         515 :     forboth(listptr, cmd->partlist, listptr2, newPartRels)
          +  -  +  +  +  
             +  +  -  +  
                      + ]
   24415                 :             :     {
   24416                 :         374 :         SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
   24417                 :         374 :         Relation    newPartRel = (Relation) lfirst(listptr2);
   24418                 :             : 
   24419                 :             :         /*
   24420                 :             :          * wqueue = NULL: verification for each cloned constraint is not
   24421                 :             :          * needed.
   24422                 :             :          */
   24423                 :         374 :         attachPartitionTable(NULL, rel, newPartRel, sps->bound);
   24424                 :             : 
   24425                 :             :         /*
   24426                 :             :          * Apply extension dependencies to the new partition's indexes. This
   24427                 :             :          * preserves any "DEPENDS ON EXTENSION" settings from the split
   24428                 :             :          * partition.
   24429                 :             :          */
   24430                 :         374 :         applyPartitionIndexExtDeps(RelationGetRelid(newPartRel), extDepState);
   24431                 :             : 
   24432                 :             :         /* Keep the lock until commit. */
   24433                 :         374 :         table_close(newPartRel, NoLock);
   24434                 :             :     }
   24435                 :             : 
   24436                 :         141 :     freePartitionIndexExtDeps(extDepState);
   24437                 :             : 
   24438                 :             :     /* Drop the split partition. */
   24439                 :         141 :     object.classId = RelationRelationId;
   24440                 :         141 :     object.objectId = splitRelOid;
   24441                 :         141 :     object.objectSubId = 0;
   24442                 :             :     /* Probably DROP_CASCADE is not needed. */
   24443                 :         141 :     performDeletion(&object, DROP_RESTRICT, 0);
   24444                 :             : 
   24445                 :             :     /* Roll back any GUC changes executed by index functions. */
   24446                 :         141 :     AtEOXact_GUC(false, save_nestlevel);
   24447                 :             : 
   24448                 :             :     /* Restore the userid and security context. */
   24449                 :         141 :     SetUserIdAndSecContext(save_userid, save_sec_context);
   24450                 :         141 : }
        

Generated by: LCOV version 2.0-1