PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
tablecmds.c File Reference
#include "postgres.h"
#include "access/attmap.h"
#include "access/genam.h"
#include "access/gist.h"
#include "access/heapam.h"
#include "access/heapam_xlog.h"
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/sysattr.h"
#include "access/tableam.h"
#include "access/toast_compression.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "access/xloginsert.h"
#include "catalog/catalog.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication_rel.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "catalog/toasting.h"
#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "commands/user.h"
#include "commands/vacuum.h"
#include "common/int.h"
#include "executor/executor.h"
#include "foreign/fdwapi.h"
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/parsenodes.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "parser/parse_utilcmd.h"
#include "parser/parser.h"
#include "partitioning/partbounds.h"
#include "partitioning/partdesc.h"
#include "pgstat.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/lock.h"
#include "storage/predicate.h"
#include "storage/smgr.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/partcache.h"
#include "utils/relcache.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
#include "utils/usercontext.h"
Include dependency graph for tablecmds.c:

Go to the source code of this file.

Data Structures

struct  OnCommitItem
 
struct  AlteredTableInfo
 
struct  NewConstraint
 
struct  NewColumnValue
 
struct  dropmsgstrings
 
struct  DropRelationCallbackState
 
struct  ForeignTruncateInfo
 
struct  AttachIndexCallbackState
 

Macros

#define AT_NUM_PASSES   (AT_PASS_MISC + 1)
 
#define ATT_TABLE   0x0001
 
#define ATT_VIEW   0x0002
 
#define ATT_MATVIEW   0x0004
 
#define ATT_INDEX   0x0008
 
#define ATT_COMPOSITE_TYPE   0x0010
 
#define ATT_FOREIGN_TABLE   0x0020
 
#define ATT_PARTITIONED_INDEX   0x0040
 
#define ATT_SEQUENCE   0x0080
 
#define ATT_PARTITIONED_TABLE   0x0100
 
#define child_dependency_type(child_is_partition)    ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
 

Typedefs

typedef struct OnCommitItem OnCommitItem
 
typedef enum AlterTablePass AlterTablePass
 
typedef struct AlteredTableInfo AlteredTableInfo
 
typedef struct NewConstraint NewConstraint
 
typedef struct NewColumnValue NewColumnValue
 
typedef struct ForeignTruncateInfo ForeignTruncateInfo
 
typedef enum addFkConstraintSides addFkConstraintSides
 

Enumerations

enum  AlterTablePass {
  AT_PASS_UNSET = -1 , AT_PASS_DROP , AT_PASS_ALTER_TYPE , AT_PASS_ADD_COL ,
  AT_PASS_SET_EXPRESSION , AT_PASS_OLD_INDEX , AT_PASS_OLD_CONSTR , AT_PASS_ADD_CONSTR ,
  AT_PASS_COL_ATTRS , AT_PASS_ADD_INDEXCONSTR , AT_PASS_ADD_INDEX , AT_PASS_ADD_OTHERCONSTR ,
  AT_PASS_MISC
}
 
enum  addFkConstraintSides { addFkReferencedSide , addFkReferencingSide , addFkBothSides }
 

Functions

static void truncate_check_rel (Oid relid, Form_pg_class reltuple)
 
static void truncate_check_perms (Oid relid, Form_pg_class reltuple)
 
static void truncate_check_activity (Relation rel)
 
static void RangeVarCallbackForTruncate (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
static ListMergeAttributes (List *columns, const List *supers, char relpersistence, bool is_partition, List **supconstr, List **supnotnulls)
 
static ListMergeCheckConstraint (List *constraints, const char *name, Node *expr, bool is_enforced)
 
static void MergeChildAttribute (List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
 
static ColumnDefMergeInheritedAttribute (List *inh_columns, int exist_attno, const ColumnDef *newdef)
 
static void MergeAttributesIntoExisting (Relation child_rel, Relation parent_rel, bool ispartition)
 
static void MergeConstraintsIntoExisting (Relation child_rel, Relation parent_rel)
 
static void StoreCatalogInheritance (Oid relationId, List *supers, bool child_is_partition)
 
static void StoreCatalogInheritance1 (Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
 
static int findAttrByName (const char *attributeName, const List *columns)
 
static void AlterIndexNamespaces (Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
 
static void AlterSeqNamespaces (Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
 
static ObjectAddress ATExecAlterConstraint (List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
 
static bool ATExecAlterConstraintInternal (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, LOCKMODE lockmode)
 
static bool ATExecAlterConstrEnforceability (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
 
static bool ATExecAlterConstrDeferrability (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
 
static bool ATExecAlterConstrInheritability (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
 
static void AlterConstrTriggerDeferrability (Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
 
static void AlterConstrEnforceabilityRecurse (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
 
static void AlterConstrDeferrabilityRecurse (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
 
static void AlterConstrUpdateConstraintEntry (ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)
 
static ObjectAddress ATExecValidateConstraint (List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
 
static void QueueFKConstraintValidation (List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
 
static void QueueCheckConstraintValidation (List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
 
static void QueueNNConstraintValidation (List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
 
static int transformColumnNameList (Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
 
static int transformFkeyGetPrimaryKey (Relation pkrel, Oid *indexOid, List **attnamelist, int16 *attnums, Oid *atttypids, Oid *attcollids, Oid *opclasses, bool *pk_has_without_overlaps)
 
static Oid transformFkeyCheckAttrs (Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
 
static void checkFkeyPermissions (Relation rel, int16 *attnums, int natts)
 
static CoercionPathType findFkeyCast (Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
 
static void validateForeignKeyConstraint (char *conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid, bool hasperiod)
 
static void CheckAlterTableIsSafe (Relation rel)
 
static void ATController (AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATPrepCmd (List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATRewriteCatalogs (List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATExecCmd (List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
 
static AlterTableCmdATParseTransformCmd (List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
 
static void ATRewriteTables (AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATRewriteTable (AlteredTableInfo *tab, Oid OIDNewHeap)
 
static AlteredTableInfoATGetQueueEntry (List **wqueue, Relation rel)
 
static void ATSimplePermissions (AlterTableType cmdtype, Relation rel, int allowed_targets)
 
static void ATSimpleRecursion (List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATCheckPartitionsNotInUse (Relation rel, LOCKMODE lockmode)
 
static void ATTypedTableRecursion (List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static Listfind_typed_table_dependencies (Oid typeOid, const char *typeName, DropBehavior behavior)
 
static void ATPrepAddColumn (List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static ObjectAddress ATExecAddColumn (List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd **cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
 
static bool check_for_column_name_collision (Relation rel, const char *colname, bool if_not_exists)
 
static void add_column_datatype_dependency (Oid relid, int32 attnum, Oid typid)
 
static void add_column_collation_dependency (Oid relid, int32 attnum, Oid collid)
 
static ObjectAddress ATExecDropNotNull (Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
 
static void set_attnotnull (List **wqueue, Relation rel, AttrNumber attnum, bool is_valid, bool queue_validation)
 
static ObjectAddress ATExecSetNotNull (List **wqueue, Relation rel, char *conName, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
 
static bool NotNullImpliedByRelConstraints (Relation rel, Form_pg_attribute attr)
 
static bool ConstraintImpliedByRelConstraint (Relation scanrel, List *testConstraint, List *provenConstraint)
 
static ObjectAddress ATExecColumnDefault (Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
 
static ObjectAddress ATExecCookedColumnDefault (Relation rel, AttrNumber attnum, Node *newDefault)
 
static ObjectAddress ATExecAddIdentity (Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
 
static ObjectAddress ATExecSetIdentity (Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
 
static ObjectAddress ATExecDropIdentity (Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
 
static ObjectAddress ATExecSetExpression (AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
 
static void ATPrepDropExpression (Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
 
static ObjectAddress ATExecDropExpression (Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
 
static ObjectAddress ATExecSetStatistics (Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
 
static ObjectAddress ATExecSetOptions (Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
 
static ObjectAddress ATExecSetStorage (Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
 
static void ATPrepDropColumn (List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static ObjectAddress ATExecDropColumn (List **wqueue, Relation rel, const char *colName, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode, ObjectAddresses *addrs)
 
static void ATPrepAddPrimaryKey (List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void verifyNotNullPKCompatible (HeapTuple tuple, const char *colname)
 
static ObjectAddress ATExecAddIndex (AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
 
static ObjectAddress ATExecAddStatistics (AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
 
static ObjectAddress ATExecAddConstraint (List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
 
static char * ChooseForeignKeyConstraintNameAddition (List *colnames)
 
static ObjectAddress ATExecAddIndexConstraint (AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
 
static ObjectAddress ATAddCheckNNConstraint (List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
 
static ObjectAddress ATAddForeignKeyConstraint (List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
 
static int validateFkOnDeleteSetColumns (int numfks, const int16 *fkattnums, int numfksetcols, int16 *fksetcolsattnums, List *fksetcols)
 
static ObjectAddress addFkConstraint (addFkConstraintSides fkside, char *constraintname, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool is_internal, bool with_period)
 
static void addFkRecurseReferenced (Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, Oid parentDelTrigger, Oid parentUpdTrigger, bool with_period)
 
static void addFkRecurseReferencing (List **wqueue, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, LOCKMODE lockmode, Oid parentInsTrigger, Oid parentUpdTrigger, bool with_period)
 
static void CloneForeignKeyConstraints (List **wqueue, Relation parentRel, Relation partitionRel)
 
static void CloneFkReferenced (Relation parentRel, Relation partitionRel)
 
static void CloneFkReferencing (List **wqueue, Relation parentRel, Relation partRel)
 
static void createForeignKeyCheckTriggers (Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
 
static void createForeignKeyActionTriggers (Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
 
static bool tryAttachPartitionForeignKey (List **wqueue, ForeignKeyCacheInfo *fk, Relation partition, Oid parentConstrOid, int numfks, AttrNumber *mapped_conkey, AttrNumber *confkey, Oid *conpfeqop, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
 
static void AttachPartitionForeignKey (List **wqueue, Relation partition, Oid partConstrOid, Oid parentConstrOid, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
 
static void RemoveInheritedConstraint (Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
 
static void DropForeignKeyConstraintTriggers (Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)
 
static void GetForeignKeyActionTriggers (Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)
 
static void GetForeignKeyCheckTriggers (Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
 
static void ATExecDropConstraint (Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
 
static ObjectAddress dropconstraint_internal (Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
 
static void ATPrepAlterColumnType (List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static bool ATColumnChangeRequiresRewrite (Node *expr, AttrNumber varattno)
 
static ObjectAddress ATExecAlterColumnType (AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode)
 
static void RememberAllDependentForRebuilding (AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
 
static void RememberConstraintForRebuilding (Oid conoid, AlteredTableInfo *tab)
 
static void RememberIndexForRebuilding (Oid indoid, AlteredTableInfo *tab)
 
static void RememberStatisticsForRebuilding (Oid stxoid, AlteredTableInfo *tab)
 
static void ATPostAlterTypeCleanup (List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
 
static void ATPostAlterTypeParse (Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
 
static void RebuildConstraintComment (AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
 
static void TryReuseIndex (Oid oldId, IndexStmt *stmt)
 
static void TryReuseForeignKey (Oid oldId, Constraint *con)
 
static ObjectAddress ATExecAlterColumnGenericOptions (Relation rel, const char *colName, List *options, LOCKMODE lockmode)
 
static void change_owner_fix_column_acls (Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
 
static void change_owner_recurse_to_sequences (Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
 
static ObjectAddress ATExecClusterOn (Relation rel, const char *indexName, LOCKMODE lockmode)
 
static void ATExecDropCluster (Relation rel, LOCKMODE lockmode)
 
static void ATPrepSetAccessMethod (AlteredTableInfo *tab, Relation rel, const char *amname)
 
static void ATExecSetAccessMethodNoStorage (Relation rel, Oid newAccessMethodId)
 
static void ATPrepChangePersistence (AlteredTableInfo *tab, Relation rel, bool toLogged)
 
static void ATPrepSetTableSpace (AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
 
static void ATExecSetTableSpace (Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 
static void ATExecSetTableSpaceNoStorage (Relation rel, Oid newTableSpace)
 
static void ATExecSetRelOptions (Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
 
static void ATExecEnableDisableTrigger (Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
 
static void ATExecEnableDisableRule (Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
 
static void ATPrepAddInherit (Relation child_rel)
 
static ObjectAddress ATExecAddInherit (Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
 
static ObjectAddress ATExecDropInherit (Relation rel, RangeVar *parent, LOCKMODE lockmode)
 
static void drop_parent_dependency (Oid relid, Oid refclassid, Oid refobjid, DependencyType deptype)
 
static ObjectAddress ATExecAddOf (Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 
static void ATExecDropOf (Relation rel, LOCKMODE lockmode)
 
static void ATExecReplicaIdentity (Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
 
static void ATExecGenericOptions (Relation rel, List *options)
 
static void ATExecSetRowSecurity (Relation rel, bool rls)
 
static void ATExecForceNoForceRowSecurity (Relation rel, bool force_rls)
 
static ObjectAddress ATExecSetCompression (Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
 
static void index_copy_data (Relation rel, RelFileLocator newrlocator)
 
static const char * storage_name (char c)
 
static void RangeVarCallbackForDropRelation (const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)
 
static void RangeVarCallbackForAlterRelation (const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
 
static PartitionSpectransformPartitionSpec (Relation rel, PartitionSpec *partspec)
 
static void ComputePartitionAttrs (ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, PartitionStrategy strategy)
 
static void CreateInheritance (Relation child_rel, Relation parent_rel, bool ispartition)
 
static void RemoveInheritance (Relation child_rel, Relation parent_rel, bool expect_detached)
 
static ObjectAddress ATExecAttachPartition (List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
 
static void AttachPartitionEnsureIndexes (List **wqueue, Relation rel, Relation attachrel)
 
static void QueuePartitionConstraintValidation (List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
 
static void CloneRowTriggersToPartition (Relation parent, Relation partition)
 
static void DetachAddConstraintIfNeeded (List **wqueue, Relation partRel)
 
static void DropClonedTriggersFromPartition (Oid partitionId)
 
static ObjectAddress ATExecDetachPartition (List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
 
static void DetachPartitionFinalize (Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
 
static ObjectAddress ATExecDetachPartitionFinalize (Relation rel, RangeVar *name)
 
static ObjectAddress ATExecAttachPartitionIdx (List **wqueue, Relation parentIdx, RangeVar *name)
 
static void validatePartitionedIndex (Relation partedIdx, Relation partedTbl)
 
static void refuseDupeIndexAttach (Relation parentIdx, Relation partIdx, Relation partitionTbl)
 
static void verifyPartitionIndexNotNull (IndexInfo *iinfo, Relation partition)
 
static ListGetParentedForeignKeyRefs (Relation partition)
 
static void ATDetachCheckNoForeignKeyRefs (Relation partition)
 
static char GetAttributeCompression (Oid atttypid, const char *compression)
 
static char GetAttributeStorage (Oid atttypid, const char *storagemode)
 
ObjectAddress DefineRelation (CreateStmt *stmt, char relkind, Oid ownerId, ObjectAddress *typaddress, const char *queryString)
 
TupleDesc BuildDescForRelation (const List *columns)
 
static void DropErrorMsgNonExistent (RangeVar *rel, char rightkind, bool missing_ok)
 
static void DropErrorMsgWrongType (const char *relname, char wrongkind, char rightkind)
 
void RemoveRelations (DropStmt *drop)
 
void ExecuteTruncate (TruncateStmt *stmt)
 
void ExecuteTruncateGuts (List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
 
void SetRelationHasSubclass (Oid relationId, bool relhassubclass)
 
bool CheckRelationTableSpaceMove (Relation rel, Oid newTableSpaceId)
 
void SetRelationTableSpace (Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
 
static void renameatt_check (Oid myrelid, Form_pg_class classform, bool recursing)
 
static AttrNumber renameatt_internal (Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
 
static void RangeVarCallbackForRenameAttribute (const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
 
ObjectAddress renameatt (RenameStmt *stmt)
 
static ObjectAddress rename_constraint_internal (Oid myrelid, Oid mytypid, const char *oldconname, const char *newconname, bool recurse, bool recursing, int expected_parents)
 
ObjectAddress RenameConstraint (RenameStmt *stmt)
 
ObjectAddress RenameRelation (RenameStmt *stmt)
 
void RenameRelationInternal (Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
 
void ResetRelRewrite (Oid myrelid)
 
void CheckTableNotInUse (Relation rel, const char *stmt)
 
Oid AlterTableLookupRelation (AlterTableStmt *stmt, LOCKMODE lockmode)
 
void AlterTable (AlterTableStmt *stmt, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
void AlterTableInternal (Oid relid, List *cmds, bool recurse)
 
LOCKMODE AlterTableGetLockLevel (List *cmds)
 
static const char * alter_table_type_to_string (AlterTableType cmdtype)
 
void find_composite_type_dependencies (Oid typeOid, Relation origRelation, const char *origTypeName)
 
void check_of_type (HeapTuple typetuple)
 
static void SetIndexStorageProperties (Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
 
static Oid CreateFKCheckTrigger (Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
 
static void RememberReplicaIdentityForRebuilding (Oid indoid, AlteredTableInfo *tab)
 
static void RememberClusterOnForRebuilding (Oid indoid, AlteredTableInfo *tab)
 
void ATExecChangeOwner (Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
 
Oid AlterTableMoveAll (AlterTableMoveAllStmt *stmt)
 
static char * decompile_conbin (HeapTuple contup, TupleDesc tupdesc)
 
static bool constraints_equivalent (HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
 
static void MarkInheritDetached (Relation child_rel, Relation parent_rel)
 
static void relation_mark_replica_identity (Relation rel, char ri_type, Oid indexOid, bool is_internal)
 
ObjectAddress AlterTableNamespace (AlterObjectSchemaStmt *stmt, Oid *oldschema)
 
void AlterTableNamespaceInternal (Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
 
void AlterRelationNamespaceInternal (Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
 
void register_on_commit_action (Oid relid, OnCommitAction action)
 
void remove_on_commit_action (Oid relid)
 
void PreCommit_on_commit_actions (void)
 
void AtEOXact_on_commit_actions (bool isCommit)
 
void AtEOSubXact_on_commit_actions (bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid)
 
void RangeVarCallbackMaintainsTable (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
void RangeVarCallbackOwnsRelation (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
bool PartConstraintImpliedByRelConstraint (Relation scanrel, List *partConstraint)
 
static void RangeVarCallbackForAttachIndex (const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
 

Variables

static Liston_commits = NIL
 
static const struct dropmsgstrings dropmsgstringarray []
 

Macro Definition Documentation

◆ AT_NUM_PASSES

#define AT_NUM_PASSES   (AT_PASS_MISC + 1)

Definition at line 166 of file tablecmds.c.

◆ ATT_COMPOSITE_TYPE

#define ATT_COMPOSITE_TYPE   0x0010

Definition at line 332 of file tablecmds.c.

◆ ATT_FOREIGN_TABLE

#define ATT_FOREIGN_TABLE   0x0020

Definition at line 333 of file tablecmds.c.

◆ ATT_INDEX

#define ATT_INDEX   0x0008

Definition at line 331 of file tablecmds.c.

◆ ATT_MATVIEW

#define ATT_MATVIEW   0x0004

Definition at line 330 of file tablecmds.c.

◆ ATT_PARTITIONED_INDEX

#define ATT_PARTITIONED_INDEX   0x0040

Definition at line 334 of file tablecmds.c.

◆ ATT_PARTITIONED_TABLE

#define ATT_PARTITIONED_TABLE   0x0100

Definition at line 336 of file tablecmds.c.

◆ ATT_SEQUENCE

#define ATT_SEQUENCE   0x0080

Definition at line 335 of file tablecmds.c.

◆ ATT_TABLE

#define ATT_TABLE   0x0001

Definition at line 328 of file tablecmds.c.

◆ ATT_VIEW

#define ATT_VIEW   0x0002

Definition at line 329 of file tablecmds.c.

◆ child_dependency_type

#define child_dependency_type (   child_is_partition)     ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)

Definition at line 365 of file tablecmds.c.

Typedef Documentation

◆ addFkConstraintSides

◆ AlteredTableInfo

◆ AlterTablePass

◆ ForeignTruncateInfo

◆ NewColumnValue

◆ NewConstraint

typedef struct NewConstraint NewConstraint

◆ OnCommitItem

typedef struct OnCommitItem OnCommitItem

Enumeration Type Documentation

◆ addFkConstraintSides

Enumerator
addFkReferencedSide 
addFkReferencingSide 
addFkBothSides 

Definition at line 353 of file tablecmds.c.

354{
addFkConstraintSides
Definition: tablecmds.c:354
@ addFkReferencingSide
Definition: tablecmds.c:356
@ addFkBothSides
Definition: tablecmds.c:357
@ addFkReferencedSide
Definition: tablecmds.c:355

◆ AlterTablePass

Enumerator
AT_PASS_UNSET 
AT_PASS_DROP 
AT_PASS_ALTER_TYPE 
AT_PASS_ADD_COL 
AT_PASS_SET_EXPRESSION 
AT_PASS_OLD_INDEX 
AT_PASS_OLD_CONSTR 
AT_PASS_ADD_CONSTR 
AT_PASS_COL_ATTRS 
AT_PASS_ADD_INDEXCONSTR 
AT_PASS_ADD_INDEX 
AT_PASS_ADD_OTHERCONSTR 
AT_PASS_MISC 

Definition at line 148 of file tablecmds.c.

149{
150 AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
151 AT_PASS_DROP, /* DROP (all flavors) */
152 AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
153 AT_PASS_ADD_COL, /* ADD COLUMN */
154 AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
155 AT_PASS_OLD_INDEX, /* re-add existing indexes */
156 AT_PASS_OLD_CONSTR, /* re-add existing constraints */
157 /* We could support a RENAME COLUMN pass here, but not currently used */
158 AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
159 AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
160 AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
161 AT_PASS_ADD_INDEX, /* ADD indexes */
162 AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
163 AT_PASS_MISC, /* other stuff */
AlterTablePass
Definition: tablecmds.c:149
@ AT_PASS_ADD_CONSTR
Definition: tablecmds.c:158
@ AT_PASS_ADD_OTHERCONSTR
Definition: tablecmds.c:162
@ AT_PASS_OLD_CONSTR
Definition: tablecmds.c:156
@ AT_PASS_ADD_COL
Definition: tablecmds.c:153
@ AT_PASS_OLD_INDEX
Definition: tablecmds.c:155
@ AT_PASS_ALTER_TYPE
Definition: tablecmds.c:152
@ AT_PASS_ADD_INDEXCONSTR
Definition: tablecmds.c:160
@ AT_PASS_DROP
Definition: tablecmds.c:151
@ AT_PASS_MISC
Definition: tablecmds.c:163
@ AT_PASS_COL_ATTRS
Definition: tablecmds.c:159
@ AT_PASS_SET_EXPRESSION
Definition: tablecmds.c:154
@ AT_PASS_ADD_INDEX
Definition: tablecmds.c:161
@ AT_PASS_UNSET
Definition: tablecmds.c:150

Function Documentation

◆ add_column_collation_dependency()

static void add_column_collation_dependency ( Oid  relid,
int32  attnum,
Oid  collid 
)
static

Definition at line 7707 of file tablecmds.c.

7708{
7709 ObjectAddress myself,
7710 referenced;
7711
7712 /* We know the default collation is pinned, so don't bother recording it */
7713 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7714 {
7715 myself.classId = RelationRelationId;
7716 myself.objectId = relid;
7717 myself.objectSubId = attnum;
7718 referenced.classId = CollationRelationId;
7719 referenced.objectId = collid;
7720 referenced.objectSubId = 0;
7721 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7722 }
7723}
#define OidIsValid(objectId)
Definition: c.h:746
Oid collid
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
int16 attnum
Definition: pg_attribute.h:74
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:45

References attnum, ObjectAddress::classId, collid, DEPENDENCY_NORMAL, ObjectAddress::objectId, ObjectAddress::objectSubId, OidIsValid, and recordDependencyOn().

Referenced by ATExecAddColumn(), and ATExecAlterColumnType().

◆ add_column_datatype_dependency()

static void add_column_datatype_dependency ( Oid  relid,
int32  attnum,
Oid  typid 
)
static

Definition at line 7689 of file tablecmds.c.

7690{
7691 ObjectAddress myself,
7692 referenced;
7693
7694 myself.classId = RelationRelationId;
7695 myself.objectId = relid;
7696 myself.objectSubId = attnum;
7697 referenced.classId = TypeRelationId;
7698 referenced.objectId = typid;
7699 referenced.objectSubId = 0;
7700 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7701}

References attnum, ObjectAddress::classId, DEPENDENCY_NORMAL, ObjectAddress::objectId, ObjectAddress::objectSubId, and recordDependencyOn().

Referenced by ATExecAddColumn(), and ATExecAlterColumnType().

◆ addFkConstraint()

static ObjectAddress addFkConstraint ( addFkConstraintSides  fkside,
char *  constraintname,
Constraint fkconstraint,
Relation  rel,
Relation  pkrel,
Oid  indexOid,
Oid  parentConstr,
int  numfks,
int16 pkattnum,
int16 fkattnum,
Oid pfeqoperators,
Oid ppeqoperators,
Oid ffeqoperators,
int  numfkdelsetcols,
int16 fkdelsetcols,
bool  is_internal,
bool  with_period 
)
static

Definition at line 10687 of file tablecmds.c.

10694{
10695 ObjectAddress address;
10696 Oid constrOid;
10697 char *conname;
10698 bool conislocal;
10699 int16 coninhcount;
10700 bool connoinherit;
10701
10702 /*
10703 * Verify relkind for each referenced partition. At the top level, this
10704 * is redundant with a previous check, but we need it when recursing.
10705 */
10706 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10707 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10708 ereport(ERROR,
10709 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10710 errmsg("referenced relation \"%s\" is not a table",
10711 RelationGetRelationName(pkrel))));
10712
10713 /*
10714 * Caller supplies us with a constraint name; however, it may be used in
10715 * this partition, so come up with a different one in that case.
10716 */
10718 RelationGetRelid(rel),
10719 constraintname))
10722 "fkey",
10724 else
10725 conname = constraintname;
10726
10727 if (fkconstraint->conname == NULL)
10728 fkconstraint->conname = pstrdup(conname);
10729
10730 if (OidIsValid(parentConstr))
10731 {
10732 conislocal = false;
10733 coninhcount = 1;
10734 connoinherit = false;
10735 }
10736 else
10737 {
10738 conislocal = true;
10739 coninhcount = 0;
10740
10741 /*
10742 * always inherit for partitioned tables, never for legacy inheritance
10743 */
10744 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10745 }
10746
10747 /*
10748 * Record the FK constraint in pg_constraint.
10749 */
10750 constrOid = CreateConstraintEntry(conname,
10752 CONSTRAINT_FOREIGN,
10753 fkconstraint->deferrable,
10754 fkconstraint->initdeferred,
10755 fkconstraint->is_enforced,
10756 fkconstraint->initially_valid,
10757 parentConstr,
10758 RelationGetRelid(rel),
10759 fkattnum,
10760 numfks,
10761 numfks,
10762 InvalidOid, /* not a domain constraint */
10763 indexOid,
10764 RelationGetRelid(pkrel),
10765 pkattnum,
10766 pfeqoperators,
10767 ppeqoperators,
10768 ffeqoperators,
10769 numfks,
10770 fkconstraint->fk_upd_action,
10771 fkconstraint->fk_del_action,
10772 fkdelsetcols,
10773 numfkdelsetcols,
10774 fkconstraint->fk_matchtype,
10775 NULL, /* no exclusion constraint */
10776 NULL, /* no check constraint */
10777 NULL,
10778 conislocal, /* islocal */
10779 coninhcount, /* inhcount */
10780 connoinherit, /* conNoInherit */
10781 with_period, /* conPeriod */
10782 is_internal); /* is_internal */
10783
10784 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10785
10786 /*
10787 * In partitioning cases, create the dependency entries for this
10788 * constraint. (For non-partitioned cases, relevant entries were created
10789 * by CreateConstraintEntry.)
10790 *
10791 * On the referenced side, we need the constraint to have an internal
10792 * dependency on its parent constraint; this means that this constraint
10793 * cannot be dropped on its own -- only through the parent constraint. It
10794 * also means the containing partition cannot be dropped on its own, but
10795 * it can be detached, at which point this dependency is removed (after
10796 * verifying that no rows are referenced via this FK.)
10797 *
10798 * When processing the referencing side, we link the constraint via the
10799 * special partitioning dependencies: the parent constraint is the primary
10800 * dependent, and the partition on which the foreign key exists is the
10801 * secondary dependency. That way, this constraint is dropped if either
10802 * of these objects is.
10803 *
10804 * Note that this is only necessary for the subsidiary pg_constraint rows
10805 * in partitions; the topmost row doesn't need any of this.
10806 */
10807 if (OidIsValid(parentConstr))
10808 {
10809 ObjectAddress referenced;
10810
10811 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10812
10813 Assert(fkside != addFkBothSides);
10814 if (fkside == addFkReferencedSide)
10815 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10816 else
10817 {
10818 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10819 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10820 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10821 }
10822 }
10823
10824 /* make new constraint visible, in case we add more */
10826
10827 return address;
10828}
int16_t int16
Definition: c.h:497
@ DEPENDENCY_INTERNAL
Definition: dependency.h:35
@ DEPENDENCY_PARTITION_PRI
Definition: dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition: dependency.h:37
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
Assert(PointerIsAligned(start, uint64))
char * pstrdup(const char *in)
Definition: mcxt.c:2321
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isEnforced, bool isValidated, Oid parentConstrId, Oid relId, const int16 *constraintKey, int constraintNKeys, int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, const int16 *fkDeleteSetCols, int numFkDeleteSetCols, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, bool conIsLocal, int16 conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
Definition: pg_constraint.c:51
bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, const char *conname)
char * ChooseConstraintName(const char *name1, const char *name2, const char *label, Oid namespaceid, List *others)
@ CONSTRAINT_RELATION
#define NIL
Definition: pg_list.h:68
#define InvalidOid
Definition: postgres_ext.h:35
unsigned int Oid
Definition: postgres_ext.h:30
#define RelationGetRelid(relation)
Definition: rel.h:516
#define RelationGetRelationName(relation)
Definition: rel.h:550
#define RelationGetNamespace(relation)
Definition: rel.h:557
bool initdeferred
Definition: parsenodes.h:2825
char fk_upd_action
Definition: parsenodes.h:2859
bool is_enforced
Definition: parsenodes.h:2826
char fk_matchtype
Definition: parsenodes.h:2858
bool initially_valid
Definition: parsenodes.h:2828
bool deferrable
Definition: parsenodes.h:2824
char * conname
Definition: parsenodes.h:2823
char fk_del_action
Definition: parsenodes.h:2860
List * fk_attrs
Definition: parsenodes.h:2854
Form_pg_class rd_rel
Definition: rel.h:111
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition: tablecmds.c:9835
void CommandCounterIncrement(void)
Definition: xact.c:1100

References addFkBothSides, addFkReferencedSide, Assert(), ChooseConstraintName(), ChooseForeignKeyConstraintNameAddition(), CommandCounterIncrement(), Constraint::conname, CONSTRAINT_RELATION, ConstraintNameIsUsed(), CreateConstraintEntry(), Constraint::deferrable, DEPENDENCY_INTERNAL, DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, ereport, errcode(), errmsg(), ERROR, Constraint::fk_attrs, Constraint::fk_del_action, Constraint::fk_matchtype, Constraint::fk_upd_action, Constraint::initdeferred, Constraint::initially_valid, InvalidOid, Constraint::is_enforced, NIL, ObjectAddressSet, OidIsValid, pstrdup(), RelationData::rd_rel, recordDependencyOn(), RelationGetNamespace, RelationGetRelationName, and RelationGetRelid.

Referenced by addFkRecurseReferenced(), addFkRecurseReferencing(), ATAddForeignKeyConstraint(), CloneFkReferenced(), and CloneFkReferencing().

◆ addFkRecurseReferenced()

static void addFkRecurseReferenced ( Constraint fkconstraint,
Relation  rel,
Relation  pkrel,
Oid  indexOid,
Oid  parentConstr,
int  numfks,
int16 pkattnum,
int16 fkattnum,
Oid pfeqoperators,
Oid ppeqoperators,
Oid ffeqoperators,
int  numfkdelsetcols,
int16 fkdelsetcols,
bool  old_check_ok,
Oid  parentDelTrigger,
Oid  parentUpdTrigger,
bool  with_period 
)
static

Definition at line 10863 of file tablecmds.c.

10872{
10873 Oid deleteTriggerOid = InvalidOid,
10874 updateTriggerOid = InvalidOid;
10875
10878
10879 /*
10880 * Create action triggers to enforce the constraint, or skip them if the
10881 * constraint is NOT ENFORCED.
10882 */
10883 if (fkconstraint->is_enforced)
10885 RelationGetRelid(pkrel),
10886 fkconstraint,
10887 parentConstr, indexOid,
10888 parentDelTrigger, parentUpdTrigger,
10889 &deleteTriggerOid, &updateTriggerOid);
10890
10891 /*
10892 * If the referenced table is partitioned, recurse on ourselves to handle
10893 * each partition. We need one pg_constraint row created for each
10894 * partition in addition to the pg_constraint row for the parent table.
10895 */
10896 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10897 {
10898 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10899
10900 for (int i = 0; i < pd->nparts; i++)
10901 {
10902 Relation partRel;
10903 AttrMap *map;
10904 AttrNumber *mapped_pkattnum;
10905 Oid partIndexId;
10906 ObjectAddress address;
10907
10908 /* XXX would it be better to acquire these locks beforehand? */
10909 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10910
10911 /*
10912 * Map the attribute numbers in the referenced side of the FK
10913 * definition to match the partition's column layout.
10914 */
10916 RelationGetDescr(pkrel),
10917 false);
10918 if (map)
10919 {
10920 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10921 for (int j = 0; j < numfks; j++)
10922 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10923 }
10924 else
10925 mapped_pkattnum = pkattnum;
10926
10927 /* Determine the index to use at this level */
10928 partIndexId = index_get_partition(partRel, indexOid);
10929 if (!OidIsValid(partIndexId))
10930 elog(ERROR, "index for %u not found in partition %s",
10931 indexOid, RelationGetRelationName(partRel));
10932
10933 /* Create entry at this level ... */
10935 fkconstraint->conname, fkconstraint, rel,
10936 partRel, partIndexId, parentConstr,
10937 numfks, mapped_pkattnum,
10938 fkattnum, pfeqoperators, ppeqoperators,
10939 ffeqoperators, numfkdelsetcols,
10940 fkdelsetcols, true, with_period);
10941 /* ... and recurse to our children */
10942 addFkRecurseReferenced(fkconstraint, rel, partRel,
10943 partIndexId, address.objectId, numfks,
10944 mapped_pkattnum, fkattnum,
10945 pfeqoperators, ppeqoperators, ffeqoperators,
10946 numfkdelsetcols, fkdelsetcols,
10947 old_check_ok,
10948 deleteTriggerOid, updateTriggerOid,
10949 with_period);
10950
10951 /* Done -- clean up (but keep the lock) */
10952 table_close(partRel, NoLock);
10953 if (map)
10954 {
10955 pfree(mapped_pkattnum);
10956 free_attrmap(map);
10957 }
10958 }
10959 }
10960}
void free_attrmap(AttrMap *map)
Definition: attmap.c:56
AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:261
int16 AttrNumber
Definition: attnum.h:21
#define elog(elevel,...)
Definition: elog.h:226
int j
Definition: isn.c:78
int i
Definition: isn.c:77
bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)
Definition: lmgr.c:334
#define NoLock
Definition: lockdefs.h:34
#define ShareRowExclusiveLock
Definition: lockdefs.h:41
void pfree(void *pointer)
Definition: mcxt.c:2146
void * palloc(Size size)
Definition: mcxt.c:1939
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:71
Oid index_get_partition(Relation partition, Oid indexId)
Definition: partition.c:176
#define RelationGetDescr(relation)
Definition: rel.h:542
Definition: attmap.h:35
AttrNumber * attnums
Definition: attmap.h:36
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13806
static ObjectAddress addFkConstraint(addFkConstraintSides fkside, char *constraintname, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool is_internal, bool with_period)
Definition: tablecmds.c:10687
static void addFkRecurseReferenced(Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, Oid parentDelTrigger, Oid parentUpdTrigger, bool with_period)
Definition: tablecmds.c:10863

References addFkConstraint(), addFkRecurseReferenced(), addFkReferencedSide, Assert(), AttrMap::attnums, build_attrmap_by_name_if_req(), CheckRelationLockedByMe(), Constraint::conname, createForeignKeyActionTriggers(), elog, ERROR, free_attrmap(), i, index_get_partition(), InvalidOid, Constraint::is_enforced, j, NoLock, PartitionDescData::nparts, ObjectAddress::objectId, OidIsValid, PartitionDescData::oids, palloc(), pfree(), RelationData::rd_rel, RelationGetDescr, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, ShareRowExclusiveLock, table_close(), and table_open().

Referenced by addFkRecurseReferenced(), ATAddForeignKeyConstraint(), CloneFkReferenced(), and DetachPartitionFinalize().

◆ addFkRecurseReferencing()

static void addFkRecurseReferencing ( List **  wqueue,
Constraint fkconstraint,
Relation  rel,
Relation  pkrel,
Oid  indexOid,
Oid  parentConstr,
int  numfks,
int16 pkattnum,
int16 fkattnum,
Oid pfeqoperators,
Oid ppeqoperators,
Oid ffeqoperators,
int  numfkdelsetcols,
int16 fkdelsetcols,
bool  old_check_ok,
LOCKMODE  lockmode,
Oid  parentInsTrigger,
Oid  parentUpdTrigger,
bool  with_period 
)
static

Definition at line 11001 of file tablecmds.c.

11009{
11010 Oid insertTriggerOid = InvalidOid,
11011 updateTriggerOid = InvalidOid;
11012
11013 Assert(OidIsValid(parentConstr));
11016
11017 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11018 ereport(ERROR,
11019 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11020 errmsg("foreign key constraints are not supported on foreign tables")));
11021
11022 /*
11023 * Add check triggers if the constraint is ENFORCED, and if needed,
11024 * schedule them to be checked in Phase 3.
11025 *
11026 * If the relation is partitioned, drill down to do it to its partitions.
11027 */
11028 if (fkconstraint->is_enforced)
11030 RelationGetRelid(pkrel),
11031 fkconstraint,
11032 parentConstr,
11033 indexOid,
11034 parentInsTrigger, parentUpdTrigger,
11035 &insertTriggerOid, &updateTriggerOid);
11036
11037 if (rel->rd_rel->relkind == RELKIND_RELATION)
11038 {
11039 /*
11040 * Tell Phase 3 to check that the constraint is satisfied by existing
11041 * rows. We can skip this during table creation, when constraint is
11042 * specified as NOT ENFORCED, or when requested explicitly by
11043 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11044 * recreating a constraint following a SET DATA TYPE operation that
11045 * did not impugn its validity.
11046 */
11047 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11048 fkconstraint->is_enforced)
11049 {
11050 NewConstraint *newcon;
11051 AlteredTableInfo *tab;
11052
11053 tab = ATGetQueueEntry(wqueue, rel);
11054
11055 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11056 newcon->name = get_constraint_name(parentConstr);
11057 newcon->contype = CONSTR_FOREIGN;
11058 newcon->refrelid = RelationGetRelid(pkrel);
11059 newcon->refindid = indexOid;
11060 newcon->conid = parentConstr;
11061 newcon->conwithperiod = fkconstraint->fk_with_period;
11062 newcon->qual = (Node *) fkconstraint;
11063
11064 tab->constraints = lappend(tab->constraints, newcon);
11065 }
11066 }
11067 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11068 {
11070 Relation trigrel;
11071
11072 /*
11073 * Triggers of the foreign keys will be manipulated a bunch of times
11074 * in the loop below. To avoid repeatedly opening/closing the trigger
11075 * catalog relation, we open it here and pass it to the subroutines
11076 * called below.
11077 */
11078 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11079
11080 /*
11081 * Recurse to take appropriate action on each partition; either we
11082 * find an existing constraint to reparent to ours, or we create a new
11083 * one.
11084 */
11085 for (int i = 0; i < pd->nparts; i++)
11086 {
11087 Relation partition = table_open(pd->oids[i], lockmode);
11088 List *partFKs;
11089 AttrMap *attmap;
11090 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11091 bool attached;
11092 ObjectAddress address;
11093
11094 CheckAlterTableIsSafe(partition);
11095
11096 attmap = build_attrmap_by_name(RelationGetDescr(partition),
11097 RelationGetDescr(rel),
11098 false);
11099 for (int j = 0; j < numfks; j++)
11100 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11101
11102 /* Check whether an existing constraint can be repurposed */
11103 partFKs = copyObject(RelationGetFKeyList(partition));
11104 attached = false;
11105 foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11106 {
11108 fk,
11109 partition,
11110 parentConstr,
11111 numfks,
11112 mapped_fkattnum,
11113 pkattnum,
11114 pfeqoperators,
11115 insertTriggerOid,
11116 updateTriggerOid,
11117 trigrel))
11118 {
11119 attached = true;
11120 break;
11121 }
11122 }
11123 if (attached)
11124 {
11125 table_close(partition, NoLock);
11126 continue;
11127 }
11128
11129 /*
11130 * No luck finding a good constraint to reuse; create our own.
11131 */
11133 fkconstraint->conname, fkconstraint,
11134 partition, pkrel, indexOid, parentConstr,
11135 numfks, pkattnum,
11136 mapped_fkattnum, pfeqoperators,
11137 ppeqoperators, ffeqoperators,
11138 numfkdelsetcols, fkdelsetcols, true,
11139 with_period);
11140
11141 /* call ourselves to finalize the creation and we're done */
11142 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11143 indexOid,
11144 address.objectId,
11145 numfks,
11146 pkattnum,
11147 mapped_fkattnum,
11148 pfeqoperators,
11149 ppeqoperators,
11150 ffeqoperators,
11151 numfkdelsetcols,
11152 fkdelsetcols,
11153 old_check_ok,
11154 lockmode,
11155 insertTriggerOid,
11156 updateTriggerOid,
11157 with_period);
11158
11159 table_close(partition, NoLock);
11160 }
11161
11162 table_close(trigrel, RowExclusiveLock);
11163 }
11164}
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:175
List * lappend(List *list, void *datum)
Definition: list.c:339
#define RowExclusiveLock
Definition: lockdefs.h:38
char * get_constraint_name(Oid conoid)
Definition: lsyscache.c:1173
void * palloc0(Size size)
Definition: mcxt.c:1969
#define copyObject(obj)
Definition: nodes.h:230
@ CONSTR_FOREIGN
Definition: parsenodes.h:2798
#define INDEX_MAX_KEYS
#define foreach_node(type, var, lst)
Definition: pg_list.h:496
List * RelationGetFKeyList(Relation relation)
Definition: relcache.c:4728
List * constraints
Definition: tablecmds.c:187
bool fk_with_period
Definition: parsenodes.h:2856
bool skip_validation
Definition: parsenodes.h:2827
Definition: pg_list.h:54
char * name
Definition: tablecmds.c:216
ConstrType contype
Definition: tablecmds.c:217
bool conwithperiod
Definition: tablecmds.c:220
Node * qual
Definition: tablecmds.c:222
Definition: nodes.h:135
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition: tablecmds.c:6552
static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, LOCKMODE lockmode, Oid parentInsTrigger, Oid parentUpdTrigger, bool with_period)
Definition: tablecmds.c:11001
static void CheckAlterTableIsSafe(Relation rel)
Definition: tablecmds.c:4439
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13941
static bool tryAttachPartitionForeignKey(List **wqueue, ForeignKeyCacheInfo *fk, Relation partition, Oid parentConstrOid, int numfks, AttrNumber *mapped_conkey, AttrNumber *confkey, Oid *conpfeqop, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11670

References addFkConstraint(), addFkRecurseReferencing(), addFkReferencingSide, Assert(), ATGetQueueEntry(), AttrMap::attnums, build_attrmap_by_name(), CheckAlterTableIsSafe(), CheckRelationLockedByMe(), NewConstraint::conid, Constraint::conname, CONSTR_FOREIGN, AlteredTableInfo::constraints, NewConstraint::contype, NewConstraint::conwithperiod, copyObject, createForeignKeyCheckTriggers(), ereport, errcode(), errmsg(), ERROR, Constraint::fk_with_period, foreach_node, get_constraint_name(), i, INDEX_MAX_KEYS, InvalidOid, Constraint::is_enforced, j, lappend(), NewConstraint::name, NoLock, PartitionDescData::nparts, ObjectAddress::objectId, OidIsValid, PartitionDescData::oids, palloc0(), NewConstraint::qual, RelationData::rd_rel, NewConstraint::refindid, NewConstraint::refrelid, RelationGetDescr, RelationGetFKeyList(), RelationGetPartitionDesc(), RelationGetRelid, RowExclusiveLock, ShareRowExclusiveLock, Constraint::skip_validation, table_close(), table_open(), and tryAttachPartitionForeignKey().

Referenced by addFkRecurseReferencing(), ATAddForeignKeyConstraint(), and CloneFkReferencing().

◆ alter_table_type_to_string()

static const char * alter_table_type_to_string ( AlterTableType  cmdtype)
static

Definition at line 6586 of file tablecmds.c.

6587{
6588 switch (cmdtype)
6589 {
6590 case AT_AddColumn:
6591 case AT_AddColumnToView:
6592 return "ADD COLUMN";
6593 case AT_ColumnDefault:
6595 return "ALTER COLUMN ... SET DEFAULT";
6596 case AT_DropNotNull:
6597 return "ALTER COLUMN ... DROP NOT NULL";
6598 case AT_SetNotNull:
6599 return "ALTER COLUMN ... SET NOT NULL";
6600 case AT_SetExpression:
6601 return "ALTER COLUMN ... SET EXPRESSION";
6602 case AT_DropExpression:
6603 return "ALTER COLUMN ... DROP EXPRESSION";
6604 case AT_SetStatistics:
6605 return "ALTER COLUMN ... SET STATISTICS";
6606 case AT_SetOptions:
6607 return "ALTER COLUMN ... SET";
6608 case AT_ResetOptions:
6609 return "ALTER COLUMN ... RESET";
6610 case AT_SetStorage:
6611 return "ALTER COLUMN ... SET STORAGE";
6612 case AT_SetCompression:
6613 return "ALTER COLUMN ... SET COMPRESSION";
6614 case AT_DropColumn:
6615 return "DROP COLUMN";
6616 case AT_AddIndex:
6617 case AT_ReAddIndex:
6618 return NULL; /* not real grammar */
6619 case AT_AddConstraint:
6620 case AT_ReAddConstraint:
6623 return "ADD CONSTRAINT";
6624 case AT_AlterConstraint:
6625 return "ALTER CONSTRAINT";
6627 return "VALIDATE CONSTRAINT";
6628 case AT_DropConstraint:
6629 return "DROP CONSTRAINT";
6630 case AT_ReAddComment:
6631 return NULL; /* not real grammar */
6632 case AT_AlterColumnType:
6633 return "ALTER COLUMN ... SET DATA TYPE";
6635 return "ALTER COLUMN ... OPTIONS";
6636 case AT_ChangeOwner:
6637 return "OWNER TO";
6638 case AT_ClusterOn:
6639 return "CLUSTER ON";
6640 case AT_DropCluster:
6641 return "SET WITHOUT CLUSTER";
6642 case AT_SetAccessMethod:
6643 return "SET ACCESS METHOD";
6644 case AT_SetLogged:
6645 return "SET LOGGED";
6646 case AT_SetUnLogged:
6647 return "SET UNLOGGED";
6648 case AT_DropOids:
6649 return "SET WITHOUT OIDS";
6650 case AT_SetTableSpace:
6651 return "SET TABLESPACE";
6652 case AT_SetRelOptions:
6653 return "SET";
6654 case AT_ResetRelOptions:
6655 return "RESET";
6657 return NULL; /* not real grammar */
6658 case AT_EnableTrig:
6659 return "ENABLE TRIGGER";
6661 return "ENABLE ALWAYS TRIGGER";
6663 return "ENABLE REPLICA TRIGGER";
6664 case AT_DisableTrig:
6665 return "DISABLE TRIGGER";
6666 case AT_EnableTrigAll:
6667 return "ENABLE TRIGGER ALL";
6668 case AT_DisableTrigAll:
6669 return "DISABLE TRIGGER ALL";
6670 case AT_EnableTrigUser:
6671 return "ENABLE TRIGGER USER";
6672 case AT_DisableTrigUser:
6673 return "DISABLE TRIGGER USER";
6674 case AT_EnableRule:
6675 return "ENABLE RULE";
6677 return "ENABLE ALWAYS RULE";
6679 return "ENABLE REPLICA RULE";
6680 case AT_DisableRule:
6681 return "DISABLE RULE";
6682 case AT_AddInherit:
6683 return "INHERIT";
6684 case AT_DropInherit:
6685 return "NO INHERIT";
6686 case AT_AddOf:
6687 return "OF";
6688 case AT_DropOf:
6689 return "NOT OF";
6690 case AT_ReplicaIdentity:
6691 return "REPLICA IDENTITY";
6693 return "ENABLE ROW SECURITY";
6695 return "DISABLE ROW SECURITY";
6697 return "FORCE ROW SECURITY";
6699 return "NO FORCE ROW SECURITY";
6700 case AT_GenericOptions:
6701 return "OPTIONS";
6702 case AT_AttachPartition:
6703 return "ATTACH PARTITION";
6704 case AT_DetachPartition:
6705 return "DETACH PARTITION";
6707 return "DETACH PARTITION ... FINALIZE";
6708 case AT_AddIdentity:
6709 return "ALTER COLUMN ... ADD IDENTITY";
6710 case AT_SetIdentity:
6711 return "ALTER COLUMN ... SET";
6712 case AT_DropIdentity:
6713 return "ALTER COLUMN ... DROP IDENTITY";
6714 case AT_ReAddStatistics:
6715 return NULL; /* not real grammar */
6716 }
6717
6718 return NULL;
6719}
@ AT_AddIndexConstraint
Definition: parsenodes.h:2430
@ AT_DropOf
Definition: parsenodes.h:2461
@ AT_SetOptions
Definition: parsenodes.h:2418
@ AT_DropIdentity
Definition: parsenodes.h:2473
@ AT_DisableTrigUser
Definition: parsenodes.h:2453
@ AT_DropNotNull
Definition: parsenodes.h:2413
@ AT_AddOf
Definition: parsenodes.h:2460
@ AT_ResetOptions
Definition: parsenodes.h:2419
@ AT_ReplicaIdentity
Definition: parsenodes.h:2462
@ AT_ReplaceRelOptions
Definition: parsenodes.h:2445
@ AT_EnableRowSecurity
Definition: parsenodes.h:2463
@ AT_AddColumnToView
Definition: parsenodes.h:2410
@ AT_ResetRelOptions
Definition: parsenodes.h:2444
@ AT_EnableReplicaTrig
Definition: parsenodes.h:2448
@ AT_DropOids
Definition: parsenodes.h:2440
@ AT_SetIdentity
Definition: parsenodes.h:2472
@ AT_ReAddStatistics
Definition: parsenodes.h:2474
@ AT_SetUnLogged
Definition: parsenodes.h:2439
@ AT_DisableTrig
Definition: parsenodes.h:2449
@ AT_SetCompression
Definition: parsenodes.h:2421
@ AT_DropExpression
Definition: parsenodes.h:2416
@ AT_AddIndex
Definition: parsenodes.h:2423
@ AT_EnableReplicaRule
Definition: parsenodes.h:2456
@ AT_ReAddIndex
Definition: parsenodes.h:2424
@ AT_DropConstraint
Definition: parsenodes.h:2431
@ AT_SetNotNull
Definition: parsenodes.h:2414
@ AT_ClusterOn
Definition: parsenodes.h:2436
@ AT_AddIdentity
Definition: parsenodes.h:2471
@ AT_ForceRowSecurity
Definition: parsenodes.h:2465
@ AT_EnableAlwaysRule
Definition: parsenodes.h:2455
@ AT_SetAccessMethod
Definition: parsenodes.h:2441
@ AT_AlterColumnType
Definition: parsenodes.h:2433
@ AT_DetachPartitionFinalize
Definition: parsenodes.h:2470
@ AT_AddInherit
Definition: parsenodes.h:2458
@ AT_ReAddDomainConstraint
Definition: parsenodes.h:2427
@ AT_EnableTrig
Definition: parsenodes.h:2446
@ AT_DropColumn
Definition: parsenodes.h:2422
@ AT_ReAddComment
Definition: parsenodes.h:2432
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:2434
@ AT_DisableTrigAll
Definition: parsenodes.h:2451
@ AT_EnableRule
Definition: parsenodes.h:2454
@ AT_NoForceRowSecurity
Definition: parsenodes.h:2466
@ AT_DetachPartition
Definition: parsenodes.h:2469
@ AT_SetStatistics
Definition: parsenodes.h:2417
@ AT_AttachPartition
Definition: parsenodes.h:2468
@ AT_AddConstraint
Definition: parsenodes.h:2425
@ AT_DropInherit
Definition: parsenodes.h:2459
@ AT_EnableAlwaysTrig
Definition: parsenodes.h:2447
@ AT_SetLogged
Definition: parsenodes.h:2438
@ AT_SetStorage
Definition: parsenodes.h:2420
@ AT_DisableRule
Definition: parsenodes.h:2457
@ AT_DisableRowSecurity
Definition: parsenodes.h:2464
@ AT_SetRelOptions
Definition: parsenodes.h:2443
@ AT_ChangeOwner
Definition: parsenodes.h:2435
@ AT_EnableTrigUser
Definition: parsenodes.h:2452
@ AT_SetExpression
Definition: parsenodes.h:2415
@ AT_ReAddConstraint
Definition: parsenodes.h:2426
@ AT_SetTableSpace
Definition: parsenodes.h:2442
@ AT_GenericOptions
Definition: parsenodes.h:2467
@ AT_ColumnDefault
Definition: parsenodes.h:2411
@ AT_CookedColumnDefault
Definition: parsenodes.h:2412
@ AT_AlterConstraint
Definition: parsenodes.h:2428
@ AT_EnableTrigAll
Definition: parsenodes.h:2450
@ AT_DropCluster
Definition: parsenodes.h:2437
@ AT_ValidateConstraint
Definition: parsenodes.h:2429
@ AT_AddColumn
Definition: parsenodes.h:2409

References AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AddInherit, AT_AddOf, AT_AlterColumnGenericOptions, AT_AlterColumnType, AT_AlterConstraint, AT_AttachPartition, AT_ChangeOwner, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_NoForceRowSecurity, AT_ReAddComment, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReAddIndex, AT_ReAddStatistics, AT_ReplaceRelOptions, AT_ReplicaIdentity, AT_ResetOptions, AT_ResetRelOptions, AT_SetAccessMethod, AT_SetCompression, AT_SetExpression, AT_SetIdentity, AT_SetLogged, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_SetUnLogged, and AT_ValidateConstraint.

Referenced by ATSimplePermissions().

◆ AlterConstrDeferrabilityRecurse()

static void AlterConstrDeferrabilityRecurse ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Relation  tgrel,
Relation  rel,
HeapTuple  contuple,
bool  recurse,
List **  otherrelids,
LOCKMODE  lockmode 
)
static

Definition at line 12782 of file tablecmds.c.

12786{
12787 Form_pg_constraint currcon;
12788 Oid conoid;
12789 ScanKeyData pkey;
12790 SysScanDesc pscan;
12791 HeapTuple childtup;
12792
12793 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12794 conoid = currcon->oid;
12795
12796 ScanKeyInit(&pkey,
12797 Anum_pg_constraint_conparentid,
12798 BTEqualStrategyNumber, F_OIDEQ,
12799 ObjectIdGetDatum(conoid));
12800
12801 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12802 true, NULL, 1, &pkey);
12803
12804 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12805 {
12806 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12807 Relation childrel;
12808
12809 childrel = table_open(childcon->conrelid, lockmode);
12810
12811 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12812 childtup, recurse, otherrelids, lockmode);
12813 table_close(childrel, NoLock);
12814 }
12815
12816 systable_endscan(pscan);
12817}
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:603
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:514
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:388
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
FormData_pg_constraint * Form_pg_constraint
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define BTEqualStrategyNumber
Definition: stratnum.h:31
static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12528

References ATExecAlterConstrDeferrability(), BTEqualStrategyNumber, GETSTRUCT(), HeapTupleIsValid, NoLock, ObjectIdGetDatum(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecAlterConstrDeferrability().

◆ AlterConstrEnforceabilityRecurse()

static void AlterConstrEnforceabilityRecurse ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Relation  tgrel,
Oid  fkrelid,
Oid  pkrelid,
HeapTuple  contuple,
LOCKMODE  lockmode,
Oid  ReferencedParentDelTrigger,
Oid  ReferencedParentUpdTrigger,
Oid  ReferencingParentInsTrigger,
Oid  ReferencingParentUpdTrigger 
)
static

Definition at line 12733 of file tablecmds.c.

12741{
12742 Form_pg_constraint currcon;
12743 Oid conoid;
12744 ScanKeyData pkey;
12745 SysScanDesc pscan;
12746 HeapTuple childtup;
12747
12748 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12749 conoid = currcon->oid;
12750
12751 ScanKeyInit(&pkey,
12752 Anum_pg_constraint_conparentid,
12753 BTEqualStrategyNumber, F_OIDEQ,
12754 ObjectIdGetDatum(conoid));
12755
12756 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12757 true, NULL, 1, &pkey);
12758
12759 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12760 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12761 pkrelid, childtup, lockmode,
12762 ReferencedParentDelTrigger,
12763 ReferencedParentUpdTrigger,
12764 ReferencingParentInsTrigger,
12765 ReferencingParentUpdTrigger);
12766
12767 systable_endscan(pscan);
12768}
static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
Definition: tablecmds.c:12385

References ATExecAlterConstrEnforceability(), BTEqualStrategyNumber, GETSTRUCT(), HeapTupleIsValid, ObjectIdGetDatum(), ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by ATExecAlterConstrEnforceability().

◆ AlterConstrTriggerDeferrability()

static void AlterConstrTriggerDeferrability ( Oid  conoid,
Relation  tgrel,
Relation  rel,
bool  deferrable,
bool  initdeferred,
List **  otherrelids 
)
static

Definition at line 12664 of file tablecmds.c.

12667{
12668 HeapTuple tgtuple;
12669 ScanKeyData tgkey;
12670 SysScanDesc tgscan;
12671
12672 ScanKeyInit(&tgkey,
12673 Anum_pg_trigger_tgconstraint,
12674 BTEqualStrategyNumber, F_OIDEQ,
12675 ObjectIdGetDatum(conoid));
12676 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12677 NULL, 1, &tgkey);
12678 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12679 {
12680 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12681 Form_pg_trigger copy_tg;
12682 HeapTuple tgCopyTuple;
12683
12684 /*
12685 * Remember OIDs of other relation(s) involved in FK constraint.
12686 * (Note: it's likely that we could skip forcing a relcache inval for
12687 * other rels that don't have a trigger whose properties change, but
12688 * let's be conservative.)
12689 */
12690 if (tgform->tgrelid != RelationGetRelid(rel))
12691 *otherrelids = list_append_unique_oid(*otherrelids,
12692 tgform->tgrelid);
12693
12694 /*
12695 * Update enable status and deferrability of RI_FKey_noaction_del,
12696 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12697 * triggers, but not others; see createForeignKeyActionTriggers and
12698 * CreateFKCheckTrigger.
12699 */
12700 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12701 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12702 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12703 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12704 continue;
12705
12706 tgCopyTuple = heap_copytuple(tgtuple);
12707 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12708
12709 copy_tg->tgdeferrable = deferrable;
12710 copy_tg->tginitdeferred = initdeferred;
12711 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12712
12713 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12714
12715 heap_freetuple(tgCopyTuple);
12716 }
12717
12718 systable_endscan(tgscan);
12719}
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:778
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
List * list_append_unique_oid(List *list, Oid datum)
Definition: list.c:1380
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:80
ItemPointerData t_self
Definition: htup.h:65

References BTEqualStrategyNumber, CatalogTupleUpdate(), GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, list_append_unique_oid(), ObjectIdGetDatum(), RelationGetRelid, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), and HeapTupleData::t_self.

Referenced by ATExecAlterConstrDeferrability().

◆ AlterConstrUpdateConstraintEntry()

static void AlterConstrUpdateConstraintEntry ( ATAlterConstraint cmdcon,
Relation  conrel,
HeapTuple  contuple 
)
static

Definition at line 12824 of file tablecmds.c.

12826{
12827 HeapTuple copyTuple;
12828 Form_pg_constraint copy_con;
12829
12830 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12831 cmdcon->alterInheritability);
12832
12833 copyTuple = heap_copytuple(contuple);
12834 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12835
12836 if (cmdcon->alterEnforceability)
12837 {
12838 copy_con->conenforced = cmdcon->is_enforced;
12839
12840 /*
12841 * NB: The convalidated status is irrelevant when the constraint is
12842 * set to NOT ENFORCED, but for consistency, it should still be set
12843 * appropriately. Similarly, if the constraint is later changed to
12844 * ENFORCED, validation will be performed during phase 3, so it makes
12845 * sense to mark it as valid in that case.
12846 */
12847 copy_con->convalidated = cmdcon->is_enforced;
12848 }
12849 if (cmdcon->alterDeferrability)
12850 {
12851 copy_con->condeferrable = cmdcon->deferrable;
12852 copy_con->condeferred = cmdcon->initdeferred;
12853 }
12854 if (cmdcon->alterInheritability)
12855 copy_con->connoinherit = cmdcon->noinherit;
12856
12857 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12858 InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12859
12860 /* Make new constraint flags visible to others */
12861 CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12862
12863 heap_freetuple(copyTuple);
12864}
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1687

References ATAlterConstraint::alterDeferrability, ATAlterConstraint::alterEnforceability, ATAlterConstraint::alterInheritability, Assert(), CacheInvalidateRelcacheByRelid(), CatalogTupleUpdate(), ATAlterConstraint::deferrable, GETSTRUCT(), heap_copytuple(), heap_freetuple(), ATAlterConstraint::initdeferred, InvokeObjectPostAlterHook, ATAlterConstraint::is_enforced, ATAlterConstraint::noinherit, and HeapTupleData::t_self.

Referenced by ATExecAlterConstrDeferrability(), ATExecAlterConstrEnforceability(), and ATExecAlterConstrInheritability().

◆ AlterIndexNamespaces()

static void AlterIndexNamespaces ( Relation  classRel,
Relation  rel,
Oid  oldNspOid,
Oid  newNspOid,
ObjectAddresses objsMoved 
)
static

Definition at line 19054 of file tablecmds.c.

19056{
19057 List *indexList;
19058 ListCell *l;
19059
19060 indexList = RelationGetIndexList(rel);
19061
19062 foreach(l, indexList)
19063 {
19064 Oid indexOid = lfirst_oid(l);
19065 ObjectAddress thisobj;
19066
19067 thisobj.classId = RelationRelationId;
19068 thisobj.objectId = indexOid;
19069 thisobj.objectSubId = 0;
19070
19071 /*
19072 * Note: currently, the index will not have its own dependency on the
19073 * namespace, so we don't need to do changeDependencyFor(). There's no
19074 * row type in pg_type, either.
19075 *
19076 * XXX this objsMoved test may be pointless -- surely we have a single
19077 * dependency link from a relation to each index?
19078 */
19079 if (!object_address_present(&thisobj, objsMoved))
19080 {
19081 AlterRelationNamespaceInternal(classRel, indexOid,
19082 oldNspOid, newNspOid,
19083 false, objsMoved);
19084 add_exact_object_address(&thisobj, objsMoved);
19085 }
19086 }
19087
19088 list_free(indexList);
19089}
bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs)
Definition: dependency.c:2608
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2548
void list_free(List *list)
Definition: list.c:1546
#define lfirst_oid(lc)
Definition: pg_list.h:174
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4833
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
Definition: tablecmds.c:18977

References add_exact_object_address(), AlterRelationNamespaceInternal(), ObjectAddress::classId, lfirst_oid, list_free(), object_address_present(), ObjectAddress::objectId, ObjectAddress::objectSubId, and RelationGetIndexList().

Referenced by AlterTableNamespaceInternal().

◆ AlterRelationNamespaceInternal()

void AlterRelationNamespaceInternal ( Relation  classRel,
Oid  relOid,
Oid  oldNspOid,
Oid  newNspOid,
bool  hasDependEntry,
ObjectAddresses objsMoved 
)

Definition at line 18977 of file tablecmds.c.

18981{
18982 HeapTuple classTup;
18983 Form_pg_class classForm;
18984 ObjectAddress thisobj;
18985 bool already_done = false;
18986
18987 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
18988 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
18989 if (!HeapTupleIsValid(classTup))
18990 elog(ERROR, "cache lookup failed for relation %u", relOid);
18991 classForm = (Form_pg_class) GETSTRUCT(classTup);
18992
18993 Assert(classForm->relnamespace == oldNspOid);
18994
18995 thisobj.classId = RelationRelationId;
18996 thisobj.objectId = relOid;
18997 thisobj.objectSubId = 0;
18998
18999 /*
19000 * If the object has already been moved, don't move it again. If it's
19001 * already in the right place, don't move it, but still fire the object
19002 * access hook.
19003 */
19004 already_done = object_address_present(&thisobj, objsMoved);
19005 if (!already_done && oldNspOid != newNspOid)
19006 {
19007 ItemPointerData otid = classTup->t_self;
19008
19009 /* check for duplicate name (more friendly than unique-index failure) */
19010 if (get_relname_relid(NameStr(classForm->relname),
19011 newNspOid) != InvalidOid)
19012 ereport(ERROR,
19013 (errcode(ERRCODE_DUPLICATE_TABLE),
19014 errmsg("relation \"%s\" already exists in schema \"%s\"",
19015 NameStr(classForm->relname),
19016 get_namespace_name(newNspOid))));
19017
19018 /* classTup is a copy, so OK to scribble on */
19019 classForm->relnamespace = newNspOid;
19020
19021 CatalogTupleUpdate(classRel, &otid, classTup);
19022 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19023
19024
19025 /* Update dependency on schema if caller said so */
19026 if (hasDependEntry &&
19027 changeDependencyFor(RelationRelationId,
19028 relOid,
19029 NamespaceRelationId,
19030 oldNspOid,
19031 newNspOid) != 1)
19032 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19033 NameStr(classForm->relname));
19034 }
19035 else
19036 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19037 if (!already_done)
19038 {
19039 add_exact_object_address(&thisobj, objsMoved);
19040
19041 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19042 }
19043
19044 heap_freetuple(classTup);
19045}
#define NameStr(name)
Definition: c.h:717
void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
Definition: lmgr.c:601
#define InplaceUpdateTupleLock
Definition: lockdefs.h:48
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3506
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:2025
FormData_pg_class * Form_pg_class
Definition: pg_class.h:156
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition: pg_depend.c:457
HeapTuple SearchSysCacheLockedCopy1(int cacheId, Datum key1)
Definition: syscache.c:404

References add_exact_object_address(), Assert(), CatalogTupleUpdate(), changeDependencyFor(), ObjectAddress::classId, elog, ereport, errcode(), errmsg(), ERROR, get_namespace_name(), get_relname_relid(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InplaceUpdateTupleLock, InvalidOid, InvokeObjectPostAlterHook, NameStr, object_address_present(), ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, SearchSysCacheLockedCopy1(), HeapTupleData::t_self, and UnlockTuple().

Referenced by AlterIndexNamespaces(), AlterSeqNamespaces(), AlterTableNamespaceInternal(), and AlterTypeNamespaceInternal().

◆ AlterSeqNamespaces()

static void AlterSeqNamespaces ( Relation  classRel,
Relation  rel,
Oid  oldNspOid,
Oid  newNspOid,
ObjectAddresses objsMoved,
LOCKMODE  lockmode 
)
static

Definition at line 19099 of file tablecmds.c.

19102{
19103 Relation depRel;
19104 SysScanDesc scan;
19105 ScanKeyData key[2];
19106 HeapTuple tup;
19107
19108 /*
19109 * SERIAL sequences are those having an auto dependency on one of the
19110 * table's columns (we don't care *which* column, exactly).
19111 */
19112 depRel = table_open(DependRelationId, AccessShareLock);
19113
19114 ScanKeyInit(&key[0],
19115 Anum_pg_depend_refclassid,
19116 BTEqualStrategyNumber, F_OIDEQ,
19117 ObjectIdGetDatum(RelationRelationId));
19118 ScanKeyInit(&key[1],
19119 Anum_pg_depend_refobjid,
19120 BTEqualStrategyNumber, F_OIDEQ,
19122 /* we leave refobjsubid unspecified */
19123
19124 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19125 NULL, 2, key);
19126
19127 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19128 {
19129 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19130 Relation seqRel;
19131
19132 /* skip dependencies other than auto dependencies on columns */
19133 if (depForm->refobjsubid == 0 ||
19134 depForm->classid != RelationRelationId ||
19135 depForm->objsubid != 0 ||
19136 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19137 continue;
19138
19139 /* Use relation_open just in case it's an index */
19140 seqRel = relation_open(depForm->objid, lockmode);
19141
19142 /* skip non-sequence relations */
19143 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19144 {
19145 /* No need to keep the lock */
19146 relation_close(seqRel, lockmode);
19147 continue;
19148 }
19149
19150 /* Fix the pg_class and pg_depend entries */
19151 AlterRelationNamespaceInternal(classRel, depForm->objid,
19152 oldNspOid, newNspOid,
19153 true, objsMoved);
19154
19155 /*
19156 * Sequences used to have entries in pg_type, but no longer do. If we
19157 * ever re-instate that, we'll need to move the pg_type entry to the
19158 * new namespace, too (using AlterTypeNamespaceInternal).
19159 */
19160 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19161
19162 /* Now we can close it. Keep the lock till end of transaction. */
19163 relation_close(seqRel, NoLock);
19164 }
19165
19166 systable_endscan(scan);
19167
19169}
@ DEPENDENCY_AUTO
Definition: dependency.h:34
#define AccessShareLock
Definition: lockdefs.h:36
FormData_pg_depend * Form_pg_depend
Definition: pg_depend.h:72
#define RelationGetForm(relation)
Definition: rel.h:510
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47

References AccessShareLock, AlterRelationNamespaceInternal(), Assert(), BTEqualStrategyNumber, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, GETSTRUCT(), HeapTupleIsValid, InvalidOid, sort-test::key, NoLock, ObjectIdGetDatum(), relation_close(), relation_open(), RelationGetForm, RelationGetRelid, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), and table_open().

Referenced by AlterTableNamespaceInternal().

◆ AlterTable()

void AlterTable ( AlterTableStmt stmt,
LOCKMODE  lockmode,
AlterTableUtilityContext context 
)

Definition at line 4524 of file tablecmds.c.

4526{
4527 Relation rel;
4528
4529 /* Caller is required to provide an adequate lock. */
4530 rel = relation_open(context->relid, NoLock);
4531
4533
4534 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4535}
#define stmt
Definition: indent_codes.h:59
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4860

References ATController(), CheckAlterTableIsSafe(), NoLock, relation_open(), AlterTableUtilityContext::relid, and stmt.

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4598 of file tablecmds.c.

4599{
4600 /*
4601 * This only works if we read catalog tables using MVCC snapshots.
4602 */
4603 ListCell *lcmd;
4605
4606 foreach(lcmd, cmds)
4607 {
4608 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4609 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4610
4611 switch (cmd->subtype)
4612 {
4613 /*
4614 * These subcommands rewrite the heap, so require full locks.
4615 */
4616 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4617 * to SELECT */
4618 case AT_SetAccessMethod: /* must rewrite heap */
4619 case AT_SetTableSpace: /* must rewrite heap */
4620 case AT_AlterColumnType: /* must rewrite heap */
4621 cmd_lockmode = AccessExclusiveLock;
4622 break;
4623
4624 /*
4625 * These subcommands may require addition of toast tables. If
4626 * we add a toast table to a table currently being scanned, we
4627 * might miss data added to the new toast table by concurrent
4628 * insert transactions.
4629 */
4630 case AT_SetStorage: /* may add toast tables, see
4631 * ATRewriteCatalogs() */
4632 cmd_lockmode = AccessExclusiveLock;
4633 break;
4634
4635 /*
4636 * Removing constraints can affect SELECTs that have been
4637 * optimized assuming the constraint holds true. See also
4638 * CloneFkReferenced.
4639 */
4640 case AT_DropConstraint: /* as DROP INDEX */
4641 case AT_DropNotNull: /* may change some SQL plans */
4642 cmd_lockmode = AccessExclusiveLock;
4643 break;
4644
4645 /*
4646 * Subcommands that may be visible to concurrent SELECTs
4647 */
4648 case AT_DropColumn: /* change visible to SELECT */
4649 case AT_AddColumnToView: /* CREATE VIEW */
4650 case AT_DropOids: /* used to equiv to DropColumn */
4651 case AT_EnableAlwaysRule: /* may change SELECT rules */
4652 case AT_EnableReplicaRule: /* may change SELECT rules */
4653 case AT_EnableRule: /* may change SELECT rules */
4654 case AT_DisableRule: /* may change SELECT rules */
4655 cmd_lockmode = AccessExclusiveLock;
4656 break;
4657
4658 /*
4659 * Changing owner may remove implicit SELECT privileges
4660 */
4661 case AT_ChangeOwner: /* change visible to SELECT */
4662 cmd_lockmode = AccessExclusiveLock;
4663 break;
4664
4665 /*
4666 * Changing foreign table options may affect optimization.
4667 */
4668 case AT_GenericOptions:
4670 cmd_lockmode = AccessExclusiveLock;
4671 break;
4672
4673 /*
4674 * These subcommands affect write operations only.
4675 */
4676 case AT_EnableTrig:
4679 case AT_EnableTrigAll:
4680 case AT_EnableTrigUser:
4681 case AT_DisableTrig:
4682 case AT_DisableTrigAll:
4683 case AT_DisableTrigUser:
4684 cmd_lockmode = ShareRowExclusiveLock;
4685 break;
4686
4687 /*
4688 * These subcommands affect write operations only. XXX
4689 * Theoretically, these could be ShareRowExclusiveLock.
4690 */
4691 case AT_ColumnDefault:
4693 case AT_AlterConstraint:
4694 case AT_AddIndex: /* from ADD CONSTRAINT */
4696 case AT_ReplicaIdentity:
4697 case AT_SetNotNull:
4702 case AT_AddIdentity:
4703 case AT_DropIdentity:
4704 case AT_SetIdentity:
4705 case AT_SetExpression:
4706 case AT_DropExpression:
4707 case AT_SetCompression:
4708 cmd_lockmode = AccessExclusiveLock;
4709 break;
4710
4711 case AT_AddConstraint:
4712 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4713 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4714 if (IsA(cmd->def, Constraint))
4715 {
4716 Constraint *con = (Constraint *) cmd->def;
4717
4718 switch (con->contype)
4719 {
4720 case CONSTR_EXCLUSION:
4721 case CONSTR_PRIMARY:
4722 case CONSTR_UNIQUE:
4723
4724 /*
4725 * Cases essentially the same as CREATE INDEX. We
4726 * could reduce the lock strength to ShareLock if
4727 * we can work out how to allow concurrent catalog
4728 * updates. XXX Might be set down to
4729 * ShareRowExclusiveLock but requires further
4730 * analysis.
4731 */
4732 cmd_lockmode = AccessExclusiveLock;
4733 break;
4734 case CONSTR_FOREIGN:
4735
4736 /*
4737 * We add triggers to both tables when we add a
4738 * Foreign Key, so the lock level must be at least
4739 * as strong as CREATE TRIGGER.
4740 */
4741 cmd_lockmode = ShareRowExclusiveLock;
4742 break;
4743
4744 default:
4745 cmd_lockmode = AccessExclusiveLock;
4746 }
4747 }
4748 break;
4749
4750 /*
4751 * These subcommands affect inheritance behaviour. Queries
4752 * started before us will continue to see the old inheritance
4753 * behaviour, while queries started after we commit will see
4754 * new behaviour. No need to prevent reads or writes to the
4755 * subtable while we hook it up though. Changing the TupDesc
4756 * may be a problem, so keep highest lock.
4757 */
4758 case AT_AddInherit:
4759 case AT_DropInherit:
4760 cmd_lockmode = AccessExclusiveLock;
4761 break;
4762
4763 /*
4764 * These subcommands affect implicit row type conversion. They
4765 * have affects similar to CREATE/DROP CAST on queries. don't
4766 * provide for invalidating parse trees as a result of such
4767 * changes, so we keep these at AccessExclusiveLock.
4768 */
4769 case AT_AddOf:
4770 case AT_DropOf:
4771 cmd_lockmode = AccessExclusiveLock;
4772 break;
4773
4774 /*
4775 * Only used by CREATE OR REPLACE VIEW which must conflict
4776 * with an SELECTs currently using the view.
4777 */
4779 cmd_lockmode = AccessExclusiveLock;
4780 break;
4781
4782 /*
4783 * These subcommands affect general strategies for performance
4784 * and maintenance, though don't change the semantic results
4785 * from normal data reads and writes. Delaying an ALTER TABLE
4786 * behind currently active writes only delays the point where
4787 * the new strategy begins to take effect, so there is no
4788 * benefit in waiting. In this case the minimum restriction
4789 * applies: we don't currently allow concurrent catalog
4790 * updates.
4791 */
4792 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4793 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4794 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4795 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4796 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4797 cmd_lockmode = ShareUpdateExclusiveLock;
4798 break;
4799
4800 case AT_SetLogged:
4801 case AT_SetUnLogged:
4802 cmd_lockmode = AccessExclusiveLock;
4803 break;
4804
4805 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4806 cmd_lockmode = ShareUpdateExclusiveLock;
4807 break;
4808
4809 /*
4810 * Rel options are more complex than first appears. Options
4811 * are set here for tables, views and indexes; for historical
4812 * reasons these can all be used with ALTER TABLE, so we can't
4813 * decide between them using the basic grammar.
4814 */
4815 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4816 * getTables() */
4817 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4818 * getTables() */
4819 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4820 break;
4821
4822 case AT_AttachPartition:
4823 cmd_lockmode = ShareUpdateExclusiveLock;
4824 break;
4825
4826 case AT_DetachPartition:
4827 if (((PartitionCmd *) cmd->def)->concurrent)
4828 cmd_lockmode = ShareUpdateExclusiveLock;
4829 else
4830 cmd_lockmode = AccessExclusiveLock;
4831 break;
4832
4834 cmd_lockmode = ShareUpdateExclusiveLock;
4835 break;
4836
4837 default: /* oops */
4838 elog(ERROR, "unrecognized alter table type: %d",
4839 (int) cmd->subtype);
4840 break;
4841 }
4842
4843 /*
4844 * Take the greatest lockmode from any subcommand
4845 */
4846 if (cmd_lockmode > lockmode)
4847 lockmode = cmd_lockmode;
4848 }
4849
4850 return lockmode;
4851}
int LOCKMODE
Definition: lockdefs.h:26
#define AccessExclusiveLock
Definition: lockdefs.h:43
#define ShareUpdateExclusiveLock
Definition: lockdefs.h:39
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
@ CONSTR_UNIQUE
Definition: parsenodes.h:2796
@ CONSTR_EXCLUSION
Definition: parsenodes.h:2797
@ CONSTR_PRIMARY
Definition: parsenodes.h:2795
#define lfirst(lc)
Definition: pg_list.h:172
LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList)
Definition: reloptions.c:2135
AlterTableType subtype
Definition: parsenodes.h:2480
ConstrType contype
Definition: parsenodes.h:2822

References AccessExclusiveLock, AlterTableGetRelOptionsLockLevel(), AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AddInherit, AT_AddOf, AT_AlterColumnGenericOptions, AT_AlterColumnType, AT_AlterConstraint, AT_AttachPartition, AT_ChangeOwner, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_NoForceRowSecurity, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReplaceRelOptions, AT_ReplicaIdentity, AT_ResetOptions, AT_ResetRelOptions, AT_SetAccessMethod, AT_SetCompression, AT_SetExpression, AT_SetIdentity, AT_SetLogged, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_SetUnLogged, AT_ValidateConstraint, CONSTR_EXCLUSION, CONSTR_FOREIGN, CONSTR_PRIMARY, CONSTR_UNIQUE, Constraint::contype, AlterTableCmd::def, elog, ERROR, IsA, lfirst, ShareRowExclusiveLock, ShareUpdateExclusiveLock, and AlterTableCmd::subtype.

Referenced by AlterTableInternal(), and ProcessUtilitySlow().

◆ AlterTableInternal()

void AlterTableInternal ( Oid  relid,
List cmds,
bool  recurse 
)

Definition at line 4553 of file tablecmds.c.

4554{
4555 Relation rel;
4556 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4557
4558 rel = relation_open(relid, lockmode);
4559
4561
4562 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4563}
void EventTriggerAlterTableRelid(Oid objectId)
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition: tablecmds.c:4598

References AlterTableGetLockLevel(), ATController(), EventTriggerAlterTableRelid(), and relation_open().

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4465 of file tablecmds.c.

4466{
4467 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4468 stmt->missing_ok ? RVR_MISSING_OK : 0,
4470 stmt);
4471}
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:441
@ RVR_MISSING_OK
Definition: namespace.h:72
static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:19509

References RangeVarCallbackForAlterRelation(), RangeVarGetRelidExtended(), RVR_MISSING_OK, and stmt.

Referenced by ProcessUtilitySlow().

◆ AlterTableMoveAll()

Oid AlterTableMoveAll ( AlterTableMoveAllStmt stmt)

Definition at line 16908 of file tablecmds.c.

16909{
16910 List *relations = NIL;
16911 ListCell *l;
16912 ScanKeyData key[1];
16913 Relation rel;
16914 TableScanDesc scan;
16915 HeapTuple tuple;
16916 Oid orig_tablespaceoid;
16917 Oid new_tablespaceoid;
16918 List *role_oids = roleSpecsToIds(stmt->roles);
16919
16920 /* Ensure we were not asked to move something we can't */
16921 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16922 stmt->objtype != OBJECT_MATVIEW)
16923 ereport(ERROR,
16924 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16925 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16926
16927 /* Get the orig and new tablespace OIDs */
16928 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16929 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16930
16931 /* Can't move shared relations in to or out of pg_global */
16932 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16933 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16934 new_tablespaceoid == GLOBALTABLESPACE_OID)
16935 ereport(ERROR,
16936 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16937 errmsg("cannot move relations in to or out of pg_global tablespace")));
16938
16939 /*
16940 * Must have CREATE rights on the new tablespace, unless it is the
16941 * database default tablespace (which all users implicitly have CREATE
16942 * rights on).
16943 */
16944 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16945 {
16946 AclResult aclresult;
16947
16948 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16949 ACL_CREATE);
16950 if (aclresult != ACLCHECK_OK)
16952 get_tablespace_name(new_tablespaceoid));
16953 }
16954
16955 /*
16956 * Now that the checks are done, check if we should set either to
16957 * InvalidOid because it is our database's default tablespace.
16958 */
16959 if (orig_tablespaceoid == MyDatabaseTableSpace)
16960 orig_tablespaceoid = InvalidOid;
16961
16962 if (new_tablespaceoid == MyDatabaseTableSpace)
16963 new_tablespaceoid = InvalidOid;
16964
16965 /* no-op */
16966 if (orig_tablespaceoid == new_tablespaceoid)
16967 return new_tablespaceoid;
16968
16969 /*
16970 * Walk the list of objects in the tablespace and move them. This will
16971 * only find objects in our database, of course.
16972 */
16973 ScanKeyInit(&key[0],
16974 Anum_pg_class_reltablespace,
16975 BTEqualStrategyNumber, F_OIDEQ,
16976 ObjectIdGetDatum(orig_tablespaceoid));
16977
16978 rel = table_open(RelationRelationId, AccessShareLock);
16979 scan = table_beginscan_catalog(rel, 1, key);
16980 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
16981 {
16982 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
16983 Oid relOid = relForm->oid;
16984
16985 /*
16986 * Do not move objects in pg_catalog as part of this, if an admin
16987 * really wishes to do so, they can issue the individual ALTER
16988 * commands directly.
16989 *
16990 * Also, explicitly avoid any shared tables, temp tables, or TOAST
16991 * (TOAST will be moved with the main table).
16992 */
16993 if (IsCatalogNamespace(relForm->relnamespace) ||
16994 relForm->relisshared ||
16995 isAnyTempNamespace(relForm->relnamespace) ||
16996 IsToastNamespace(relForm->relnamespace))
16997 continue;
16998
16999 /* Only move the object type requested */
17000 if ((stmt->objtype == OBJECT_TABLE &&
17001 relForm->relkind != RELKIND_RELATION &&
17002 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17003 (stmt->objtype == OBJECT_INDEX &&
17004 relForm->relkind != RELKIND_INDEX &&
17005 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17006 (stmt->objtype == OBJECT_MATVIEW &&
17007 relForm->relkind != RELKIND_MATVIEW))
17008 continue;
17009
17010 /* Check if we are only moving objects owned by certain roles */
17011 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17012 continue;
17013
17014 /*
17015 * Handle permissions-checking here since we are locking the tables
17016 * and also to avoid doing a bunch of work only to fail part-way. Note
17017 * that permissions will also be checked by AlterTableInternal().
17018 *
17019 * Caller must be considered an owner on the table to move it.
17020 */
17021 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17023 NameStr(relForm->relname));
17024
17025 if (stmt->nowait &&
17027 ereport(ERROR,
17028 (errcode(ERRCODE_OBJECT_IN_USE),
17029 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17030 get_namespace_name(relForm->relnamespace),
17031 NameStr(relForm->relname))));
17032 else
17034
17035 /* Add to our list of objects to move */
17036 relations = lappend_oid(relations, relOid);
17037 }
17038
17039 table_endscan(scan);
17041
17042 if (relations == NIL)
17044 (errcode(ERRCODE_NO_DATA_FOUND),
17045 errmsg("no matching relations in tablespace \"%s\" found",
17046 orig_tablespaceoid == InvalidOid ? "(database default)" :
17047 get_tablespace_name(orig_tablespaceoid))));
17048
17049 /* Everything is locked, loop through and move all of the relations. */
17050 foreach(l, relations)
17051 {
17052 List *cmds = NIL;
17054
17056 cmd->name = stmt->new_tablespacename;
17057
17058 cmds = lappend(cmds, cmd);
17059
17061 /* OID is set by AlterTableInternal */
17062 AlterTableInternal(lfirst_oid(l), cmds, false);
17064 }
17065
17066 return new_tablespaceoid;
17067}
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
@ ACLCHECK_NOT_OWNER
Definition: acl.h:185
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2639
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3821
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4075
char * get_tablespace_name(Oid spc_oid)
Definition: tablespace.c:1472
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
Definition: tablespace.c:1426
bool IsToastNamespace(Oid namespaceId)
Definition: catalog.c:261
bool IsCatalogNamespace(Oid namespaceId)
Definition: catalog.c:243
#define NOTICE
Definition: elog.h:35
void EventTriggerAlterTableStart(Node *parsetree)
void EventTriggerAlterTableEnd(void)
Oid MyDatabaseTableSpace
Definition: globals.c:97
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1314
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:722
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:151
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:107
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2143
Oid GetUserId(void)
Definition: miscinit.c:520
bool isAnyTempNamespace(Oid namespaceId)
Definition: namespace.c:3687
#define makeNode(_type_)
Definition: nodes.h:161
ObjectType get_relkind_objtype(char relkind)
@ OBJECT_MATVIEW
Definition: parsenodes.h:2340
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2359
@ OBJECT_INDEX
Definition: parsenodes.h:2337
@ OBJECT_TABLE
Definition: parsenodes.h:2358
#define ACL_CREATE
Definition: parsenodes.h:85
@ ForwardScanDirection
Definition: sdir.h:28
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:113
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:979
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition: tablecmds.c:4553
List * roleSpecsToIds(List *memberNames)
Definition: user.c:1652

References AccessExclusiveLock, AccessShareLock, ACL_CREATE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, AlterTableInternal(), AT_SetTableSpace, BTEqualStrategyNumber, ConditionalLockRelationOid(), ereport, errcode(), errmsg(), ERROR, EventTriggerAlterTableEnd(), EventTriggerAlterTableStart(), ForwardScanDirection, get_namespace_name(), get_rel_relkind(), get_relkind_objtype(), get_tablespace_name(), get_tablespace_oid(), GETSTRUCT(), GetUserId(), heap_getnext(), InvalidOid, isAnyTempNamespace(), IsCatalogNamespace(), IsToastNamespace(), sort-test::key, lappend(), lappend_oid(), lfirst_oid, list_member_oid(), LockRelationOid(), makeNode, MyDatabaseTableSpace, AlterTableCmd::name, NameStr, NIL, NOTICE, object_aclcheck(), OBJECT_INDEX, OBJECT_MATVIEW, object_ownercheck(), OBJECT_TABLE, OBJECT_TABLESPACE, ObjectIdGetDatum(), OidIsValid, roleSpecsToIds(), ScanKeyInit(), stmt, AlterTableCmd::subtype, table_beginscan_catalog(), table_close(), table_endscan(), and table_open().

Referenced by ProcessUtilitySlow().

◆ AlterTableNamespace()

ObjectAddress AlterTableNamespace ( AlterObjectSchemaStmt stmt,
Oid oldschema 
)

Definition at line 18869 of file tablecmds.c.

18870{
18871 Relation rel;
18872 Oid relid;
18873 Oid oldNspOid;
18874 Oid nspOid;
18875 RangeVar *newrv;
18876 ObjectAddresses *objsMoved;
18877 ObjectAddress myself;
18878
18880 stmt->missing_ok ? RVR_MISSING_OK : 0,
18882 stmt);
18883
18884 if (!OidIsValid(relid))
18885 {
18887 (errmsg("relation \"%s\" does not exist, skipping",
18888 stmt->relation->relname)));
18889 return InvalidObjectAddress;
18890 }
18891
18892 rel = relation_open(relid, NoLock);
18893
18894 oldNspOid = RelationGetNamespace(rel);
18895
18896 /* If it's an owned sequence, disallow moving it by itself. */
18897 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18898 {
18899 Oid tableId;
18900 int32 colId;
18901
18902 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18903 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18904 ereport(ERROR,
18905 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18906 errmsg("cannot move an owned sequence into another schema"),
18907 errdetail("Sequence \"%s\" is linked to table \"%s\".",
18909 get_rel_name(tableId))));
18910 }
18911
18912 /* Get and lock schema OID and check its permissions. */
18913 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18914 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18915
18916 /* common checks on switching namespaces */
18917 CheckSetNamespace(oldNspOid, nspOid);
18918
18919 objsMoved = new_object_addresses();
18920 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18921 free_object_addresses(objsMoved);
18922
18923 ObjectAddressSet(myself, RelationRelationId, relid);
18924
18925 if (oldschema)
18926 *oldschema = oldNspOid;
18927
18928 /* close rel, but keep lock until commit */
18929 relation_close(rel, NoLock);
18930
18931 return myself;
18932}
int32_t int32
Definition: c.h:498
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2502
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2788
int errdetail(const char *fmt,...)
Definition: elog.c:1204
char * get_rel_name(Oid relid)
Definition: lsyscache.c:2068
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:473
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition: namespace.c:739
void CheckSetNamespace(Oid oldNspOid, Oid nspOid)
Definition: namespace.c:3459
const ObjectAddress InvalidObjectAddress
bool sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
Definition: pg_depend.c:828
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:18940

References AccessExclusiveLock, AlterTableNamespaceInternal(), CheckSetNamespace(), DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, ereport, errcode(), errdetail(), errmsg(), ERROR, free_object_addresses(), get_rel_name(), InvalidObjectAddress, makeRangeVar(), new_object_addresses(), NoLock, NOTICE, ObjectAddressSet, OidIsValid, RangeVarCallbackForAlterRelation(), RangeVarGetAndCheckCreationNamespace(), RangeVarGetRelidExtended(), RelationData::rd_rel, relation_close(), relation_open(), RelationGetNamespace, RelationGetRelationName, RVR_MISSING_OK, sequenceIsOwned(), and stmt.

Referenced by ExecAlterObjectSchemaStmt().

◆ AlterTableNamespaceInternal()

void AlterTableNamespaceInternal ( Relation  rel,
Oid  oldNspOid,
Oid  nspOid,
ObjectAddresses objsMoved 
)

Definition at line 18940 of file tablecmds.c.

18942{
18943 Relation classRel;
18944
18945 Assert(objsMoved != NULL);
18946
18947 /* OK, modify the pg_class row and pg_depend entry */
18948 classRel = table_open(RelationRelationId, RowExclusiveLock);
18949
18950 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18951 nspOid, true, objsMoved);
18952
18953 /* Fix the table's row type too, if it has one */
18954 if (OidIsValid(rel->rd_rel->reltype))
18955 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18956 false, /* isImplicitArray */
18957 false, /* ignoreDependent */
18958 false, /* errorOnTableType */
18959 objsMoved);
18960
18961 /* Fix other dependent stuff */
18962 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
18963 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
18964 objsMoved, AccessExclusiveLock);
18965 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
18966 false, objsMoved);
18967
18968 table_close(classRel, RowExclusiveLock);
18969}
void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, Oid newNspId, bool isType, ObjectAddresses *objsMoved)
static void AlterSeqNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
Definition: tablecmds.c:19099
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19054
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition: typecmds.c:4147

References AccessExclusiveLock, AlterConstraintNamespaces(), AlterIndexNamespaces(), AlterRelationNamespaceInternal(), AlterSeqNamespaces(), AlterTypeNamespaceInternal(), Assert(), OidIsValid, RelationData::rd_rel, RelationGetRelid, RowExclusiveLock, table_close(), and table_open().

Referenced by AlterObjectNamespace_oid(), and AlterTableNamespace().

◆ ATAddCheckNNConstraint()

static ObjectAddress ATAddCheckNNConstraint ( List **  wqueue,
AlteredTableInfo tab,
Relation  rel,
Constraint constr,
bool  recurse,
bool  recursing,
bool  is_readd,
LOCKMODE  lockmode 
)
static

Definition at line 9877 of file tablecmds.c.

9880{
9881 List *newcons;
9882 ListCell *lcon;
9883 List *children;
9884 ListCell *child;
9886
9887 /* Guard against stack overflow due to overly deep inheritance tree. */
9889
9890 /* At top level, permission check was done in ATPrepCmd, else do it */
9891 if (recursing)
9894
9895 /*
9896 * Call AddRelationNewConstraints to do the work, making sure it works on
9897 * a copy of the Constraint so transformExpr can't modify the original. It
9898 * returns a list of cooked constraints.
9899 *
9900 * If the constraint ends up getting merged with a pre-existing one, it's
9901 * omitted from the returned list, which is what we want: we do not need
9902 * to do any validation work. That can only happen at child tables,
9903 * though, since we disallow merging at the top level.
9904 */
9905 newcons = AddRelationNewConstraints(rel, NIL,
9906 list_make1(copyObject(constr)),
9907 recursing || is_readd, /* allow_merge */
9908 !recursing, /* is_local */
9909 is_readd, /* is_internal */
9910 NULL); /* queryString not available
9911 * here */
9912
9913 /* we don't expect more than one constraint here */
9914 Assert(list_length(newcons) <= 1);
9915
9916 /* Add each to-be-validated constraint to Phase 3's queue */
9917 foreach(lcon, newcons)
9918 {
9919 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9920
9921 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9922 {
9923 NewConstraint *newcon;
9924
9925 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9926 newcon->name = ccon->name;
9927 newcon->contype = ccon->contype;
9928 newcon->qual = ccon->expr;
9929
9930 tab->constraints = lappend(tab->constraints, newcon);
9931 }
9932
9933 /* Save the actually assigned name if it was defaulted */
9934 if (constr->conname == NULL)
9935 constr->conname = ccon->name;
9936
9937 /*
9938 * If adding a valid not-null constraint, set the pg_attribute flag
9939 * and tell phase 3 to verify existing rows, if needed. For an
9940 * invalid constraint, just set attnotnull, without queueing
9941 * verification.
9942 */
9943 if (constr->contype == CONSTR_NOTNULL)
9944 set_attnotnull(wqueue, rel, ccon->attnum,
9945 !constr->skip_validation,
9946 !constr->skip_validation);
9947
9948 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9949 }
9950
9951 /* At this point we must have a locked-down name to use */
9952 Assert(newcons == NIL || constr->conname != NULL);
9953
9954 /* Advance command counter in case same table is visited multiple times */
9956
9957 /*
9958 * If the constraint got merged with an existing constraint, we're done.
9959 * We mustn't recurse to child tables in this case, because they've
9960 * already got the constraint, and visiting them again would lead to an
9961 * incorrect value for coninhcount.
9962 */
9963 if (newcons == NIL)
9964 return address;
9965
9966 /*
9967 * If adding a NO INHERIT constraint, no need to find our children.
9968 */
9969 if (constr->is_no_inherit)
9970 return address;
9971
9972 /*
9973 * Propagate to children as appropriate. Unlike most other ALTER
9974 * routines, we have to do this one level of recursion at a time; we can't
9975 * use find_all_inheritors to do it in one pass.
9976 */
9977 children =
9979
9980 /*
9981 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9982 * constraint creation only if there are no children currently. Error out
9983 * otherwise.
9984 */
9985 if (!recurse && children != NIL)
9986 ereport(ERROR,
9987 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9988 errmsg("constraint must be added to child tables too")));
9989
9990 /*
9991 * Recurse to create the constraint on each child.
9992 */
9993 foreach(child, children)
9994 {
9995 Oid childrelid = lfirst_oid(child);
9996 Relation childrel;
9997 AlteredTableInfo *childtab;
9998
9999 /* find_inheritance_children already got lock */
10000 childrel = table_open(childrelid, NoLock);
10001 CheckAlterTableIsSafe(childrel);
10002
10003 /* Find or create work queue entry for this table */
10004 childtab = ATGetQueueEntry(wqueue, childrel);
10005
10006 /* Recurse to this child */
10007 ATAddCheckNNConstraint(wqueue, childtab, childrel,
10008 constr, recurse, true, is_readd, lockmode);
10009
10010 table_close(childrel, NoLock);
10011 }
10012
10013 return address;
10014}
List * AddRelationNewConstraints(Relation rel, List *newColDefaults, List *newConstraints, bool allow_merge, bool is_local, bool is_internal, const char *queryString)
Definition: heap.c:2375
@ CONSTR_NOTNULL
Definition: parsenodes.h:2790
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
Definition: pg_inherits.c:58
static int list_length(const List *l)
Definition: pg_list.h:152
#define list_make1(x1)
Definition: pg_list.h:212
void check_stack_depth(void)
Definition: stack_depth.c:95
bool is_no_inherit
Definition: parsenodes.h:2829
Oid conoid
Definition: heap.h:39
char * name
Definition: heap.h:40
AttrNumber attnum
Definition: heap.h:41
bool skip_validation
Definition: heap.h:44
ConstrType contype
Definition: heap.h:37
Node * expr
Definition: heap.h:42
#define ATT_TABLE
Definition: tablecmds.c:328
static ObjectAddress ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9877
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition: tablecmds.c:6729
#define ATT_FOREIGN_TABLE
Definition: tablecmds.c:333
#define ATT_PARTITIONED_TABLE
Definition: tablecmds.c:336
static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool is_valid, bool queue_validation)
Definition: tablecmds.c:7830

References AddRelationNewConstraints(), Assert(), AT_AddConstraint, ATAddCheckNNConstraint(), ATGetQueueEntry(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, CookedConstraint::attnum, check_stack_depth(), CheckAlterTableIsSafe(), CommandCounterIncrement(), Constraint::conname, CookedConstraint::conoid, CONSTR_NOTNULL, AlteredTableInfo::constraints, NewConstraint::contype, CookedConstraint::contype, Constraint::contype, copyObject, ereport, errcode(), errmsg(), ERROR, CookedConstraint::expr, find_inheritance_children(), InvalidObjectAddress, Constraint::is_no_inherit, lappend(), lfirst, lfirst_oid, list_length(), list_make1, NewConstraint::name, CookedConstraint::name, NIL, NoLock, ObjectAddressSet, palloc0(), NewConstraint::qual, RelationGetRelid, set_attnotnull(), CookedConstraint::skip_validation, Constraint::skip_validation, table_close(), and table_open().

Referenced by ATAddCheckNNConstraint(), ATExecAddConstraint(), and DetachAddConstraintIfNeeded().

◆ ATAddForeignKeyConstraint()

static ObjectAddress ATAddForeignKeyConstraint ( List **  wqueue,
AlteredTableInfo tab,
Relation  rel,
Constraint fkconstraint,
bool  recurse,
bool  recursing,
LOCKMODE  lockmode 
)
static

Definition at line 10032 of file tablecmds.c.

10035{
10036 Relation pkrel;
10037 int16 pkattnum[INDEX_MAX_KEYS] = {0};
10038 int16 fkattnum[INDEX_MAX_KEYS] = {0};
10039 Oid pktypoid[INDEX_MAX_KEYS] = {0};
10040 Oid fktypoid[INDEX_MAX_KEYS] = {0};
10041 Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10042 Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10043 Oid opclasses[INDEX_MAX_KEYS] = {0};
10044 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10045 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10046 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10047 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10048 bool with_period;
10049 bool pk_has_without_overlaps;
10050 int i;
10051 int numfks,
10052 numpks,
10053 numfkdelsetcols;
10054 Oid indexOid;
10055 bool old_check_ok;
10056 ObjectAddress address;
10057 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10058
10059 /*
10060 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10061 * delete rows out from under us.
10062 */
10063 if (OidIsValid(fkconstraint->old_pktable_oid))
10064 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10065 else
10066 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10067
10068 /*
10069 * Validity checks (permission checks wait till we have the column
10070 * numbers)
10071 */
10072 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10073 ereport(ERROR,
10074 errcode(ERRCODE_WRONG_OBJECT_TYPE),
10075 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10077 RelationGetRelationName(pkrel)));
10078
10079 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10080 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10081 ereport(ERROR,
10082 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10083 errmsg("referenced relation \"%s\" is not a table",
10084 RelationGetRelationName(pkrel))));
10085
10087 ereport(ERROR,
10088 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10089 errmsg("permission denied: \"%s\" is a system catalog",
10090 RelationGetRelationName(pkrel))));
10091
10092 /*
10093 * References from permanent or unlogged tables to temp tables, and from
10094 * permanent tables to unlogged tables, are disallowed because the
10095 * referenced data can vanish out from under us. References from temp
10096 * tables to any other table type are also disallowed, because other
10097 * backends might need to run the RI triggers on the perm table, but they
10098 * can't reliably see tuples in the local buffers of other backends.
10099 */
10100 switch (rel->rd_rel->relpersistence)
10101 {
10102 case RELPERSISTENCE_PERMANENT:
10103 if (!RelationIsPermanent(pkrel))
10104 ereport(ERROR,
10105 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10106 errmsg("constraints on permanent tables may reference only permanent tables")));
10107 break;
10108 case RELPERSISTENCE_UNLOGGED:
10109 if (!RelationIsPermanent(pkrel)
10110 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10111 ereport(ERROR,
10112 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10113 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10114 break;
10115 case RELPERSISTENCE_TEMP:
10116 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10117 ereport(ERROR,
10118 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10119 errmsg("constraints on temporary tables may reference only temporary tables")));
10120 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10121 ereport(ERROR,
10122 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10123 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10124 break;
10125 }
10126
10127 /*
10128 * Look up the referencing attributes to make sure they exist, and record
10129 * their attnums and type and collation OIDs.
10130 */
10132 fkconstraint->fk_attrs,
10133 fkattnum, fktypoid, fkcolloid);
10134 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10135 if (with_period && !fkconstraint->fk_with_period)
10136 ereport(ERROR,
10137 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10138 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10139
10140 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10141 fkconstraint->fk_del_set_cols,
10142 fkdelsetcols, NULL, NULL);
10143 numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10144 numfkdelsetcols,
10145 fkdelsetcols,
10146 fkconstraint->fk_del_set_cols);
10147
10148 /*
10149 * If the attribute list for the referenced table was omitted, lookup the
10150 * definition of the primary key and use it. Otherwise, validate the
10151 * supplied attribute list. In either case, discover the index OID and
10152 * index opclasses, and the attnums and type and collation OIDs of the
10153 * attributes.
10154 */
10155 if (fkconstraint->pk_attrs == NIL)
10156 {
10157 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10158 &fkconstraint->pk_attrs,
10159 pkattnum, pktypoid, pkcolloid,
10160 opclasses, &pk_has_without_overlaps);
10161
10162 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10163 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10164 ereport(ERROR,
10165 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10166 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10167 }
10168 else
10169 {
10171 fkconstraint->pk_attrs,
10172 pkattnum, pktypoid, pkcolloid);
10173
10174 /* Since we got pk_attrs, one should be a period. */
10175 if (with_period && !fkconstraint->pk_with_period)
10176 ereport(ERROR,
10177 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10178 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10179
10180 /* Look for an index matching the column list */
10181 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10182 with_period, opclasses, &pk_has_without_overlaps);
10183 }
10184
10185 /*
10186 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10187 * must use PERIOD.
10188 */
10189 if (pk_has_without_overlaps && !with_period)
10190 ereport(ERROR,
10191 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10192 errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
10193
10194 /*
10195 * Now we can check permissions.
10196 */
10197 checkFkeyPermissions(pkrel, pkattnum, numpks);
10198
10199 /*
10200 * Check some things for generated columns.
10201 */
10202 for (i = 0; i < numfks; i++)
10203 {
10204 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10205
10206 if (attgenerated)
10207 {
10208 /*
10209 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10210 */
10211 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10212 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10213 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10214 ereport(ERROR,
10215 (errcode(ERRCODE_SYNTAX_ERROR),
10216 errmsg("invalid %s action for foreign key constraint containing generated column",
10217 "ON UPDATE")));
10218 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10220 ereport(ERROR,
10221 (errcode(ERRCODE_SYNTAX_ERROR),
10222 errmsg("invalid %s action for foreign key constraint containing generated column",
10223 "ON DELETE")));
10224 }
10225
10226 /*
10227 * FKs on virtual columns are not supported. This would require
10228 * various additional support in ri_triggers.c, including special
10229 * handling in ri_NullCheck(), ri_KeysEqual(),
10230 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10231 * as NULL there). Also not really practical as long as you can't
10232 * index virtual columns.
10233 */
10234 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10235 ereport(ERROR,
10236 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10237 errmsg("foreign key constraints on virtual generated columns are not supported")));
10238 }
10239
10240 /*
10241 * Some actions are currently unsupported for foreign keys using PERIOD.
10242 */
10243 if (fkconstraint->fk_with_period)
10244 {
10245 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10246 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10247 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10249 ereport(ERROR,
10250 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10251 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10252 "ON UPDATE"));
10253
10254 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10255 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10256 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10258 ereport(ERROR,
10259 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10260 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10261 "ON DELETE"));
10262 }
10263
10264 /*
10265 * Look up the equality operators to use in the constraint.
10266 *
10267 * Note that we have to be careful about the difference between the actual
10268 * PK column type and the opclass' declared input type, which might be
10269 * only binary-compatible with it. The declared opcintype is the right
10270 * thing to probe pg_amop with.
10271 */
10272 if (numfks != numpks)
10273 ereport(ERROR,
10274 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10275 errmsg("number of referencing and referenced columns for foreign key disagree")));
10276
10277 /*
10278 * On the strength of a previous constraint, we might avoid scanning
10279 * tables to validate this one. See below.
10280 */
10281 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10282 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10283
10284 for (i = 0; i < numpks; i++)
10285 {
10286 Oid pktype = pktypoid[i];
10287 Oid fktype = fktypoid[i];
10288 Oid fktyped;
10289 Oid pkcoll = pkcolloid[i];
10290 Oid fkcoll = fkcolloid[i];
10291 HeapTuple cla_ht;
10292 Form_pg_opclass cla_tup;
10293 Oid amid;
10294 Oid opfamily;
10295 Oid opcintype;
10296 bool for_overlaps;
10297 CompareType cmptype;
10298 Oid pfeqop;
10299 Oid ppeqop;
10300 Oid ffeqop;
10301 int16 eqstrategy;
10302 Oid pfeqop_right;
10303
10304 /* We need several fields out of the pg_opclass entry */
10305 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10306 if (!HeapTupleIsValid(cla_ht))
10307 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10308 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10309 amid = cla_tup->opcmethod;
10310 opfamily = cla_tup->opcfamily;
10311 opcintype = cla_tup->opcintype;
10312 ReleaseSysCache(cla_ht);
10313
10314 /*
10315 * Get strategy number from index AM.
10316 *
10317 * For a normal foreign-key constraint, this should not fail, since we
10318 * already checked that the index is unique and should therefore have
10319 * appropriate equal operators. For a period foreign key, this could
10320 * fail if we selected a non-matching exclusion constraint earlier.
10321 * (XXX Maybe we should do these lookups earlier so we don't end up
10322 * doing that.)
10323 */
10324 for_overlaps = with_period && i == numpks - 1;
10325 cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10326 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10327 if (eqstrategy == InvalidStrategy)
10328 ereport(ERROR,
10329 errcode(ERRCODE_UNDEFINED_OBJECT),
10330 for_overlaps
10331 ? errmsg("could not identify an overlaps operator for foreign key")
10332 : errmsg("could not identify an equality operator for foreign key"),
10333 errdetail("Could not translate compare type %d for operator family \"%s\", input type %s, access method \"%s\".",
10334 cmptype, get_opfamily_name(opfamily, false), format_type_be(opcintype), get_am_name(amid)));
10335
10336 /*
10337 * There had better be a primary equality operator for the index.
10338 * We'll use it for PK = PK comparisons.
10339 */
10340 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10341 eqstrategy);
10342
10343 if (!OidIsValid(ppeqop))
10344 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10345 eqstrategy, opcintype, opcintype, opfamily);
10346
10347 /*
10348 * Are there equality operators that take exactly the FK type? Assume
10349 * we should look through any domain here.
10350 */
10351 fktyped = getBaseType(fktype);
10352
10353 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10354 eqstrategy);
10355 if (OidIsValid(pfeqop))
10356 {
10357 pfeqop_right = fktyped;
10358 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10359 eqstrategy);
10360 }
10361 else
10362 {
10363 /* keep compiler quiet */
10364 pfeqop_right = InvalidOid;
10365 ffeqop = InvalidOid;
10366 }
10367
10368 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10369 {
10370 /*
10371 * Otherwise, look for an implicit cast from the FK type to the
10372 * opcintype, and if found, use the primary equality operator.
10373 * This is a bit tricky because opcintype might be a polymorphic
10374 * type such as ANYARRAY or ANYENUM; so what we have to test is
10375 * whether the two actual column types can be concurrently cast to
10376 * that type. (Otherwise, we'd fail to reject combinations such
10377 * as int[] and point[].)
10378 */
10379 Oid input_typeids[2];
10380 Oid target_typeids[2];
10381
10382 input_typeids[0] = pktype;
10383 input_typeids[1] = fktype;
10384 target_typeids[0] = opcintype;
10385 target_typeids[1] = opcintype;
10386 if (can_coerce_type(2, input_typeids, target_typeids,
10388 {
10389 pfeqop = ffeqop = ppeqop;
10390 pfeqop_right = opcintype;
10391 }
10392 }
10393
10394 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10395 ereport(ERROR,
10396 (errcode(ERRCODE_DATATYPE_MISMATCH),
10397 errmsg("foreign key constraint \"%s\" cannot be implemented",
10398 fkconstraint->conname),
10399 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10400 "are of incompatible types: %s and %s.",
10401 strVal(list_nth(fkconstraint->fk_attrs, i)),
10402 strVal(list_nth(fkconstraint->pk_attrs, i)),
10403 format_type_be(fktype),
10404 format_type_be(pktype))));
10405
10406 /*
10407 * This shouldn't be possible, but better check to make sure we have a
10408 * consistent state for the check below.
10409 */
10410 if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10411 elog(ERROR, "key columns are not both collatable");
10412
10413 if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10414 {
10415 bool pkcolldet;
10416 bool fkcolldet;
10417
10418 pkcolldet = get_collation_isdeterministic(pkcoll);
10419 fkcolldet = get_collation_isdeterministic(fkcoll);
10420
10421 /*
10422 * SQL requires that both collations are the same. This is
10423 * because we need a consistent notion of equality on both
10424 * columns. We relax this by allowing different collations if
10425 * they are both deterministic. (This is also for backward
10426 * compatibility, because PostgreSQL has always allowed this.)
10427 */
10428 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10429 ereport(ERROR,
10430 (errcode(ERRCODE_COLLATION_MISMATCH),
10431 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10432 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10433 "have incompatible collations: \"%s\" and \"%s\". "
10434 "If either collation is nondeterministic, then both collations have to be the same.",
10435 strVal(list_nth(fkconstraint->fk_attrs, i)),
10436 strVal(list_nth(fkconstraint->pk_attrs, i)),
10437 get_collation_name(fkcoll),
10438 get_collation_name(pkcoll))));
10439 }
10440
10441 if (old_check_ok)
10442 {
10443 /*
10444 * When a pfeqop changes, revalidate the constraint. We could
10445 * permit intra-opfamily changes, but that adds subtle complexity
10446 * without any concrete benefit for core types. We need not
10447 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10448 */
10449 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10450 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10451 old_pfeqop_item);
10452 }
10453 if (old_check_ok)
10454 {
10455 Oid old_fktype;
10456 Oid new_fktype;
10457 CoercionPathType old_pathtype;
10458 CoercionPathType new_pathtype;
10459 Oid old_castfunc;
10460 Oid new_castfunc;
10461 Oid old_fkcoll;
10462 Oid new_fkcoll;
10464 fkattnum[i] - 1);
10465
10466 /*
10467 * Identify coercion pathways from each of the old and new FK-side
10468 * column types to the right (foreign) operand type of the pfeqop.
10469 * We may assume that pg_constraint.conkey is not changing.
10470 */
10471 old_fktype = attr->atttypid;
10472 new_fktype = fktype;
10473 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10474 &old_castfunc);
10475 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10476 &new_castfunc);
10477
10478 old_fkcoll = attr->attcollation;
10479 new_fkcoll = fkcoll;
10480
10481 /*
10482 * Upon a change to the cast from the FK column to its pfeqop
10483 * operand, revalidate the constraint. For this evaluation, a
10484 * binary coercion cast is equivalent to no cast at all. While
10485 * type implementors should design implicit casts with an eye
10486 * toward consistency of operations like equality, we cannot
10487 * assume here that they have done so.
10488 *
10489 * A function with a polymorphic argument could change behavior
10490 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10491 * when the cast destination is polymorphic, we only avoid
10492 * revalidation if the input type has not changed at all. Given
10493 * just the core data types and operator classes, this requirement
10494 * prevents no would-be optimizations.
10495 *
10496 * If the cast converts from a base type to a domain thereon, then
10497 * that domain type must be the opcintype of the unique index.
10498 * Necessarily, the primary key column must then be of the domain
10499 * type. Since the constraint was previously valid, all values on
10500 * the foreign side necessarily exist on the primary side and in
10501 * turn conform to the domain. Consequently, we need not treat
10502 * domains specially here.
10503 *
10504 * If the collation changes, revalidation is required, unless both
10505 * collations are deterministic, because those share the same
10506 * notion of equality (because texteq reduces to bitwise
10507 * equality).
10508 *
10509 * We need not directly consider the PK type. It's necessarily
10510 * binary coercible to the opcintype of the unique index column,
10511 * and ri_triggers.c will only deal with PK datums in terms of
10512 * that opcintype. Changing the opcintype also changes pfeqop.
10513 */
10514 old_check_ok = (new_pathtype == old_pathtype &&
10515 new_castfunc == old_castfunc &&
10516 (!IsPolymorphicType(pfeqop_right) ||
10517 new_fktype == old_fktype) &&
10518 (new_fkcoll == old_fkcoll ||
10519 (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10520 }
10521
10522 pfeqoperators[i] = pfeqop;
10523 ppeqoperators[i] = ppeqop;
10524 ffeqoperators[i] = ffeqop;
10525 }
10526
10527 /*
10528 * For FKs with PERIOD we need additional operators to check whether the
10529 * referencing row's range is contained by the aggregated ranges of the
10530 * referenced row(s). For rangetypes and multirangetypes this is
10531 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10532 * support for now. FKs will look these up at "runtime", but we should
10533 * make sure the lookup works here, even if we don't use the values.
10534 */
10535 if (with_period)
10536 {
10537 Oid periodoperoid;
10538 Oid aggedperiodoperoid;
10539 Oid intersectoperoid;
10540
10541 FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10542 &intersectoperoid);
10543 }
10544
10545 /* First, create the constraint catalog entry itself. */
10547 fkconstraint->conname, fkconstraint, rel, pkrel,
10548 indexOid,
10549 InvalidOid, /* no parent constraint */
10550 numfks,
10551 pkattnum,
10552 fkattnum,
10553 pfeqoperators,
10554 ppeqoperators,
10555 ffeqoperators,
10556 numfkdelsetcols,
10557 fkdelsetcols,
10558 false,
10559 with_period);
10560
10561 /* Next process the action triggers at the referenced side and recurse */
10562 addFkRecurseReferenced(fkconstraint, rel, pkrel,
10563 indexOid,
10564 address.objectId,
10565 numfks,
10566 pkattnum,
10567 fkattnum,
10568 pfeqoperators,
10569 ppeqoperators,
10570 ffeqoperators,
10571 numfkdelsetcols,
10572 fkdelsetcols,
10573 old_check_ok,
10575 with_period);
10576
10577 /* Lastly create the check triggers at the referencing side and recurse */
10578 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10579 indexOid,
10580 address.objectId,
10581 numfks,
10582 pkattnum,
10583 fkattnum,
10584 pfeqoperators,
10585 ppeqoperators,
10586 ffeqoperators,
10587 numfkdelsetcols,
10588 fkdelsetcols,
10589 old_check_ok,
10590 lockmode,
10592 with_period);
10593
10594 /*
10595 * Done. Close pk table, but keep lock until we've committed.
10596 */
10597 table_close(pkrel, NoLock);
10598
10599 return address;
10600}
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition: amapi.c:148
char * get_am_name(Oid amOid)
Definition: amcmds.c:192
bool IsSystemRelation(Relation relation)
Definition: catalog.c:74
CompareType
Definition: cmptype.h:32
@ COMPARE_OVERLAP
Definition: cmptype.h:40
@ COMPARE_EQ
Definition: cmptype.h:36
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
bool allowSystemTableMods
Definition: globals.c:131
char * get_collation_name(Oid colloid)
Definition: lsyscache.c:1127
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:167
bool get_collation_isdeterministic(Oid colloid)
Definition: lsyscache.c:1146
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition: lsyscache.c:1393
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2661
bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids, CoercionContext ccontext)
Definition: parse_coerce.c:557
CoercionPathType
Definition: parse_coerce.h:25
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2809
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2812
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2810
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2811
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:202
void FindFKPeriodOpers(Oid opclass, Oid *containedbyoperoid, Oid *aggedcontainedbyoperoid, Oid *intersectoperoid)
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
@ COERCION_IMPLICIT
Definition: primnodes.h:731
#define RelationIsPermanent(relation)
Definition: rel.h:628
#define InvalidStrategy
Definition: stratnum.h:24
TupleDesc oldDesc
Definition: tablecmds.c:173
List * pk_attrs
Definition: parsenodes.h:2855
List * fk_del_set_cols
Definition: parsenodes.h:2861
Oid old_pktable_oid
Definition: parsenodes.h:2863
List * old_conpfeqop
Definition: parsenodes.h:2862
bool pk_with_period
Definition: parsenodes.h:2857
RangeVar * pktable
Definition: parsenodes.h:2853
bool rd_islocaltemp
Definition: rel.h:61
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: table.c:83
static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, int16 *fksetcolsattnums, List *fksetcols)
Definition: tablecmds.c:10611
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
Definition: tablecmds.c:13585
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:13434
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
Definition: tablecmds.c:13614
static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, List **attnamelist, int16 *attnums, Oid *atttypids, Oid *attcollids, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:13331
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
Definition: tablecmds.c:13276
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:160
#define strVal(v)
Definition: value.h:82

References addFkBothSides, addFkConstraint(), addFkRecurseReferenced(), addFkRecurseReferencing(), allowSystemTableMods, Assert(), can_coerce_type(), checkFkeyPermissions(), COERCION_IMPLICIT, COMPARE_EQ, COMPARE_OVERLAP, Constraint::conname, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, findFkeyCast(), FindFKPeriodOpers(), Constraint::fk_attrs, Constraint::fk_del_action, Constraint::fk_del_set_cols, Constraint::fk_upd_action, Constraint::fk_with_period, FKCONSTR_ACTION_CASCADE, FKCONSTR_ACTION_RESTRICT, FKCONSTR_ACTION_SETDEFAULT, FKCONSTR_ACTION_SETNULL, format_type_be(), get_am_name(), get_collation_isdeterministic(), get_collation_name(), get_opfamily_member(), get_opfamily_name(), getBaseType(), GETSTRUCT(), HeapTupleIsValid, i, INDEX_MAX_KEYS, IndexAmTranslateCompareType(), InvalidOid, InvalidStrategy, IsSystemRelation(), lfirst_oid, list_head(), list_length(), list_nth(), lnext(), NIL, NoLock, ObjectAddress::objectId, ObjectIdGetDatum(), OidIsValid, Constraint::old_conpfeqop, Constraint::old_pktable_oid, AlteredTableInfo::oldDesc, Constraint::pk_attrs, Constraint::pk_with_period, Constraint::pktable, RelationData::rd_islocaltemp, RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RelationIsPermanent, ReleaseSysCache(), SearchSysCache1(), ShareRowExclusiveLock, strVal, table_close(), table_open(), table_openrv(), transformColumnNameList(), transformFkeyCheckAttrs(), transformFkeyGetPrimaryKey(), TupleDescAttr(), and validateFkOnDeleteSetColumns().

Referenced by ATExecAddConstraint().

◆ ATCheckPartitionsNotInUse()

static void ATCheckPartitionsNotInUse ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 6851 of file tablecmds.c.

6852{
6853 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6854 {
6855 List *inh;
6856 ListCell *cell;
6857
6858 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6859 /* first element is the parent rel; must ignore it */
6860 for_each_from(cell, inh, 1)
6861 {
6862 Relation childrel;
6863
6864 /* find_all_inheritors already got lock */
6865 childrel = table_open(lfirst_oid(cell), NoLock);
6866 CheckAlterTableIsSafe(childrel);
6867 table_close(childrel, NoLock);
6868 }
6869 list_free(inh);
6870 }
6871}
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:255
#define for_each_from(cell, lst, N)
Definition: pg_list.h:414

References CheckAlterTableIsSafe(), find_all_inheritors(), for_each_from, lfirst_oid, list_free(), NoLock, RelationData::rd_rel, RelationGetRelid, table_close(), and table_open().

Referenced by ATPrepCmd().

◆ ATColumnChangeRequiresRewrite()

static bool ATColumnChangeRequiresRewrite ( Node expr,
AttrNumber  varattno 
)
static

Definition at line 14625 of file tablecmds.c.

14626{
14627 Assert(expr != NULL);
14628
14629 for (;;)
14630 {
14631 /* only one varno, so no need to check that */
14632 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14633 return false;
14634 else if (IsA(expr, RelabelType))
14635 expr = (Node *) ((RelabelType *) expr)->arg;
14636 else if (IsA(expr, CoerceToDomain))
14637 {
14638 CoerceToDomain *d = (CoerceToDomain *) expr;
14639
14641 return true;
14642 expr = (Node *) d->arg;
14643 }
14644 else if (IsA(expr, FuncExpr))
14645 {
14646 FuncExpr *f = (FuncExpr *) expr;
14647
14648 switch (f->funcid)
14649 {
14650 case F_TIMESTAMPTZ_TIMESTAMP:
14651 case F_TIMESTAMP_TIMESTAMPTZ:
14653 return true;
14654 else
14655 expr = linitial(f->args);
14656 break;
14657 default:
14658 return true;
14659 }
14660 }
14661 else
14662 return true;
14663 }
14664}
bool TimestampTimestampTzRequiresRewrite(void)
Definition: timestamp.c:6435
void * arg
#define linitial(l)
Definition: pg_list.h:178
Oid funcid
Definition: primnodes.h:767
List * args
Definition: primnodes.h:785
Definition: primnodes.h:262
bool DomainHasConstraints(Oid type_id)
Definition: typcache.c:1489

References arg, CoerceToDomain::arg, FuncExpr::args, Assert(), DomainHasConstraints(), FuncExpr::funcid, IsA, linitial, CoerceToDomain::resulttype, and TimestampTimestampTzRequiresRewrite().

Referenced by ATPrepAlterColumnType().

◆ ATController()

static void ATController ( AlterTableStmt parsetree,
Relation  rel,
List cmds,
bool  recurse,
LOCKMODE  lockmode,
AlterTableUtilityContext context 
)
static

Definition at line 4860 of file tablecmds.c.

4863{
4864 List *wqueue = NIL;
4865 ListCell *lcmd;
4866
4867 /* Phase 1: preliminary examination of commands, create work queue */
4868 foreach(lcmd, cmds)
4869 {
4870 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4871
4872 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4873 }
4874
4875 /* Close the relation, but keep lock until commit */
4876 relation_close(rel, NoLock);
4877
4878 /* Phase 2: update system catalogs */
4879 ATRewriteCatalogs(&wqueue, lockmode, context);
4880
4881 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4882 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4883}
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5292
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4895
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5828

References ATPrepCmd(), ATRewriteCatalogs(), ATRewriteTables(), lfirst, NIL, NoLock, and relation_close().

Referenced by AlterTable(), and AlterTableInternal().

◆ ATDetachCheckNoForeignKeyRefs()

static void ATDetachCheckNoForeignKeyRefs ( Relation  partition)
static

Definition at line 21896 of file tablecmds.c.

21897{
21898 List *constraints;
21899 ListCell *cell;
21900
21901 constraints = GetParentedForeignKeyRefs(partition);
21902
21903 foreach(cell, constraints)
21904 {
21905 Oid constrOid = lfirst_oid(cell);
21906 HeapTuple tuple;
21907 Form_pg_constraint constrForm;
21908 Relation rel;
21909 Trigger trig = {0};
21910
21911 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21912 if (!HeapTupleIsValid(tuple))
21913 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21914 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21915
21916 Assert(OidIsValid(constrForm->conparentid));
21917 Assert(constrForm->confrelid == RelationGetRelid(partition));
21918
21919 /* prevent data changes into the referencing table until commit */
21920 rel = table_open(constrForm->conrelid, ShareLock);
21921
21922 trig.tgoid = InvalidOid;
21923 trig.tgname = NameStr(constrForm->conname);
21925 trig.tgisinternal = true;
21926 trig.tgconstrrelid = RelationGetRelid(partition);
21927 trig.tgconstrindid = constrForm->conindid;
21928 trig.tgconstraint = constrForm->oid;
21929 trig.tgdeferrable = false;
21930 trig.tginitdeferred = false;
21931 /* we needn't fill in remaining fields */
21932
21933 RI_PartitionRemove_Check(&trig, rel, partition);
21934
21935 ReleaseSysCache(tuple);
21936
21937 table_close(rel, NoLock);
21938 }
21939}
#define ShareLock
Definition: lockdefs.h:40
void RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1813
char tgenabled
Definition: reltrigger.h:30
Oid tgoid
Definition: reltrigger.h:25
Oid tgconstrindid
Definition: reltrigger.h:34
Oid tgconstraint
Definition: reltrigger.h:35
Oid tgconstrrelid
Definition: reltrigger.h:33
char * tgname
Definition: reltrigger.h:27
bool tgdeferrable
Definition: reltrigger.h:36
bool tginitdeferred
Definition: reltrigger.h:37
bool tgisinternal
Definition: reltrigger.h:31
static List * GetParentedForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21843
#define TRIGGER_FIRES_ON_ORIGIN
Definition: trigger.h:149

References Assert(), elog, ERROR, GetParentedForeignKeyRefs(), GETSTRUCT(), HeapTupleIsValid, InvalidOid, lfirst_oid, NameStr, NoLock, ObjectIdGetDatum(), OidIsValid, RelationGetRelid, ReleaseSysCache(), RI_PartitionRemove_Check(), SearchSysCache1(), ShareLock, table_close(), table_open(), Trigger::tgconstraint, Trigger::tgconstrindid, Trigger::tgconstrrelid, Trigger::tgdeferrable, Trigger::tgenabled, Trigger::tginitdeferred, Trigger::tgisinternal, Trigger::tgname, Trigger::tgoid, and TRIGGER_FIRES_ON_ORIGIN.

Referenced by ATExecDetachPartition().

◆ AtEOSubXact_on_commit_actions()

void AtEOSubXact_on_commit_actions ( bool  isCommit,
SubTransactionId  mySubid,
SubTransactionId  parentSubid 
)

Definition at line 19382 of file tablecmds.c.

19384{
19385 ListCell *cur_item;
19386
19387 foreach(cur_item, on_commits)
19388 {
19389 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19390
19391 if (!isCommit && oc->creating_subid == mySubid)
19392 {
19393 /* cur_item must be removed */
19395 pfree(oc);
19396 }
19397 else
19398 {
19399 /* cur_item must be preserved */
19400 if (oc->creating_subid == mySubid)
19401 oc->creating_subid = parentSubid;
19402 if (oc->deleting_subid == mySubid)
19403 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19404 }
19405 }
19406}
#define InvalidSubTransactionId
Definition: c.h:629
#define foreach_delete_current(lst, var_or_cell)
Definition: pg_list.h:391
SubTransactionId creating_subid
Definition: tablecmds.c:127
SubTransactionId deleting_subid
Definition: tablecmds.c:128
static List * on_commits
Definition: tablecmds.c:131

References OnCommitItem::creating_subid, OnCommitItem::deleting_subid, foreach_delete_current, InvalidSubTransactionId, lfirst, on_commits, and pfree().

Referenced by AbortSubTransaction(), and CommitSubTransaction().

◆ AtEOXact_on_commit_actions()

void AtEOXact_on_commit_actions ( bool  isCommit)

Definition at line 19350 of file tablecmds.c.

19351{
19352 ListCell *cur_item;
19353
19354 foreach(cur_item, on_commits)
19355 {
19356 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19357
19358 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19360 {
19361 /* cur_item must be removed */
19363 pfree(oc);
19364 }
19365 else
19366 {
19367 /* cur_item must be preserved */
19370 }
19371 }
19372}

References OnCommitItem::creating_subid, OnCommitItem::deleting_subid, foreach_delete_current, InvalidSubTransactionId, lfirst, on_commits, and pfree().

Referenced by AbortTransaction(), CommitTransaction(), and PrepareTransaction().

◆ ATExecAddColumn()

static ObjectAddress ATExecAddColumn ( List **  wqueue,
AlteredTableInfo tab,
Relation  rel,
AlterTableCmd **  cmd,
bool  recurse,
bool  recursing,
LOCKMODE  lockmode,
AlterTablePass  cur_pass,
AlterTableUtilityContext context 
)
static

Definition at line 7207 of file tablecmds.c.

7211{
7212 Oid myrelid = RelationGetRelid(rel);
7213 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7214 bool if_not_exists = (*cmd)->missing_ok;
7215 Relation pgclass,
7216 attrdesc;
7217 HeapTuple reltup;
7218 Form_pg_class relform;
7219 Form_pg_attribute attribute;
7220 int newattnum;
7221 char relkind;
7222 Expr *defval;
7223 List *children;
7224 ListCell *child;
7225 AlterTableCmd *childcmd;
7226 ObjectAddress address;
7227 TupleDesc tupdesc;
7228
7229 /* since this function recurses, it could be driven to stack overflow */
7231
7232 /* At top level, permission check was done in ATPrepCmd, else do it */
7233 if (recursing)
7234 ATSimplePermissions((*cmd)->subtype, rel,
7236
7237 if (rel->rd_rel->relispartition && !recursing)
7238 ereport(ERROR,
7239 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7240 errmsg("cannot add column to a partition")));
7241
7242 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7243
7244 /*
7245 * Are we adding the column to a recursion child? If so, check whether to
7246 * merge with an existing definition for the column. If we do merge, we
7247 * must not recurse. Children will already have the column, and recursing
7248 * into them would mess up attinhcount.
7249 */
7250 if (colDef->inhcount > 0)
7251 {
7252 HeapTuple tuple;
7253
7254 /* Does child already have a column by this name? */
7255 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7256 if (HeapTupleIsValid(tuple))
7257 {
7258 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7259 Oid ctypeId;
7260 int32 ctypmod;
7261 Oid ccollid;
7262
7263 /* Child column must match on type, typmod, and collation */
7264 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7265 if (ctypeId != childatt->atttypid ||
7266 ctypmod != childatt->atttypmod)
7267 ereport(ERROR,
7268 (errcode(ERRCODE_DATATYPE_MISMATCH),
7269 errmsg("child table \"%s\" has different type for column \"%s\"",
7270 RelationGetRelationName(rel), colDef->colname)));
7271 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7272 if (ccollid != childatt->attcollation)
7273 ereport(ERROR,
7274 (errcode(ERRCODE_COLLATION_MISMATCH),
7275 errmsg("child table \"%s\" has different collation for column \"%s\"",
7276 RelationGetRelationName(rel), colDef->colname),
7277 errdetail("\"%s\" versus \"%s\"",
7278 get_collation_name(ccollid),
7279 get_collation_name(childatt->attcollation))));
7280
7281 /* Bump the existing child att's inhcount */
7282 if (pg_add_s16_overflow(childatt->attinhcount, 1,
7283 &childatt->attinhcount))
7284 ereport(ERROR,
7285 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7286 errmsg("too many inheritance parents"));
7287 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7288
7289 heap_freetuple(tuple);
7290
7291 /* Inform the user about the merge */
7293 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7294 colDef->colname, RelationGetRelationName(rel))));
7295
7296 table_close(attrdesc, RowExclusiveLock);
7297
7298 /* Make the child column change visible */
7300
7301 return InvalidObjectAddress;
7302 }
7303 }
7304
7305 /* skip if the name already exists and if_not_exists is true */
7306 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7307 {
7308 table_close(attrdesc, RowExclusiveLock);
7309 return InvalidObjectAddress;
7310 }
7311
7312 /*
7313 * Okay, we need to add the column, so go ahead and do parse
7314 * transformation. This can result in queueing up, or even immediately
7315 * executing, subsidiary operations (such as creation of unique indexes);
7316 * so we mustn't do it until we have made the if_not_exists check.
7317 *
7318 * When recursing, the command was already transformed and we needn't do
7319 * so again. Also, if context isn't given we can't transform. (That
7320 * currently happens only for AT_AddColumnToView; we expect that view.c
7321 * passed us a ColumnDef that doesn't need work.)
7322 */
7323 if (context != NULL && !recursing)
7324 {
7325 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7326 cur_pass, context);
7327 Assert(*cmd != NULL);
7328 colDef = castNode(ColumnDef, (*cmd)->def);
7329 }
7330
7331 /*
7332 * Regular inheritance children are independent enough not to inherit the
7333 * identity column from parent hence cannot recursively add identity
7334 * column if the table has inheritance children.
7335 *
7336 * Partitions, on the other hand, are integral part of a partitioned table
7337 * and inherit identity column. Hence propagate identity column down the
7338 * partition hierarchy.
7339 */
7340 if (colDef->identity &&
7341 recurse &&
7342 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7344 ereport(ERROR,
7345 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7346 errmsg("cannot recursively add identity column to table that has child tables")));
7347
7348 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7349
7350 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7351 if (!HeapTupleIsValid(reltup))
7352 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7353 relform = (Form_pg_class) GETSTRUCT(reltup);
7354 relkind = relform->relkind;
7355
7356 /* Determine the new attribute's number */
7357 newattnum = relform->relnatts + 1;
7358 if (newattnum > MaxHeapAttributeNumber)
7359 ereport(ERROR,
7360 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7361 errmsg("tables can have at most %d columns",
7363
7364 /*
7365 * Construct new attribute's pg_attribute entry.
7366 */
7367 tupdesc = BuildDescForRelation(list_make1(colDef));
7368
7369 attribute = TupleDescAttr(tupdesc, 0);
7370
7371 /* Fix up attribute number */
7372 attribute->attnum = newattnum;
7373
7374 /* make sure datatype is legal for a column */
7375 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7376 list_make1_oid(rel->rd_rel->reltype),
7377 0);
7378
7379 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7380
7381 table_close(attrdesc, RowExclusiveLock);
7382
7383 /*
7384 * Update pg_class tuple as appropriate
7385 */
7386 relform->relnatts = newattnum;
7387
7388 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7389
7390 heap_freetuple(reltup);
7391
7392 /* Post creation hook for new attribute */
7393 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7394
7395 table_close(pgclass, RowExclusiveLock);
7396
7397 /* Make the attribute's catalog entry visible */
7399
7400 /*
7401 * Store the DEFAULT, if any, in the catalogs
7402 */
7403 if (colDef->raw_default)
7404 {
7405 RawColumnDefault *rawEnt;
7406
7407 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7408 rawEnt->attnum = attribute->attnum;
7409 rawEnt->raw_default = copyObject(colDef->raw_default);
7410 rawEnt->generated = colDef->generated;
7411
7412 /*
7413 * This function is intended for CREATE TABLE, so it processes a
7414 * _list_ of defaults, but we just do one.
7415 */
7417 false, true, false, NULL);
7418
7419 /* Make the additional catalog changes visible */
7421 }
7422
7423 /*
7424 * Tell Phase 3 to fill in the default expression, if there is one.
7425 *
7426 * If there is no default, Phase 3 doesn't have to do anything, because
7427 * that effectively means that the default is NULL. The heap tuple access
7428 * routines always check for attnum > # of attributes in tuple, and return
7429 * NULL if so, so without any modification of the tuple data we will get
7430 * the effect of NULL values in the new column.
7431 *
7432 * An exception occurs when the new column is of a domain type: the domain
7433 * might have a not-null constraint, or a check constraint that indirectly
7434 * rejects nulls. If there are any domain constraints then we construct
7435 * an explicit NULL default value that will be passed through
7436 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7437 * rewriting the table which we really wouldn't have to do; but we do it
7438 * to preserve the historical behavior that such a failure will be raised
7439 * only if the table currently contains some rows.)
7440 *
7441 * Note: we use build_column_default, and not just the cooked default
7442 * returned by AddRelationNewConstraints, so that the right thing happens
7443 * when a datatype's default applies.
7444 *
7445 * Note: it might seem that this should happen at the end of Phase 2, so
7446 * that the effects of subsequent subcommands can be taken into account.
7447 * It's intentional that we do it now, though. The new column should be
7448 * filled according to what is said in the ADD COLUMN subcommand, so that
7449 * the effects are the same as if this subcommand had been run by itself
7450 * and the later subcommands had been issued in new ALTER TABLE commands.
7451 *
7452 * We can skip this entirely for relations without storage, since Phase 3
7453 * is certainly not going to touch them.
7454 */
7455 if (RELKIND_HAS_STORAGE(relkind))
7456 {
7457 bool has_domain_constraints;
7458 bool has_missing = false;
7459
7460 /*
7461 * For an identity column, we can't use build_column_default(),
7462 * because the sequence ownership isn't set yet. So do it manually.
7463 */
7464 if (colDef->identity)
7465 {
7467
7468 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7469 nve->typeId = attribute->atttypid;
7470
7471 defval = (Expr *) nve;
7472 }
7473 else
7474 defval = (Expr *) build_column_default(rel, attribute->attnum);
7475
7476 /* Build CoerceToDomain(NULL) expression if needed */
7477 has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7478 if (!defval && has_domain_constraints)
7479 {
7480 Oid baseTypeId;
7481 int32 baseTypeMod;
7482 Oid baseTypeColl;
7483
7484 baseTypeMod = attribute->atttypmod;
7485 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7486 baseTypeColl = get_typcollation(baseTypeId);
7487 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7488 defval = (Expr *) coerce_to_target_type(NULL,
7489 (Node *) defval,
7490 baseTypeId,
7491 attribute->atttypid,
7492 attribute->atttypmod,
7495 -1);
7496 if (defval == NULL) /* should not happen */
7497 elog(ERROR, "failed to coerce base type to domain");
7498 }
7499
7500 if (defval)
7501 {
7503
7504 /* Prepare defval for execution, either here or in Phase 3 */
7505 defval = expression_planner(defval);
7506
7507 /* Add the new default to the newvals list */
7509 newval->attnum = attribute->attnum;
7510 newval->expr = defval;
7511 newval->is_generated = (colDef->generated != '\0');
7512
7513 tab->newvals = lappend(tab->newvals, newval);
7514
7515 /*
7516 * Attempt to skip a complete table rewrite by storing the
7517 * specified DEFAULT value outside of the heap. This is only
7518 * allowed for plain relations and non-generated columns, and the
7519 * default expression can't be volatile (stable is OK). Note that
7520 * contain_volatile_functions deems CoerceToDomain immutable, but
7521 * here we consider that coercion to a domain with constraints is
7522 * volatile; else it might fail even when the table is empty.
7523 */
7524 if (rel->rd_rel->relkind == RELKIND_RELATION &&
7525 !colDef->generated &&
7526 !has_domain_constraints &&
7527 !contain_volatile_functions((Node *) defval))
7528 {
7529 EState *estate;
7530 ExprState *exprState;
7531 Datum missingval;
7532 bool missingIsNull;
7533
7534 /* Evaluate the default expression */
7535 estate = CreateExecutorState();
7536 exprState = ExecPrepareExpr(defval, estate);
7537 missingval = ExecEvalExpr(exprState,
7538 GetPerTupleExprContext(estate),
7539 &missingIsNull);
7540 /* If it turns out NULL, nothing to do; else store it */
7541 if (!missingIsNull)
7542 {
7543 StoreAttrMissingVal(rel, attribute->attnum, missingval);
7544 /* Make the additional catalog change visible */
7546 has_missing = true;
7547 }
7548 FreeExecutorState(estate);
7549 }
7550 else
7551 {
7552 /*
7553 * Failed to use missing mode. We have to do a table rewrite
7554 * to install the value --- unless it's a virtual generated
7555 * column.
7556 */
7557 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7559 }
7560 }
7561
7562 if (!has_missing)
7563 {
7564 /*
7565 * If the new column is NOT NULL, and there is no missing value,
7566 * tell Phase 3 it needs to check for NULLs.
7567 */
7568 tab->verify_new_notnull |= colDef->is_not_null;
7569 }
7570 }
7571
7572 /*
7573 * Add needed dependency entries for the new column.
7574 */
7575 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7576 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7577
7578 /*
7579 * Propagate to children as appropriate. Unlike most other ALTER
7580 * routines, we have to do this one level of recursion at a time; we can't
7581 * use find_all_inheritors to do it in one pass.
7582 */
7583 children =
7585
7586 /*
7587 * If we are told not to recurse, there had better not be any child
7588 * tables; else the addition would put them out of step.
7589 */
7590 if (children && !recurse)
7591 ereport(ERROR,
7592 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7593 errmsg("column must be added to child tables too")));
7594
7595 /* Children should see column as singly inherited */
7596 if (!recursing)
7597 {
7598 childcmd = copyObject(*cmd);
7599 colDef = castNode(ColumnDef, childcmd->def);
7600 colDef->inhcount = 1;
7601 colDef->is_local = false;
7602 }
7603 else
7604 childcmd = *cmd; /* no need to copy again */
7605
7606 foreach(child, children)
7607 {
7608 Oid childrelid = lfirst_oid(child);
7609 Relation childrel;
7610 AlteredTableInfo *childtab;
7611
7612 /* find_inheritance_children already got lock */
7613 childrel = table_open(childrelid, NoLock);
7614 CheckAlterTableIsSafe(childrel);
7615
7616 /* Find or create work queue entry for this table */
7617 childtab = ATGetQueueEntry(wqueue, childrel);
7618
7619 /* Recurse to child; return value is ignored */
7620 ATExecAddColumn(wqueue, childtab, childrel,
7621 &childcmd, recurse, true,
7622 lockmode, cur_pass, context);
7623
7624 table_close(childrel, NoLock);
7625 }
7626
7627 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7628 return address;
7629}
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:539
#define AT_REWRITE_DEFAULT_VAL
Definition: event_trigger.h:41
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:765
void FreeExecutorState(EState *estate)
Definition: execUtils.c:193
EState * CreateExecutorState(void)
Definition: execUtils.c:88
#define GetPerTupleExprContext(estate)
Definition: executor.h:678
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:415
#define newval
void CheckAttributeType(const char *attname, Oid atttypid, Oid attcollation, List *containing_rowtypes, int flags)
Definition: heap.c:544
void InsertPgAttributeTuples(Relation pg_attribute_rel, TupleDesc tupdesc, Oid new_rel_oid, const FormExtraData_pg_attribute tupdesc_extra[], CatalogIndexState indstate)
Definition: heap.c:708
void StoreAttrMissingVal(Relation rel, AttrNumber attnum, Datum missingval)
Definition: heap.c:2020
#define MaxHeapAttributeNumber
Definition: htup_details.h:48
static bool pg_add_s16_overflow(int16 a, int16 b, int16 *result)
Definition: int.h:67
Oid get_typcollation(Oid typid)
Definition: lsyscache.c:3196
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition: lsyscache.c:2678
Const * makeNullConst(Oid consttype, int32 consttypmod, Oid constcollid)
Definition: makefuncs.c:388
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition: namespace.h:80
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:173
#define ObjectAddressSubSet(addr, class_id, object_id, object_sub_id)
Definition: objectaddress.h:33
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:78
void typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName, Oid *typeid_p, int32 *typmod_p)
Definition: parse_type.c:310
Oid GetColumnDefCollation(ParseState *pstate, const ColumnDef *coldef, Oid typeOid)
Definition: parse_type.c:540
#define list_make1_oid(x1)
Definition: pg_list.h:242
Expr * expression_planner(Expr *expr)
Definition: planner.c:6645
uintptr_t Datum
Definition: postgres.h:69
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:753
@ COERCION_ASSIGNMENT
Definition: primnodes.h:732
Node * build_column_default(Relation rel, int attrno)
bool verify_new_notnull
Definition: tablecmds.c:190
bool is_not_null
Definition: parsenodes.h:742
char identity
Definition: parsenodes.h:748
RangeVar * identitySequence
Definition: parsenodes.h:749
char * colname
Definition: parsenodes.h:737
TypeName * typeName
Definition: parsenodes.h:738
char generated
Definition: parsenodes.h:751
Node * raw_default
Definition: parsenodes.h:746
bool is_local
Definition: parsenodes.h:741
int16 inhcount
Definition: parsenodes.h:740
Node * raw_default
Definition: heap.h:31
AttrNumber attnum
Definition: heap.h:30
char generated
Definition: heap.h:32
HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname)
Definition: syscache.c:503
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:91
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
Definition: tablecmds.c:7689
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition: tablecmds.c:7707
TupleDesc BuildDescForRelation(const List *columns)
Definition: tablecmds.c:1370
static AlterTableCmd * ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5701
static bool check_for_column_name_collision(Relation rel, const char *colname, bool if_not_exists)
Definition: tablecmds.c:7636
static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd **cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:7207

References add_column_collation_dependency(), add_column_datatype_dependency(), AddRelationNewConstraints(), Assert(), AT_REWRITE_DEFAULT_VAL, ATExecAddColumn(), ATGetQueueEntry(), ATParseTransformCmd(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, RawColumnDefault::attnum, build_column_default(), BuildDescForRelation(), castNode, CatalogTupleUpdate(), check_for_column_name_collision(), check_stack_depth(), CheckAlterTableIsSafe(), CheckAttributeType(), COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, ColumnDef::colname, CommandCounterIncrement(), contain_volatile_functions(), copyObject, CreateExecutorState(), AlterTableCmd::def, DomainHasConstraints(), elog, ereport, errcode(), errdetail(), errmsg(), ERROR, ExecEvalExpr(), ExecPrepareExpr(), expression_planner(), find_inheritance_children(), FreeExecutorState(), RawColumnDefault::generated, ColumnDef::generated, get_collation_name(), get_typcollation(), getBaseTypeAndTypmod(), GetColumnDefCollation(), GetPerTupleExprContext, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, ColumnDef::identity, ColumnDef::identitySequence, ColumnDef::inhcount, InsertPgAttributeTuples(), InvalidObjectAddress, InvokeObjectPostCreateHook, ColumnDef::is_local, ColumnDef::is_not_null, lappend(), lfirst_oid, list_make1, list_make1_oid, makeNode, makeNullConst(), MaxHeapAttributeNumber, NameStr, newval, AlteredTableInfo::newvals, NIL, NoLock, NOTICE, ObjectAddressSubSet, ObjectIdGetDatum(), palloc(), palloc0(), pg_add_s16_overflow(), RangeVarGetRelid, RawColumnDefault::raw_default, ColumnDef::raw_default, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, AlteredTableInfo::rewrite, RowExclusiveLock, SearchSysCacheCopy1, SearchSysCacheCopyAttName(), NextValueExpr::seqid, StoreAttrMissingVal(), HeapTupleData::t_self, table_close(), table_open(), TupleDescAttr(), NextValueExpr::typeId, ColumnDef::typeName, typenameTypeIdAndMod(), and AlteredTableInfo::verify_new_notnull.

Referenced by ATExecAddColumn(), and ATExecCmd().

◆ ATExecAddConstraint()

static ObjectAddress ATExecAddConstraint ( List **  wqueue,
AlteredTableInfo tab,
Relation  rel,
Constraint newConstraint,
bool  recurse,
bool  is_readd,
LOCKMODE  lockmode 
)
static

Definition at line 9761 of file tablecmds.c.

9764{
9766
9767 Assert(IsA(newConstraint, Constraint));
9768
9769 /*
9770 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9771 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9772 * parse_utilcmd.c).
9773 */
9774 switch (newConstraint->contype)
9775 {
9776 case CONSTR_CHECK:
9777 case CONSTR_NOTNULL:
9778 address =
9779 ATAddCheckNNConstraint(wqueue, tab, rel,
9780 newConstraint, recurse, false, is_readd,
9781 lockmode);
9782 break;
9783
9784 case CONSTR_FOREIGN:
9785
9786 /*
9787 * Assign or validate constraint name
9788 */
9789 if (newConstraint->conname)
9790 {
9792 RelationGetRelid(rel),
9793 newConstraint->conname))
9794 ereport(ERROR,
9796 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9797 newConstraint->conname,
9799 }
9800 else
9801 newConstraint->conname =
9804 "fkey",
9806 NIL);
9807
9808 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9809 newConstraint,
9810 recurse, false,
9811 lockmode);
9812 break;
9813
9814 default:
9815 elog(ERROR, "unrecognized constraint type: %d",
9816 (int) newConstraint->contype);
9817 }
9818
9819 return address;
9820}
@ CONSTR_CHECK
Definition: parsenodes.h:2794
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:10032

References Assert(), ATAddCheckNNConstraint(), ATAddForeignKeyConstraint(), ChooseConstraintName(), ChooseForeignKeyConstraintNameAddition(), Constraint::conname, CONSTR_CHECK, CONSTR_FOREIGN, CONSTR_NOTNULL, CONSTRAINT_RELATION, ConstraintNameIsUsed(), Constraint::contype, elog, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, Constraint::fk_attrs, InvalidObjectAddress, IsA, NIL, RelationGetNamespace, RelationGetRelationName, and RelationGetRelid.

Referenced by ATExecCmd().

◆ ATExecAddIdentity()

static ObjectAddress ATExecAddIdentity ( Relation  rel,
const char *  colName,
Node def,
LOCKMODE  lockmode,
bool  recurse,
bool  recursing 
)
static

Definition at line 8230 of file tablecmds.c.

8232{
8233 Relation attrelation;
8234 HeapTuple tuple;
8235 Form_pg_attribute attTup;
8237 ObjectAddress address;
8238 ColumnDef *cdef = castNode(ColumnDef, def);
8239 bool ispartitioned;
8240
8241 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8242 if (ispartitioned && !recurse)
8243 ereport(ERROR,
8244 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8245 errmsg("cannot add identity to a column of only the partitioned table"),
8246 errhint("Do not specify the ONLY keyword.")));
8247
8248 if (rel->rd_rel->relispartition && !recursing)
8249 ereport(ERROR,
8250 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8251 errmsg("cannot add identity to a column of a partition"));
8252
8253 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8254
8255 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8256 if (!HeapTupleIsValid(tuple))
8257 ereport(ERROR,
8258 (errcode(ERRCODE_UNDEFINED_COLUMN),
8259 errmsg("column \"%s\" of relation \"%s\" does not exist",
8260 colName, RelationGetRelationName(rel))));
8261 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8262 attnum = attTup->attnum;
8263
8264 /* Can't alter a system attribute */
8265 if (attnum <= 0)
8266 ereport(ERROR,
8267 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8268 errmsg("cannot alter system column \"%s\"",
8269 colName)));
8270
8271 /*
8272 * Creating a column as identity implies NOT NULL, so adding the identity
8273 * to an existing column that is not NOT NULL would create a state that
8274 * cannot be reproduced without contortions.
8275 */
8276 if (!attTup->attnotnull)
8277 ereport(ERROR,
8278 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8279 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8280 colName, RelationGetRelationName(rel))));
8281
8282 if (attTup->attidentity)
8283 ereport(ERROR,
8284 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8285 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8286 colName, RelationGetRelationName(rel))));
8287
8288 if (attTup->atthasdef)
8289 ereport(ERROR,
8290 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8291 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8292 colName, RelationGetRelationName(rel))));
8293
8294 attTup->attidentity = cdef->identity;
8295 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8296
8297 InvokeObjectPostAlterHook(RelationRelationId,
8298 RelationGetRelid(rel),
8299 attTup->attnum);
8300 ObjectAddressSubSet(address, RelationRelationId,
8301 RelationGetRelid(rel), attnum);
8302 heap_freetuple(tuple);
8303
8304 table_close(attrelation, RowExclusiveLock);
8305
8306 /*
8307 * Recurse to propagate the identity column to partitions. Identity is
8308 * not inherited in regular inheritance children.
8309 */
8310 if (recurse && ispartitioned)
8311 {
8312 List *children;
8313 ListCell *lc;
8314
8315 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8316
8317 foreach(lc, children)
8318 {
8319 Relation childrel;
8320
8321 childrel = table_open(lfirst_oid(lc), NoLock);
8322 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8323 table_close(childrel, NoLock);
8324 }
8325 }
8326
8327 return address;
8328}
int errhint(const char *fmt,...)
Definition: elog.c:1318
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8230

References ATExecAddIdentity(), attnum, castNode, CatalogTupleUpdate(), ereport, errcode(), errhint(), errmsg(), ERROR, find_inheritance_children(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, ColumnDef::identity, InvokeObjectPostAlterHook, lfirst_oid, NoLock, ObjectAddressSubSet, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecAddIdentity(), and ATExecCmd().

◆ ATExecAddIndex()

static ObjectAddress ATExecAddIndex ( AlteredTableInfo tab,
Relation  rel,
IndexStmt stmt,
bool  is_rebuild,
LOCKMODE  lockmode 
)
static

Definition at line 9585 of file tablecmds.c.

9587{
9588 bool check_rights;
9589 bool skip_build;
9590 bool quiet;
9591 ObjectAddress address;
9592
9594 Assert(!stmt->concurrent);
9595
9596 /* The IndexStmt has already been through transformIndexStmt */
9597 Assert(stmt->transformed);
9598
9599 /* suppress schema rights check when rebuilding existing index */
9600 check_rights = !is_rebuild;
9601 /* skip index build if phase 3 will do it or we're reusing an old one */
9602 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9603 /* suppress notices when rebuilding existing index */
9604 quiet = is_rebuild;
9605
9606 address = DefineIndex(RelationGetRelid(rel),
9607 stmt,
9608 InvalidOid, /* no predefined OID */
9609 InvalidOid, /* no parent index */
9610 InvalidOid, /* no parent constraint */
9611 -1, /* total_parts unknown */
9612 true, /* is_alter_table */
9613 check_rights,
9614 false, /* check_not_in_use - we did it already */
9615 skip_build,
9616 quiet);
9617
9618 /*
9619 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9620 * new index instead of building from scratch. Restore associated fields.
9621 * This may store InvalidSubTransactionId in both fields, in which case
9622 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9623 * this after the CCI that made catalog rows visible to any rebuild. The
9624 * DROP of the old edition of this index will have scheduled the storage
9625 * for deletion at commit, so cancel that pending deletion.
9626 */
9627 if (RelFileNumberIsValid(stmt->oldNumber))
9628 {
9629 Relation irel = index_open(address.objectId, NoLock);
9630
9631 irel->rd_createSubid = stmt->oldCreateSubid;
9632 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9634 index_close(irel, NoLock);
9635 }
9636
9637 return address;
9638}
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
ObjectAddress DefineIndex(Oid tableId, IndexStmt *stmt, Oid indexRelationId, Oid parentIndexId, Oid parentConstraintId, int total_parts, bool is_alter_table, bool check_rights, bool check_not_in_use, bool skip_build, bool quiet)
Definition: indexcmds.c:542
#define RelFileNumberIsValid(relnumber)
Definition: relpath.h:27
void RelationPreserveStorage(RelFileLocator rlocator, bool atCommit)
Definition: storage.c:252
SubTransactionId rd_firstRelfilelocatorSubid
Definition: rel.h:106
SubTransactionId rd_createSubid
Definition: rel.h:103
RelFileLocator rd_locator
Definition: rel.h:57

References Assert(), DefineIndex(), index_close(), index_open(), InvalidOid, IsA, NoLock, ObjectAddress::objectId, RelationData::rd_createSubid, RelationData::rd_firstRelfilelocatorSubid, RelationData::rd_locator, RelationGetRelid, RelationPreserveStorage(), RelFileNumberIsValid, AlteredTableInfo::rewrite, and stmt.

Referenced by ATExecCmd().

◆ ATExecAddIndexConstraint()

static ObjectAddress ATExecAddIndexConstraint ( AlteredTableInfo tab,
Relation  rel,
IndexStmt stmt,
LOCKMODE  lockmode 
)
static

Definition at line 9669 of file tablecmds.c.

9671{
9672 Oid index_oid = stmt->indexOid;
9673 Relation indexRel;
9674 char *indexName;
9675 IndexInfo *indexInfo;
9676 char *constraintName;
9677 char constraintType;
9678 ObjectAddress address;
9679 bits16 flags;
9680
9682 Assert(OidIsValid(index_oid));
9683 Assert(stmt->isconstraint);
9684
9685 /*
9686 * Doing this on partitioned tables is not a simple feature to implement,
9687 * so let's punt for now.
9688 */
9689 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9690 ereport(ERROR,
9691 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9692 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9693
9694 indexRel = index_open(index_oid, AccessShareLock);
9695
9696 indexName = pstrdup(RelationGetRelationName(indexRel));
9697
9698 indexInfo = BuildIndexInfo(indexRel);
9699
9700 /* this should have been checked at parse time */
9701 if (!indexInfo->ii_Unique)
9702 elog(ERROR, "index \"%s\" is not unique", indexName);
9703
9704 /*
9705 * Determine name to assign to constraint. We require a constraint to
9706 * have the same name as the underlying index; therefore, use the index's
9707 * existing name as the default constraint name, and if the user
9708 * explicitly gives some other name for the constraint, rename the index
9709 * to match.
9710 */
9711 constraintName = stmt->idxname;
9712 if (constraintName == NULL)
9713 constraintName = indexName;
9714 else if (strcmp(constraintName, indexName) != 0)
9715 {
9717 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9718 indexName, constraintName)));
9719 RenameRelationInternal(index_oid, constraintName, false, true);
9720 }
9721
9722 /* Extra checks needed if making primary key */
9723 if (stmt->primary)
9724 index_check_primary_key(rel, indexInfo, true, stmt);
9725
9726 /* Note we currently don't support EXCLUSION constraints here */
9727 if (stmt->primary)
9728 constraintType = CONSTRAINT_PRIMARY;
9729 else
9730 constraintType = CONSTRAINT_UNIQUE;
9731
9732 /* Create the catalog entries for the constraint */
9735 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9736 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9738
9739 address = index_constraint_create(rel,
9740 index_oid,
9741 InvalidOid,
9742 indexInfo,
9743 constraintName,
9744 constraintType,
9745 flags,
9747 false); /* is_internal */
9748
9749 index_close(indexRel, NoLock);
9750
9751 return address;
9752}
uint16 bits16
Definition: c.h:510
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:2428
void index_check_primary_key(Relation heapRel, const IndexInfo *indexInfo, bool is_alter_table, const IndexStmt *stmt)
Definition: index.c:202
ObjectAddress index_constraint_create(Relation heapRelation, Oid indexRelationId, Oid parentConstraintId, const IndexInfo *indexInfo, const char *constraintName, char constraintType, bits16 constr_flags, bool allow_system_table_mods, bool is_internal)
Definition: index.c:1885
#define INDEX_CONSTR_CREATE_UPDATE_INDEX
Definition: index.h:94
#define INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
Definition: index.h:95
#define INDEX_CONSTR_CREATE_DEFERRABLE
Definition: index.h:92
#define INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
Definition: index.h:91
#define INDEX_CONSTR_CREATE_INIT_DEFERRED
Definition: index.h:93
bool ii_Unique
Definition: execnodes.h:209
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:4260

References AccessShareLock, allowSystemTableMods, Assert(), BuildIndexInfo(), elog, ereport, errcode(), errmsg(), ERROR, IndexInfo::ii_Unique, index_check_primary_key(), index_close(), INDEX_CONSTR_CREATE_DEFERRABLE, INDEX_CONSTR_CREATE_INIT_DEFERRED, INDEX_CONSTR_CREATE_MARK_AS_PRIMARY, INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS, INDEX_CONSTR_CREATE_UPDATE_INDEX, index_constraint_create(), index_open(), InvalidOid, IsA, NoLock, NOTICE, OidIsValid, pstrdup(), RelationData::rd_rel, RelationGetRelationName, RenameRelationInternal(), and stmt.

Referenced by ATExecCmd().

◆ ATExecAddInherit()

static ObjectAddress ATExecAddInherit ( Relation  child_rel,
RangeVar parent,
LOCKMODE  lockmode 
)
static

Definition at line 17184 of file tablecmds.c.

17185{
17186 Relation parent_rel;
17187 List *children;
17188 ObjectAddress address;
17189 const char *trigger_name;
17190
17191 /*
17192 * A self-exclusive lock is needed here. See the similar case in
17193 * MergeAttributes() for a full explanation.
17194 */
17195 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17196
17197 /*
17198 * Must be owner of both parent and child -- child was checked by
17199 * ATSimplePermissions call in ATPrepCmd
17200 */
17203
17204 /* Permanent rels cannot inherit from temporary ones */
17205 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17206 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17207 ereport(ERROR,
17208 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17209 errmsg("cannot inherit from temporary relation \"%s\"",
17210 RelationGetRelationName(parent_rel))));
17211
17212 /* If parent rel is temp, it must belong to this session */
17213 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17214 !parent_rel->rd_islocaltemp)
17215 ereport(ERROR,
17216 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17217 errmsg("cannot inherit from temporary relation of another session")));
17218
17219 /* Ditto for the child */
17220 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17221 !child_rel->rd_islocaltemp)
17222 ereport(ERROR,
17223 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17224 errmsg("cannot inherit to temporary relation of another session")));
17225
17226 /* Prevent partitioned tables from becoming inheritance parents */
17227 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17228 ereport(ERROR,
17229 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17230 errmsg("cannot inherit from partitioned table \"%s\"",
17231 parent->relname)));
17232
17233 /* Likewise for partitions */
17234 if (parent_rel->rd_rel->relispartition)
17235 ereport(ERROR,
17236 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17237 errmsg("cannot inherit from a partition")));
17238
17239 /*
17240 * Prevent circularity by seeing if proposed parent inherits from child.
17241 * (In particular, this disallows making a rel inherit from itself.)
17242 *
17243 * This is not completely bulletproof because of race conditions: in
17244 * multi-level inheritance trees, someone else could concurrently be
17245 * making another inheritance link that closes the loop but does not join
17246 * either of the rels we have locked. Preventing that seems to require
17247 * exclusive locks on the entire inheritance tree, which is a cure worse
17248 * than the disease. find_all_inheritors() will cope with circularity
17249 * anyway, so don't sweat it too much.
17250 *
17251 * We use weakest lock we can on child's children, namely AccessShareLock.
17252 */
17253 children = find_all_inheritors(RelationGetRelid(child_rel),
17254 AccessShareLock, NULL);
17255
17256 if (list_member_oid(children, RelationGetRelid(parent_rel)))
17257 ereport(ERROR,
17258 (errcode(ERRCODE_DUPLICATE_TABLE),
17259 errmsg("circular inheritance not allowed"),
17260 errdetail("\"%s\" is already a child of \"%s\".",
17261 parent->relname,
17262 RelationGetRelationName(child_rel))));
17263
17264 /*
17265 * If child_rel has row-level triggers with transition tables, we
17266 * currently don't allow it to become an inheritance child. See also
17267 * prohibitions in ATExecAttachPartition() and CreateTrigger().
17268 */
17269 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17270 if (trigger_name != NULL)
17271 ereport(ERROR,
17272 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17273 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17274 trigger_name, RelationGetRelationName(child_rel)),
17275 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17276
17277 /* OK to create inheritance */
17278 CreateInheritance(child_rel, parent_rel, false);
17279
17280 ObjectAddressSet(address, RelationRelationId,
17281 RelationGetRelid(parent_rel));
17282
17283 /* keep our lock on the parent relation until commit */
17284 table_close(parent_rel, NoLock);
17285
17286 return address;
17287}
char * relname
Definition: primnodes.h:83
TriggerDesc * trigdesc
Definition: rel.h:117
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17297
const char * FindTriggerIncompatibleWithInheritance(TriggerDesc *trigdesc)
Definition: trigger.c:2277

References AccessShareLock, AT_AddInherit, ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, CreateInheritance(), ereport, errcode(), errdetail(), errmsg(), ERROR, find_all_inheritors(), FindTriggerIncompatibleWithInheritance(), list_member_oid(), NoLock, ObjectAddressSet, RelationData::rd_islocaltemp, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RangeVar::relname, ShareUpdateExclusiveLock, table_close(), table_openrv(), and RelationData::trigdesc.

Referenced by ATExecCmd().

◆ ATExecAddOf()

static ObjectAddress ATExecAddOf ( Relation  rel,
const TypeName ofTypename,
LOCKMODE  lockmode 
)
static

Definition at line 18139 of file tablecmds.c.

18140{
18141 Oid relid = RelationGetRelid(rel);
18142 Type typetuple;
18143 Form_pg_type typeform;
18144 Oid typeid;
18145 Relation inheritsRelation,
18146 relationRelation;
18147 SysScanDesc scan;
18149 AttrNumber table_attno,
18150 type_attno;
18151 TupleDesc typeTupleDesc,
18152 tableTupleDesc;
18153 ObjectAddress tableobj,
18154 typeobj;
18155 HeapTuple classtuple;
18156
18157 /* Validate the type. */
18158 typetuple = typenameType(NULL, ofTypename, NULL);
18159 check_of_type(typetuple);
18160 typeform = (Form_pg_type) GETSTRUCT(typetuple);
18161 typeid = typeform->oid;
18162
18163 /* Fail if the table has any inheritance parents. */
18164 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18166 Anum_pg_inherits_inhrelid,
18167 BTEqualStrategyNumber, F_OIDEQ,
18168 ObjectIdGetDatum(relid));
18169 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18170 true, NULL, 1, &key);
18172 ereport(ERROR,
18173 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18174 errmsg("typed tables cannot inherit")));
18175 systable_endscan(scan);
18176 table_close(inheritsRelation, AccessShareLock);
18177
18178 /*
18179 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18180 * require that the order also match. However, attnotnull need not match.
18181 */
18182 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18183 tableTupleDesc = RelationGetDescr(rel);
18184 table_attno = 1;
18185 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18186 {
18187 Form_pg_attribute type_attr,
18188 table_attr;
18189 const char *type_attname,
18190 *table_attname;
18191
18192 /* Get the next non-dropped type attribute. */
18193 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18194 if (type_attr->attisdropped)
18195 continue;
18196 type_attname = NameStr(type_attr->attname);
18197
18198 /* Get the next non-dropped table attribute. */
18199 do
18200 {
18201 if (table_attno > tableTupleDesc->natts)
18202 ereport(ERROR,
18203 (errcode(ERRCODE_DATATYPE_MISMATCH),
18204 errmsg("table is missing column \"%s\"",
18205 type_attname)));
18206 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18207 table_attno++;
18208 } while (table_attr->attisdropped);
18209 table_attname = NameStr(table_attr->attname);
18210
18211 /* Compare name. */
18212 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18213 ereport(ERROR,
18214 (errcode(ERRCODE_DATATYPE_MISMATCH),
18215 errmsg("table has column \"%s\" where type requires \"%s\"",
18216 table_attname, type_attname)));
18217
18218 /* Compare type. */
18219 if (table_attr->atttypid != type_attr->atttypid ||
18220 table_attr->atttypmod != type_attr->atttypmod ||
18221 table_attr->attcollation != type_attr->attcollation)
18222 ereport(ERROR,
18223 (errcode(ERRCODE_DATATYPE_MISMATCH),
18224 errmsg("table \"%s\" has different type for column \"%s\"",
18225 RelationGetRelationName(rel), type_attname)));
18226 }
18227 ReleaseTupleDesc(typeTupleDesc);
18228
18229 /* Any remaining columns at the end of the table had better be dropped. */
18230 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18231 {
18232 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18233 table_attno - 1);
18234
18235 if (!table_attr->attisdropped)
18236 ereport(ERROR,
18237 (errcode(ERRCODE_DATATYPE_MISMATCH),
18238 errmsg("table has extra column \"%s\"",
18239 NameStr(table_attr->attname))));
18240 }
18241
18242 /* If the table was already typed, drop the existing dependency. */
18243 if (rel->rd_rel->reloftype)
18244 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18246
18247 /* Record a dependency on the new type. */
18248 tableobj.classId = RelationRelationId;
18249 tableobj.objectId = relid;
18250 tableobj.objectSubId = 0;
18251 typeobj.classId = TypeRelationId;
18252 typeobj.objectId = typeid;
18253 typeobj.objectSubId = 0;
18254 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18255
18256 /* Update pg_class.reloftype */
18257 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18258 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18259 if (!HeapTupleIsValid(classtuple))
18260 elog(ERROR, "cache lookup failed for relation %u", relid);
18261 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18262 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18263
18264 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18265
18266 heap_freetuple(classtuple);
18267 table_close(relationRelation, RowExclusiveLock);
18268
18269 ReleaseSysCache(typetuple);
18270
18271 return typeobj;
18272}
Type typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
Definition: parse_type.c:264
#define NAMEDATALEN
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid, DependencyType deptype)
Definition: tablecmds.c:18087
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:7133
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:219
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1922

References AccessShareLock, BTEqualStrategyNumber, CatalogTupleUpdate(), check_of_type(), ObjectAddress::classId, DEPENDENCY_NORMAL, drop_parent_dependency(), elog, ereport, errcode(), errmsg(), ERROR, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, sort-test::key, lookup_rowtype_tupdesc(), NAMEDATALEN, NameStr, TupleDescData::natts, ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, RelationData::rd_rel, recordDependencyOn(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), ReleaseTupleDesc, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopy1, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), table_open(), TupleDescAttr(), and typenameType().

Referenced by ATExecCmd().

◆ ATExecAddStatistics()

static ObjectAddress ATExecAddStatistics ( AlteredTableInfo tab,
Relation  rel,
CreateStatsStmt stmt,
bool  is_rebuild,
LOCKMODE  lockmode 
)
static

Definition at line 9648 of file tablecmds.c.

9650{
9651 ObjectAddress address;
9652
9654
9655 /* The CreateStatsStmt has already been through transformStatsStmt */
9656 Assert(stmt->transformed);
9657
9658 address = CreateStatistics(stmt);
9659
9660 return address;
9661}
ObjectAddress CreateStatistics(CreateStatsStmt *stmt)
Definition: statscmds.c:62

References Assert(), CreateStatistics(), IsA, and stmt.

Referenced by ATExecCmd().

◆ ATExecAlterColumnGenericOptions()

static ObjectAddress ATExecAlterColumnGenericOptions ( Relation  rel,
const char *  colName,
List options,
LOCKMODE  lockmode 
)
static

Definition at line 15878 of file tablecmds.c.

15882{
15883 Relation ftrel;
15884 Relation attrel;
15885 ForeignServer *server;
15886 ForeignDataWrapper *fdw;
15887 HeapTuple tuple;
15888 HeapTuple newtuple;
15889 bool isnull;
15890 Datum repl_val[Natts_pg_attribute];
15891 bool repl_null[Natts_pg_attribute];
15892 bool repl_repl[Natts_pg_attribute];
15893 Datum datum;
15894 Form_pg_foreign_table fttableform;
15895 Form_pg_attribute atttableform;
15897 ObjectAddress address;
15898
15899 if (options == NIL)
15900 return InvalidObjectAddress;
15901
15902 /* First, determine FDW validator associated to the foreign table. */
15903 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15904 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15905 if (!HeapTupleIsValid(tuple))
15906 ereport(ERROR,
15907 (errcode(ERRCODE_UNDEFINED_OBJECT),
15908 errmsg("foreign table \"%s\" does not exist",
15910 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15911 server = GetForeignServer(fttableform->ftserver);
15912 fdw = GetForeignDataWrapper(server->fdwid);
15913
15915 ReleaseSysCache(tuple);
15916
15917 attrel = table_open(AttributeRelationId, RowExclusiveLock);
15918 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15919 if (!HeapTupleIsValid(tuple))
15920 ereport(ERROR,
15921 (errcode(ERRCODE_UNDEFINED_COLUMN),
15922 errmsg("column \"%s\" of relation \"%s\" does not exist",
15923 colName, RelationGetRelationName(rel))));
15924
15925 /* Prevent them from altering a system attribute */
15926 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15927 attnum = atttableform->attnum;
15928 if (attnum <= 0)
15929 ereport(ERROR,
15930 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15931 errmsg("cannot alter system column \"%s\"", colName)));
15932
15933
15934 /* Initialize buffers for new tuple values */
15935 memset(repl_val, 0, sizeof(repl_val));
15936 memset(repl_null, false, sizeof(repl_null));
15937 memset(repl_repl, false, sizeof(repl_repl));
15938
15939 /* Extract the current options */
15940 datum = SysCacheGetAttr(ATTNAME,
15941 tuple,
15942 Anum_pg_attribute_attfdwoptions,
15943 &isnull);
15944 if (isnull)
15945 datum = PointerGetDatum(NULL);
15946
15947 /* Transform the options */
15948 datum = transformGenericOptions(AttributeRelationId,
15949 datum,
15950 options,
15951 fdw->fdwvalidator);
15952
15953 if (PointerIsValid(DatumGetPointer(datum)))
15954 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
15955 else
15956 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
15957
15958 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
15959
15960 /* Everything looks good - update the tuple */
15961
15962 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
15963 repl_val, repl_null, repl_repl);
15964
15965 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
15966
15967 InvokeObjectPostAlterHook(RelationRelationId,
15968 RelationGetRelid(rel),
15969 atttableform->attnum);
15970 ObjectAddressSubSet(address, RelationRelationId,
15971 RelationGetRelid(rel), attnum);
15972
15973 ReleaseSysCache(tuple);
15974
15976
15977 heap_freetuple(newtuple);
15978
15979 return address;
15980}
#define PointerIsValid(pointer)
Definition: c.h:734
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition: foreign.c:37
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:111
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
Definition: foreigncmds.c:110
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition: heaptuple.c:1210
FormData_pg_foreign_table * Form_pg_foreign_table
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
Oid rd_id
Definition: rel.h:113
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:600
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition: syscache.c:480

References AccessShareLock, attnum, CatalogTupleUpdate(), DatumGetPointer(), ereport, errcode(), errmsg(), ERROR, ForeignServer::fdwid, ForeignDataWrapper::fdwvalidator, GetForeignDataWrapper(), GetForeignServer(), GETSTRUCT(), heap_freetuple(), heap_modify_tuple(), HeapTupleIsValid, InvalidObjectAddress, InvokeObjectPostAlterHook, NIL, ObjectAddressSubSet, ObjectIdGetDatum(), PointerGetDatum(), PointerIsValid, RelationData::rd_id, RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), SearchSysCacheAttName(), SysCacheGetAttr(), HeapTupleData::t_self, table_close(), table_open(), and transformGenericOptions().

Referenced by ATExecCmd().

◆ ATExecAlterColumnType()

static ObjectAddress ATExecAlterColumnType ( AlteredTableInfo tab,
Relation  rel,
AlterTableCmd cmd,
LOCKMODE  lockmode 
)
static

Definition at line 14672 of file tablecmds.c.

14674{
14675 char *colName = cmd->name;
14676 ColumnDef *def = (ColumnDef *) cmd->def;
14677 TypeName *typeName = def->typeName;
14678 HeapTuple heapTup;
14679 Form_pg_attribute attTup,
14680 attOldTup;
14682 HeapTuple typeTuple;
14683 Form_pg_type tform;
14684 Oid targettype;
14685 int32 targettypmod;
14686 Oid targetcollid;
14687 Node *defaultexpr;
14688 Relation attrelation;
14689 Relation depRel;
14690 ScanKeyData key[3];
14691 SysScanDesc scan;
14692 HeapTuple depTup;
14693 ObjectAddress address;
14694
14695 /*
14696 * Clear all the missing values if we're rewriting the table, since this
14697 * renders them pointless.
14698 */
14699 if (tab->rewrite)
14700 {
14701 Relation newrel;
14702
14703 newrel = table_open(RelationGetRelid(rel), NoLock);
14704 RelationClearMissing(newrel);
14705 relation_close(newrel, NoLock);
14706 /* make sure we don't conflict with later attribute modifications */
14708 }
14709
14710 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14711
14712 /* Look up the target column */
14713 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14714 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14715 ereport(ERROR,
14716 (errcode(ERRCODE_UNDEFINED_COLUMN),
14717 errmsg("column \"%s\" of relation \"%s\" does not exist",
14718 colName, RelationGetRelationName(rel))));
14719 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14720 attnum = attTup->attnum;
14721 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14722
14723 /* Check for multiple ALTER TYPE on same column --- can't cope */
14724 if (attTup->atttypid != attOldTup->atttypid ||
14725 attTup->atttypmod != attOldTup->atttypmod)
14726 ereport(ERROR,
14727 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14728 errmsg("cannot alter type of column \"%s\" twice",
14729 colName)));
14730
14731 /* Look up the target type (should not fail, since prep found it) */
14732 typeTuple = typenameType(NULL, typeName, &targettypmod);
14733 tform = (Form_pg_type) GETSTRUCT(typeTuple);
14734 targettype = tform->oid;
14735 /* And the collation */
14736 targetcollid = GetColumnDefCollation(NULL, def, targettype);
14737
14738 /*
14739 * If there is a default expression for the column, get it and ensure we
14740 * can coerce it to the new datatype. (We must do this before changing
14741 * the column type, because build_column_default itself will try to
14742 * coerce, and will not issue the error message we want if it fails.)
14743 *
14744 * We remove any implicit coercion steps at the top level of the old
14745 * default expression; this has been agreed to satisfy the principle of
14746 * least surprise. (The conversion to the new column type should act like
14747 * it started from what the user sees as the stored expression, and the
14748 * implicit coercions aren't going to be shown.)
14749 */
14750 if (attTup->atthasdef)
14751 {
14752 defaultexpr = build_column_default(rel, attnum);
14753 Assert(defaultexpr);
14754 defaultexpr = strip_implicit_coercions(defaultexpr);
14755 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14756 defaultexpr, exprType(defaultexpr),
14757 targettype, targettypmod,
14760 -1);
14761 if (defaultexpr == NULL)
14762 {
14763 if (attTup->attgenerated)
14764 ereport(ERROR,
14765 (errcode(ERRCODE_DATATYPE_MISMATCH),
14766 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14767 colName, format_type_be(targettype))));
14768 else
14769 ereport(ERROR,
14770 (errcode(ERRCODE_DATATYPE_MISMATCH),
14771 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14772 colName, format_type_be(targettype))));
14773 }
14774 }
14775 else
14776 defaultexpr = NULL;
14777
14778 /*
14779 * Find everything that depends on the column (constraints, indexes, etc),
14780 * and record enough information to let us recreate the objects.
14781 *
14782 * The actual recreation does not happen here, but only after we have
14783 * performed all the individual ALTER TYPE operations. We have to save
14784 * the info before executing ALTER TYPE, though, else the deparser will
14785 * get confused.
14786 */
14788
14789 /*
14790 * Now scan for dependencies of this column on other things. The only
14791 * things we should find are the dependency on the column datatype and
14792 * possibly a collation dependency. Those can be removed.
14793 */
14794 depRel = table_open(DependRelationId, RowExclusiveLock);
14795
14796 ScanKeyInit(&key[0],
14797 Anum_pg_depend_classid,
14798 BTEqualStrategyNumber, F_OIDEQ,
14799 ObjectIdGetDatum(RelationRelationId));
14800 ScanKeyInit(&key[1],
14801 Anum_pg_depend_objid,
14802 BTEqualStrategyNumber, F_OIDEQ,
14804 ScanKeyInit(&key[2],
14805 Anum_pg_depend_objsubid,
14806 BTEqualStrategyNumber, F_INT4EQ,
14808
14809 scan = systable_beginscan(depRel, DependDependerIndexId, true,
14810 NULL, 3, key);
14811
14812 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14813 {
14814 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14815 ObjectAddress foundObject;
14816
14817 foundObject.classId = foundDep->refclassid;
14818 foundObject.objectId = foundDep->refobjid;
14819 foundObject.objectSubId = foundDep->refobjsubid;
14820
14821 if (foundDep->deptype != DEPENDENCY_NORMAL)
14822 elog(ERROR, "found unexpected dependency type '%c'",
14823 foundDep->deptype);
14824 if (!(foundDep->refclassid == TypeRelationId &&
14825 foundDep->refobjid == attTup->atttypid) &&
14826 !(foundDep->refclassid == CollationRelationId &&
14827 foundDep->refobjid == attTup->attcollation))
14828 elog(ERROR, "found unexpected dependency for column: %s",
14829 getObjectDescription(&foundObject, false));
14830
14831 CatalogTupleDelete(depRel, &depTup->t_self);
14832 }
14833
14834 systable_endscan(scan);
14835
14837
14838 /*
14839 * Here we go --- change the recorded column type and collation. (Note
14840 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14841 * fix up the missing value if any.
14842 */
14843 if (attTup->atthasmissing)
14844 {
14845 Datum missingval;
14846 bool missingNull;
14847
14848 /* if rewrite is true the missing value should already be cleared */
14849 Assert(tab->rewrite == 0);
14850
14851 /* Get the missing value datum */
14852 missingval = heap_getattr(heapTup,
14853 Anum_pg_attribute_attmissingval,
14854 attrelation->rd_att,
14855 &missingNull);
14856
14857 /* if it's a null array there is nothing to do */
14858
14859 if (!missingNull)
14860 {
14861 /*
14862 * Get the datum out of the array and repack it in a new array
14863 * built with the new type data. We assume that since the table
14864 * doesn't need rewriting, the actual Datum doesn't need to be
14865 * changed, only the array metadata.
14866 */
14867
14868 int one = 1;
14869 bool isNull;
14870 Datum valuesAtt[Natts_pg_attribute] = {0};
14871 bool nullsAtt[Natts_pg_attribute] = {0};
14872 bool replacesAtt[Natts_pg_attribute] = {0};
14873 HeapTuple newTup;
14874
14875 missingval = array_get_element(missingval,
14876 1,
14877 &one,
14878 0,
14879 attTup->attlen,
14880 attTup->attbyval,
14881 attTup->attalign,
14882 &isNull);
14883 missingval = PointerGetDatum(construct_array(&missingval,
14884 1,
14885 targettype,
14886 tform->typlen,
14887 tform->typbyval,
14888 tform->typalign));
14889
14890 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14891 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14892 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14893
14894 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14895 valuesAtt, nullsAtt, replacesAtt);
14896 heap_freetuple(heapTup);
14897 heapTup = newTup;
14898 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14899 }
14900 }
14901
14902 attTup->atttypid = targettype;
14903 attTup->atttypmod = targettypmod;
14904 attTup->attcollation = targetcollid;
14905 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14906 ereport(ERROR,
14907 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14908 errmsg("too many array dimensions"));
14909 attTup->attndims = list_length(typeName->arrayBounds);
14910 attTup->attlen = tform->typlen;
14911 attTup->attbyval = tform->typbyval;
14912 attTup->attalign = tform->typalign;
14913 attTup->attstorage = tform->typstorage;
14914 attTup->attcompression = InvalidCompressionMethod;
14915
14916 ReleaseSysCache(typeTuple);
14917
14918 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14919
14920 table_close(attrelation, RowExclusiveLock);
14921
14922 /* Install dependencies on new datatype and collation */
14925
14926 /*
14927 * Drop any pg_statistic entry for the column, since it's now wrong type
14928 */
14930
14931 InvokeObjectPostAlterHook(RelationRelationId,
14932 RelationGetRelid(rel), attnum);
14933
14934 /*
14935 * Update the default, if present, by brute force --- remove and re-add
14936 * the default. Probably unsafe to take shortcuts, since the new version
14937 * may well have additional dependencies. (It's okay to do this now,
14938 * rather than after other ALTER TYPE commands, since the default won't
14939 * depend on other column types.)
14940 */
14941 if (defaultexpr)
14942 {
14943 /*
14944 * If it's a GENERATED default, drop its dependency records, in
14945 * particular its INTERNAL dependency on the column, which would
14946 * otherwise cause dependency.c to refuse to perform the deletion.
14947 */
14948 if (attTup->attgenerated)
14949 {
14950 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14951
14952 if (!OidIsValid(attrdefoid))
14953 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14954 RelationGetRelid(rel), attnum);
14955 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14956 }
14957
14958 /*
14959 * Make updates-so-far visible, particularly the new pg_attribute row
14960 * which will be updated again.
14961 */
14963
14964 /*
14965 * We use RESTRICT here for safety, but at present we do not expect
14966 * anything to depend on the default.
14967 */
14969 true);
14970
14971 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
14972 }
14973
14974 ObjectAddressSubSet(address, RelationRelationId,
14975 RelationGetRelid(rel), attnum);
14976
14977 /* Cleanup */
14978 heap_freetuple(heapTup);
14979
14980 return address;
14981}
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3361
Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
Definition: arrayfuncs.c:1820
#define PG_INT16_MAX
Definition: c.h:557
void RelationClearMissing(Relation rel)
Definition: heap.c:1954
void RemoveStatistics(Oid relid, AttrNumber attnum)
Definition: heap.c:3398
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:904
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
Node * strip_implicit_coercions(Node *node)
Definition: nodeFuncs.c:705
char * getObjectDescription(const ObjectAddress *object, bool missing_ok)
@ DROP_RESTRICT
Definition: parsenodes.h:2390
Oid StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr, bool is_internal)
Definition: pg_attrdef.c:35
Oid GetAttrDefaultOid(Oid relid, AttrNumber attnum)
Definition: pg_attrdef.c:278
void RemoveAttrDefault(Oid relid, AttrNumber attnum, DropBehavior behavior, bool complain, bool internal)
Definition: pg_attrdef.c:152
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:301
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:217
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
Definition: tablecmds.c:14989
#define InvalidCompressionMethod

References add_column_collation_dependency(), add_column_datatype_dependency(), array_get_element(), Assert(), AT_AlterColumnType, attnum, BTEqualStrategyNumber, build_column_default(), CatalogTupleDelete(), CatalogTupleUpdate(), ObjectAddress::classId, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, CommandCounterIncrement(), construct_array(), AlterTableCmd::def, deleteDependencyRecordsFor(), DEPENDENCY_NORMAL, DROP_RESTRICT, elog, ereport, errcode(), errmsg(), ERROR, exprType(), format_type_be(), GetAttrDefaultOid(), GetColumnDefCollation(), getObjectDescription(), GETSTRUCT(), heap_freetuple(), heap_getattr(), heap_modify_tuple(), HeapTupleIsValid, if(), Int32GetDatum(), InvalidCompressionMethod, InvokeObjectPostAlterHook, sort-test::key, list_length(), AlterTableCmd::name, NoLock, ObjectAddressSubSet, ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, OidIsValid, AlteredTableInfo::oldDesc, PG_INT16_MAX, PointerGetDatum(), relation_close(), RelationClearMissing(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RememberAllDependentForRebuilding(), RemoveAttrDefault(), RemoveStatistics(), AlteredTableInfo::rewrite, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopyAttName(), StoreAttrDefault(), strip_implicit_coercions(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TupleDescAttr(), ColumnDef::typeName, and typenameType().

Referenced by ATExecCmd().

◆ ATExecAlterConstraint()

static ObjectAddress ATExecAlterConstraint ( List **  wqueue,
Relation  rel,
ATAlterConstraint cmdcon,
bool  recurse,
LOCKMODE  lockmode 
)
static

Definition at line 12171 of file tablecmds.c.

12173{
12174 Relation conrel;
12175 Relation tgrel;
12176 SysScanDesc scan;
12177 ScanKeyData skey[3];
12178 HeapTuple contuple;
12179 Form_pg_constraint currcon;
12180 ObjectAddress address;
12181
12182 /*
12183 * Disallow altering ONLY a partitioned table, as it would make no sense.
12184 * This is okay for legacy inheritance.
12185 */
12186 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12187 ereport(ERROR,
12188 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12189 errmsg("constraint must be altered in child tables too"),
12190 errhint("Do not specify the ONLY keyword."));
12191
12192
12193 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12194 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12195
12196 /*
12197 * Find and check the target constraint
12198 */
12199 ScanKeyInit(&skey[0],
12200 Anum_pg_constraint_conrelid,
12201 BTEqualStrategyNumber, F_OIDEQ,
12203 ScanKeyInit(&skey[1],
12204 Anum_pg_constraint_contypid,
12205 BTEqualStrategyNumber, F_OIDEQ,
12207 ScanKeyInit(&skey[2],
12208 Anum_pg_constraint_conname,
12209 BTEqualStrategyNumber, F_NAMEEQ,
12210 CStringGetDatum(cmdcon->conname));
12211 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12212 true, NULL, 3, skey);
12213
12214 /* There can be at most one matching row */
12215 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12216 ereport(ERROR,
12217 (errcode(ERRCODE_UNDEFINED_OBJECT),
12218 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12219 cmdcon->conname, RelationGetRelationName(rel))));
12220
12221 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12222 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12223 ereport(ERROR,
12224 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12225 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12226 cmdcon->conname, RelationGetRelationName(rel))));
12227 if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12228 ereport(ERROR,
12229 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12230 errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12231 cmdcon->conname, RelationGetRelationName(rel))));
12232 if (cmdcon->alterInheritability &&
12233 currcon->contype != CONSTRAINT_NOTNULL)
12234 ereport(ERROR,
12235 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12236 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12237 cmdcon->conname, RelationGetRelationName(rel)));
12238
12239 /* Refuse to modify inheritability of inherited constraints */
12240 if (cmdcon->alterInheritability &&
12241 cmdcon->noinherit && currcon->coninhcount > 0)
12242 ereport(ERROR,
12243 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12244 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12245 NameStr(currcon->conname),
12247
12248 /*
12249 * If it's not the topmost constraint, raise an error.
12250 *
12251 * Altering a non-topmost constraint leaves some triggers untouched, since
12252 * they are not directly connected to this constraint; also, pg_dump would
12253 * ignore the deferrability status of the individual constraint, since it
12254 * only dumps topmost constraints. Avoid these problems by refusing this
12255 * operation and telling the user to alter the parent constraint instead.
12256 */
12257 if (OidIsValid(currcon->conparentid))
12258 {
12259 HeapTuple tp;
12260 Oid parent = currcon->conparentid;
12261 char *ancestorname = NULL;
12262 char *ancestortable = NULL;
12263
12264 /* Loop to find the topmost constraint */
12265 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12266 {
12268
12269 /* If no parent, this is the constraint we want */
12270 if (!OidIsValid(contup->conparentid))
12271 {
12272 ancestorname = pstrdup(NameStr(contup->conname));
12273 ancestortable = get_rel_name(contup->conrelid);
12274 ReleaseSysCache(tp);
12275 break;
12276 }
12277
12278 parent = contup->conparentid;
12279 ReleaseSysCache(tp);
12280 }
12281
12282 ereport(ERROR,
12283 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12284 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12285 cmdcon->conname, RelationGetRelationName(rel)),
12286 ancestorname && ancestortable ?
12287 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12288 cmdcon->conname, ancestorname, ancestortable) : 0,
12289 errhint("You may alter the constraint it derives from instead.")));
12290 }
12291
12292 address = InvalidObjectAddress;
12293
12294 /*
12295 * Do the actual catalog work, and recurse if necessary.
12296 */
12297 if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12298 contuple, recurse, lockmode))
12299 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12300
12301 systable_endscan(scan);
12302
12305
12306 return address;
12307}
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:355
static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12314

References ATAlterConstraint::alterDeferrability, ATAlterConstraint::alterEnforceability, ATAlterConstraint::alterInheritability, ATExecAlterConstraintInternal(), BTEqualStrategyNumber, ATAlterConstraint::conname, CStringGetDatum(), ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, get_rel_name(), GETSTRUCT(), HeapTupleIsValid, InvalidObjectAddress, InvalidOid, NameStr, ATAlterConstraint::noinherit, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, pstrdup(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, ScanKeyInit(), SearchSysCache1(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecAlterConstraintInternal()

static bool ATExecAlterConstraintInternal ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Relation  tgrel,
Relation  rel,
HeapTuple  contuple,
bool  recurse,
LOCKMODE  lockmode 
)
static

Definition at line 12314 of file tablecmds.c.

12318{
12319 Form_pg_constraint currcon;
12320 bool changed = false;
12321 List *otherrelids = NIL;
12322
12323 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12324
12325 /*
12326 * Do the catalog work for the enforceability or deferrability change,
12327 * recurse if necessary.
12328 *
12329 * Note that even if deferrability is requested to be altered along with
12330 * enforceability, we don't need to explicitly update multiple entries in
12331 * pg_trigger related to deferrability.
12332 *
12333 * Modifying enforceability involves either creating or dropping the
12334 * trigger, during which the deferrability setting will be adjusted
12335 * automatically.
12336 */
12337 if (cmdcon->alterEnforceability &&
12338 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12339 currcon->conrelid, currcon->confrelid,
12340 contuple, lockmode, InvalidOid,
12342 changed = true;
12343
12344 else if (cmdcon->alterDeferrability &&
12345 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12346 contuple, recurse, &otherrelids,
12347 lockmode))
12348 {
12349 /*
12350 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12351 * the relations having the constraint itself; here we also invalidate
12352 * for relations that have any triggers that are part of the
12353 * constraint.
12354 */
12355 foreach_oid(relid, otherrelids)
12357
12358 changed = true;
12359 }
12360
12361 /*
12362 * Do the catalog work for the inheritability change.
12363 */
12364 if (cmdcon->alterInheritability &&
12365 ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12366 lockmode))
12367 changed = true;
12368
12369 return changed;
12370}
#define foreach_oid(var, lst)
Definition: pg_list.h:471
static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12585

References ATAlterConstraint::alterDeferrability, ATAlterConstraint::alterEnforceability, ATAlterConstraint::alterInheritability, ATExecAlterConstrDeferrability(), ATExecAlterConstrEnforceability(), ATExecAlterConstrInheritability(), CacheInvalidateRelcacheByRelid(), foreach_oid, GETSTRUCT(), InvalidOid, and NIL.

Referenced by ATExecAlterConstraint().

◆ ATExecAlterConstrDeferrability()

static bool ATExecAlterConstrDeferrability ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Relation  tgrel,
Relation  rel,
HeapTuple  contuple,
bool  recurse,
List **  otherrelids,
LOCKMODE  lockmode 
)
static

Definition at line 12528 of file tablecmds.c.

12532{
12533 Form_pg_constraint currcon;
12534 Oid refrelid;
12535 bool changed = false;
12536
12537 /* since this function recurses, it could be driven to stack overflow */
12539
12540 Assert(cmdcon->alterDeferrability);
12541
12542 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12543 refrelid = currcon->confrelid;
12544
12545 /* Should be foreign key constraint */
12546 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12547
12548 /*
12549 * If called to modify a constraint that's already in the desired state,
12550 * silently do nothing.
12551 */
12552 if (currcon->condeferrable != cmdcon->deferrable ||
12553 currcon->condeferred != cmdcon->initdeferred)
12554 {
12555 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12556 changed = true;
12557
12558 /*
12559 * Now we need to update the multiple entries in pg_trigger that
12560 * implement the constraint.
12561 */
12562 AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12563 cmdcon->deferrable,
12564 cmdcon->initdeferred, otherrelids);
12565 }
12566
12567 /*
12568 * If the table at either end of the constraint is partitioned, we need to
12569 * handle every constraint that is a child of this one.
12570 */
12571 if (recurse && changed &&
12572 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12573 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12574 AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12575 contuple, recurse, otherrelids,
12576 lockmode);
12577
12578 return changed;
12579}
static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)
Definition: tablecmds.c:12824
static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12782
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
Definition: tablecmds.c:12664

References AlterConstrDeferrabilityRecurse(), AlterConstrTriggerDeferrability(), AlterConstrUpdateConstraintEntry(), ATAlterConstraint::alterDeferrability, Assert(), check_stack_depth(), ATAlterConstraint::deferrable, get_rel_relkind(), GETSTRUCT(), ATAlterConstraint::initdeferred, and RelationData::rd_rel.

Referenced by AlterConstrDeferrabilityRecurse(), and ATExecAlterConstraintInternal().

◆ ATExecAlterConstrEnforceability()

static bool ATExecAlterConstrEnforceability ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Relation  tgrel,
Oid  fkrelid,
Oid  pkrelid,
HeapTuple  contuple,
LOCKMODE  lockmode,
Oid  ReferencedParentDelTrigger,
Oid  ReferencedParentUpdTrigger,
Oid  ReferencingParentInsTrigger,
Oid  ReferencingParentUpdTrigger 
)
static

Definition at line 12385 of file tablecmds.c.

12393{
12394 Form_pg_constraint currcon;
12395 Oid conoid;
12396 Relation rel;
12397 bool changed = false;
12398
12399 /* Since this function recurses, it could be driven to stack overflow */
12401
12402 Assert(cmdcon->alterEnforceability);
12403
12404 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12405 conoid = currcon->oid;
12406
12407 /* Should be foreign key constraint */
12408 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12409
12410 rel = table_open(currcon->conrelid, lockmode);
12411
12412 if (currcon->conenforced != cmdcon->is_enforced)
12413 {
12414 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12415 changed = true;
12416 }
12417
12418 /* Drop triggers */
12419 if (!cmdcon->is_enforced)
12420 {
12421 /*
12422 * When setting a constraint to NOT ENFORCED, the constraint triggers
12423 * need to be dropped. Therefore, we must process the child relations
12424 * first, followed by the parent, to account for dependencies.
12425 */
12426 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12427 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12428 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12429 fkrelid, pkrelid, contuple,
12430 lockmode, InvalidOid, InvalidOid,
12432
12433 /* Drop all the triggers */
12435 }
12436 else if (changed) /* Create triggers */
12437 {
12438 Oid ReferencedDelTriggerOid = InvalidOid,
12439 ReferencedUpdTriggerOid = InvalidOid,
12440 ReferencingInsTriggerOid = InvalidOid,
12441 ReferencingUpdTriggerOid = InvalidOid;
12442
12443 /* Prepare the minimal information required for trigger creation. */
12444 Constraint *fkconstraint = makeNode(Constraint);
12445
12446 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12447 fkconstraint->fk_matchtype = currcon->confmatchtype;
12448 fkconstraint->fk_upd_action = currcon->confupdtype;
12449 fkconstraint->fk_del_action = currcon->confdeltype;
12450
12451 /* Create referenced triggers */
12452 if (currcon->conrelid == fkrelid)
12453 createForeignKeyActionTriggers(currcon->conrelid,
12454 currcon->confrelid,
12455 fkconstraint,
12456 conoid,
12457 currcon->conindid,
12458 ReferencedParentDelTrigger,
12459 ReferencedParentUpdTrigger,
12460 &ReferencedDelTriggerOid,
12461 &ReferencedUpdTriggerOid);
12462
12463 /* Create referencing triggers */
12464 if (currcon->confrelid == pkrelid)
12465 createForeignKeyCheckTriggers(currcon->conrelid,
12466 pkrelid,
12467 fkconstraint,
12468 conoid,
12469 currcon->conindid,
12470 ReferencingParentInsTrigger,
12471 ReferencingParentUpdTrigger,
12472 &ReferencingInsTriggerOid,
12473 &ReferencingUpdTriggerOid);
12474
12475 /*
12476 * Tell Phase 3 to check that the constraint is satisfied by existing
12477 * rows.
12478 */
12479 if (rel->rd_rel->relkind == RELKIND_RELATION)
12480 {
12481 AlteredTableInfo *tab;
12482 NewConstraint *newcon;
12483
12484 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12485 newcon->name = fkconstraint->conname;
12486 newcon->contype = CONSTR_FOREIGN;
12487 newcon->refrelid = currcon->confrelid;
12488 newcon->refindid = currcon->conindid;
12489 newcon->conid = currcon->oid;
12490 newcon->qual = (Node *) fkconstraint;
12491
12492 /* Find or create work queue entry for this table */
12493 tab = ATGetQueueEntry(wqueue, rel);
12494 tab->constraints = lappend(tab->constraints, newcon);
12495 }
12496
12497 /*
12498 * If the table at either end of the constraint is partitioned, we
12499 * need to recurse and create triggers for each constraint that is a
12500 * child of this one.
12501 */
12502 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12503 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12504 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12505 fkrelid, pkrelid, contuple,
12506 lockmode, ReferencedDelTriggerOid,
12507 ReferencedUpdTriggerOid,
12508 ReferencingInsTriggerOid,
12509 ReferencingUpdTriggerOid);
12510 }
12511
12512 table_close(rel, NoLock);
12513
12514 return changed;
12515}
static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
Definition: tablecmds.c:12733
static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)
Definition: tablecmds.c:11976

References AlterConstrEnforceabilityRecurse(), AlterConstrUpdateConstraintEntry(), ATAlterConstraint::alterEnforceability, Assert(), ATGetQueueEntry(), check_stack_depth(), NewConstraint::conid, Constraint::conname, CONSTR_FOREIGN, AlteredTableInfo::constraints, NewConstraint::contype, createForeignKeyActionTriggers(), createForeignKeyCheckTriggers(), DropForeignKeyConstraintTriggers(), Constraint::fk_del_action, Constraint::fk_matchtype, Constraint::fk_upd_action, get_rel_relkind(), GETSTRUCT(), InvalidOid, ATAlterConstraint::is_enforced, lappend(), makeNode, NewConstraint::name, NameStr, NoLock, palloc0(), pstrdup(), NewConstraint::qual, RelationData::rd_rel, NewConstraint::refindid, NewConstraint::refrelid, table_close(), and table_open().

Referenced by AlterConstrEnforceabilityRecurse(), and ATExecAlterConstraintInternal().

◆ ATExecAlterConstrInheritability()

static bool ATExecAlterConstrInheritability ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Relation  rel,
HeapTuple  contuple,
LOCKMODE  lockmode 
)
static

Definition at line 12585 of file tablecmds.c.

12588{
12589 Form_pg_constraint currcon;
12590 AttrNumber colNum;
12591 char *colName;
12592 List *children;
12593
12594 Assert(cmdcon->alterInheritability);
12595
12596 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12597
12598 /* The current implementation only works for NOT NULL constraints */
12599 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12600
12601 /*
12602 * If called to modify a constraint that's already in the desired state,
12603 * silently do nothing.
12604 */
12605 if (cmdcon->noinherit == currcon->connoinherit)
12606 return false;
12607
12608 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12610
12611 /* Fetch the column number and name */
12612 colNum = extractNotNullColumn(contuple);
12613 colName = get_attname(currcon->conrelid, colNum, false);
12614
12615 /*
12616 * Propagate the change to children. For this subcommand type we don't
12617 * recursively affect children, just the immediate level.
12618 */
12620 lockmode);
12621 foreach_oid(childoid, children)
12622 {
12623 ObjectAddress addr;
12624
12625 if (cmdcon->noinherit)
12626 {
12627 HeapTuple childtup;
12628 Form_pg_constraint childcon;
12629
12630 childtup = findNotNullConstraint(childoid, colName);
12631 if (!childtup)
12632 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12633 colName, childoid);
12634 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12635 Assert(childcon->coninhcount > 0);
12636 childcon->coninhcount--;
12637 childcon->conislocal = true;
12638 CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12639 heap_freetuple(childtup);
12640 }
12641 else
12642 {
12643 Relation childrel = table_open(childoid, NoLock);
12644
12645 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12646 colName, true, true, lockmode);
12647 if (OidIsValid(addr.objectId))
12649 table_close(childrel, NoLock);
12650 }
12651 }
12652
12653 return true;
12654}
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:919
HeapTuple findNotNullConstraint(Oid relid, const char *colname)
AttrNumber extractNotNullColumn(HeapTuple constrTup)
static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:7903

References AlterConstrUpdateConstraintEntry(), ATAlterConstraint::alterInheritability, Assert(), ATExecSetNotNull(), CatalogTupleUpdate(), CommandCounterIncrement(), elog, ERROR, extractNotNullColumn(), find_inheritance_children(), findNotNullConstraint(), foreach_oid, get_attname(), GETSTRUCT(), heap_freetuple(), NameStr, ATAlterConstraint::noinherit, NoLock, ObjectAddress::objectId, OidIsValid, RelationGetRelid, HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecAlterConstraintInternal().

◆ ATExecAttachPartition()

static ObjectAddress ATExecAttachPartition ( List **  wqueue,
Relation  rel,
PartitionCmd cmd,
AlterTableUtilityContext context 
)
static

Definition at line 20163 of file tablecmds.c.

20165{
20166 Relation attachrel,
20167 catalog;
20168 List *attachrel_children;
20169 List *partConstraint;
20170 SysScanDesc scan;
20171 ScanKeyData skey;
20172 AttrNumber attno;
20173 int natts;
20174 TupleDesc tupleDesc;
20175 ObjectAddress address;
20176 const char *trigger_name;
20177 Oid defaultPartOid;
20178 List *partBoundConstraint;
20179 ParseState *pstate = make_parsestate(NULL);
20180
20181 pstate->p_sourcetext = context->queryString;
20182
20183 /*
20184 * We must lock the default partition if one exists, because attaching a
20185 * new partition will change its partition constraint.
20186 */
20187 defaultPartOid =
20189 if (OidIsValid(defaultPartOid))
20190 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20191
20192 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20193
20194 /*
20195 * XXX I think it'd be a good idea to grab locks on all tables referenced
20196 * by FKs at this point also.
20197 */
20198
20199 /*
20200 * Must be owner of both parent and source table -- parent was checked by
20201 * ATSimplePermissions call in ATPrepCmd
20202 */
20205
20206 /* A partition can only have one parent */
20207 if (attachrel->rd_rel->relispartition)
20208 ereport(ERROR,
20209 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20210 errmsg("\"%s\" is already a partition",
20211 RelationGetRelationName(attachrel))));
20212
20213 if (OidIsValid(attachrel->rd_rel->reloftype))
20214 ereport(ERROR,
20215 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20216 errmsg("cannot attach a typed table as partition")));
20217
20218 /*
20219 * Table being attached should not already be part of inheritance; either
20220 * as a child table...
20221 */
20222 catalog = table_open(InheritsRelationId, AccessShareLock);
20223 ScanKeyInit(&skey,
20224 Anum_pg_inherits_inhrelid,
20225 BTEqualStrategyNumber, F_OIDEQ,
20227 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20228 NULL, 1, &skey);
20230 ereport(ERROR,
20231 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20232 errmsg("cannot attach inheritance child as partition")));
20233 systable_endscan(scan);
20234
20235 /* ...or as a parent table (except the case when it is partitioned) */
20236 ScanKeyInit(&skey,
20237 Anum_pg_inherits_inhparent,
20238 BTEqualStrategyNumber, F_OIDEQ,
20240 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20241 1, &skey);
20243 attachrel->rd_rel->relkind == RELKIND_RELATION)
20244 ereport(ERROR,
20245 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20246 errmsg("cannot attach inheritance parent as partition")));
20247 systable_endscan(scan);
20248 table_close(catalog, AccessShareLock);
20249
20250 /*
20251 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20252 * particular, this disallows making a rel a partition of itself.)
20253 *
20254 * We do that by checking if rel is a member of the list of attachrel's
20255 * partitions provided the latter is partitioned at all. We want to avoid
20256 * having to construct this list again, so we request the strongest lock
20257 * on all partitions. We need the strongest lock, because we may decide
20258 * to scan them if we find out that the table being attached (or its leaf
20259 * partitions) may contain rows that violate the partition constraint. If
20260 * the table has a constraint that would prevent such rows, which by
20261 * definition is present in all the partitions, we need not scan the
20262 * table, nor its partitions. But we cannot risk a deadlock by taking a
20263 * weaker lock now and the stronger one only when needed.
20264 */
20265 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20266 AccessExclusiveLock, NULL);
20267 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20268 ereport(ERROR,
20269 (errcode(ERRCODE_DUPLICATE_TABLE),
20270 errmsg("circular inheritance not allowed"),
20271 errdetail("\"%s\" is already a child of \"%s\".",
20273 RelationGetRelationName(attachrel))));
20274
20275 /* If the parent is permanent, so must be all of its partitions. */
20276 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20277 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20278 ereport(ERROR,
20279 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20280 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20282
20283 /* Temp parent cannot have a partition that is itself not a temp */
20284 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20285 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20286 ereport(ERROR,
20287 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20288 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20290
20291 /* If the parent is temp, it must belong to this session */
20292 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20293 !rel->rd_islocaltemp)
20294 ereport(ERROR,
20295 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20296 errmsg("cannot attach as partition of temporary relation of another session")));
20297
20298 /* Ditto for the partition */
20299 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20300 !attachrel->rd_islocaltemp)
20301 ereport(ERROR,
20302 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20303 errmsg("cannot attach temporary relation of another session as partition")));
20304
20305 /*
20306 * Check if attachrel has any identity columns or any columns that aren't
20307 * in the parent.
20308 */
20309 tupleDesc = RelationGetDescr(attachrel);
20310 natts = tupleDesc->natts;
20311 for (attno = 1; attno <= natts; attno++)
20312 {
20313 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20314 char *attributeName = NameStr(attribute->attname);
20315
20316 /* Ignore dropped */
20317 if (attribute->attisdropped)
20318 continue;
20319
20320 if (attribute->attidentity)
20321 ereport(ERROR,
20322 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20323 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20324 RelationGetRelationName(attachrel), attributeName),
20325 errdetail("The new partition may not contain an identity column."));
20326
20327 /* Try to find the column in parent (matching on column name) */
20328 if (!SearchSysCacheExists2(ATTNAME,
20330 CStringGetDatum(attributeName)))
20331 ereport(ERROR,
20332 (errcode(ERRCODE_DATATYPE_MISMATCH),
20333 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20334 RelationGetRelationName(attachrel), attributeName,
20336 errdetail("The new partition may contain only the columns present in parent.")));
20337 }
20338
20339 /*
20340 * If child_rel has row-level triggers with transition tables, we
20341 * currently don't allow it to become a partition. See also prohibitions
20342 * in ATExecAddInherit() and CreateTrigger().
20343 */
20344 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20345 if (trigger_name != NULL)
20346 ereport(ERROR,
20347 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20348 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20349 trigger_name, RelationGetRelationName(attachrel)),
20350 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20351
20352 /*
20353 * Check that the new partition's bound is valid and does not overlap any
20354 * of existing partitions of the parent - note that it does not return on
20355 * error.
20356 */
20358 cmd->bound, pstate);
20359
20360 /* OK to create inheritance. Rest of the checks performed there */
20361 CreateInheritance(attachrel, rel, true);
20362
20363 /* Update the pg_class entry. */
20364 StorePartitionBound(attachrel, rel, cmd->bound);
20365
20366 /* Ensure there exists a correct set of indexes in the partition. */
20367 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20368
20369 /* and triggers */
20370 CloneRowTriggersToPartition(rel, attachrel);
20371
20372 /*
20373 * Clone foreign key constraints. Callee is responsible for setting up
20374 * for phase 3 constraint verification.
20375 */
20376 CloneForeignKeyConstraints(wqueue, rel, attachrel);
20377
20378 /*
20379 * Generate partition constraint from the partition bound specification.
20380 * If the parent itself is a partition, make sure to include its
20381 * constraint as well.
20382 */
20383 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20384
20385 /*
20386 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20387 * since it's needed later to construct the constraint expression for
20388 * validating against the default partition, if any.
20389 */
20390 partConstraint = list_concat_copy(partBoundConstraint,
20392
20393 /* Skip validation if there are no constraints to validate. */
20394 if (partConstraint)
20395 {
20396 /*
20397 * Run the partition quals through const-simplification similar to
20398 * check constraints. We skip canonicalize_qual, though, because
20399 * partition quals should be in canonical form already.
20400 */
20401 partConstraint =
20403 (Node *) partConstraint);
20404
20405 /* XXX this sure looks wrong */
20406 partConstraint = list_make1(make_ands_explicit(partConstraint));
20407
20408 /*
20409 * Adjust the generated constraint to match this partition's attribute
20410 * numbers.
20411 */
20412 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20413 rel);
20414
20415 /* Validate partition constraints against the table being attached. */
20416 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20417 false);
20418 }
20419
20420 /*
20421 * If we're attaching a partition other than the default partition and a
20422 * default one exists, then that partition's partition constraint changes,
20423 * so add an entry to the work queue to validate it, too. (We must not do
20424 * this when the partition being attached is the default one; we already
20425 * did it above!)
20426 */
20427 if (OidIsValid(defaultPartOid))
20428 {
20429 Relation defaultrel;
20430 List *defPartConstraint;
20431
20432 Assert(!cmd->bound->is_default);
20433
20434 /* we already hold a lock on the default partition */
20435 defaultrel = table_open(defaultPartOid, NoLock);
20436 defPartConstraint =
20437 get_proposed_default_constraint(partBoundConstraint);
20438
20439 /*
20440 * Map the Vars in the constraint expression from rel's attnos to
20441 * defaultrel's.
20442 */
20443 defPartConstraint =
20444 map_partition_varattnos(defPartConstraint,
20445 1, defaultrel, rel);
20446 QueuePartitionConstraintValidation(wqueue, defaultrel,
20447 defPartConstraint, true);
20448
20449 /* keep our lock until commit. */
20450 table_close(defaultrel, NoLock);
20451 }
20452
20453 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20454
20455 /*
20456 * If the partition we just attached is partitioned itself, invalidate
20457 * relcache for all descendent partitions too to ensure that their
20458 * rd_partcheck expression trees are rebuilt; partitions already locked at
20459 * the beginning of this function.
20460 */
20461 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20462 {
20463 ListCell *l;
20464
20465 foreach(l, attachrel_children)
20466 {
20468 }
20469 }
20470
20471 /* keep our lock until commit */
20472 table_close(attachrel, NoLock);
20473
20474 return address;
20475}
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2256
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition: heap.c:3956
List * list_concat_copy(const List *list1, const List *list2)
Definition: list.c:598
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:799
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:39
void check_new_partition_bound(char *relname, Relation parent, PartitionBoundSpec *spec, ParseState *pstate)
Definition: partbounds.c:2896
List * get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:249
List * RelationGetPartitionQual(Relation rel)
Definition: partcache.c:277
Oid get_default_oid_from_partdesc(PartitionDesc partdesc)
Definition: partdesc.c:501
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition: partition.c:222
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:370
const char * queryString
Definition: utility.h:33
const char * p_sourcetext
Definition: parse_node.h:209
PartitionBoundSpec * bound
Definition: parsenodes.h:958
RangeVar * name
Definition: parsenodes.h:957
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition: syscache.h:102
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
Definition: tablecmds.c:20668
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:20486
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11180
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
Definition: tablecmds.c:20090

References AccessExclusiveLock, AccessShareLock, Assert(), AT_AttachPartition, ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, AttachPartitionEnsureIndexes(), PartitionCmd::bound, BTEqualStrategyNumber, CacheInvalidateRelcacheByRelid(), check_new_partition_bound(), CloneForeignKeyConstraints(), CloneRowTriggersToPartition(), CreateInheritance(), CStringGetDatum(), ereport, errcode(), errdetail(), errmsg(), ERROR, eval_const_expressions(), find_all_inheritors(), FindTriggerIncompatibleWithInheritance(), get_default_oid_from_partdesc(), get_proposed_default_constraint(), get_qual_from_partbound(), HeapTupleIsValid, PartitionBoundSpec::is_default, lfirst_oid, list_concat_copy(), list_make1, list_member_oid(), LockRelationOid(), make_ands_explicit(), make_parsestate(), map_partition_varattnos(), PartitionCmd::name, NameStr, TupleDescData::natts, NoLock, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, ParseState::p_sourcetext, AlterTableUtilityContext::queryString, QueuePartitionConstraintValidation(), RelationData::rd_islocaltemp, RelationData::rd_rel, RelationGetDescr, RelationGetPartitionDesc(), RelationGetPartitionQual(), RelationGetRelationName, RelationGetRelid, ScanKeyInit(), SearchSysCacheExists2, StorePartitionBound(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), table_openrv(), RelationData::trigdesc, and TupleDescAttr().

Referenced by ATExecCmd().

◆ ATExecAttachPartitionIdx()

static ObjectAddress ATExecAttachPartitionIdx ( List **  wqueue,
Relation  parentIdx,
RangeVar name 
)
static

Definition at line 21534 of file tablecmds.c.

21535{
21536 Relation partIdx;
21537 Relation partTbl;
21538 Relation parentTbl;
21539 ObjectAddress address;
21540 Oid partIdxId;
21541 Oid currParent;
21543
21544 /*
21545 * We need to obtain lock on the index 'name' to modify it, but we also
21546 * need to read its owning table's tuple descriptor -- so we need to lock
21547 * both. To avoid deadlocks, obtain lock on the table before doing so on
21548 * the index. Furthermore, we need to examine the parent table of the
21549 * partition, so lock that one too.
21550 */
21551 state.partitionOid = InvalidOid;
21552 state.parentTblOid = parentIdx->rd_index->indrelid;
21553 state.lockedParentTbl = false;
21554 partIdxId =
21557 &state);
21558 /* Not there? */
21559 if (!OidIsValid(partIdxId))
21560 ereport(ERROR,
21561 (errcode(ERRCODE_UNDEFINED_OBJECT),
21562 errmsg("index \"%s\" does not exist", name->relname)));
21563
21564 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21565 partIdx = relation_open(partIdxId, AccessExclusiveLock);
21566
21567 /* we already hold locks on both tables, so this is safe: */
21568 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21569 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21570
21571 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21572
21573 /* Silently do nothing if already in the right state */
21574 currParent = partIdx->rd_rel->relispartition ?
21575 get_partition_parent(partIdxId, false) : InvalidOid;
21576 if (currParent != RelationGetRelid(parentIdx))
21577 {
21578 IndexInfo *childInfo;
21579 IndexInfo *parentInfo;
21580 AttrMap *attmap;
21581 bool found;
21582 int i;
21583 PartitionDesc partDesc;
21584 Oid constraintOid,
21585 cldConstrId = InvalidOid;
21586
21587 /*
21588 * If this partition already has an index attached, refuse the
21589 * operation.
21590 */
21591 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21592
21593 if (OidIsValid(currParent))
21594 ereport(ERROR,
21595 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21596 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21597 RelationGetRelationName(partIdx),
21598 RelationGetRelationName(parentIdx)),
21599 errdetail("Index \"%s\" is already attached to another index.",
21600 RelationGetRelationName(partIdx))));
21601
21602 /* Make sure it indexes a partition of the other index's table */
21603 partDesc = RelationGetPartitionDesc(parentTbl, true);
21604 found = false;
21605 for (i = 0; i < partDesc->nparts; i++)
21606 {
21607 if (partDesc->oids[i] == state.partitionOid)
21608 {
21609 found = true;
21610 break;
21611 }
21612 }
21613 if (!found)
21614 ereport(ERROR,
21615 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21616 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21617 RelationGetRelationName(partIdx),
21618 RelationGetRelationName(parentIdx)),
21619 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21620 RelationGetRelationName(partIdx),
21621 RelationGetRelationName(parentTbl))));
21622
21623 /* Ensure the indexes are compatible */
21624 childInfo = BuildIndexInfo(partIdx);
21625 parentInfo = BuildIndexInfo(parentIdx);
21626 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21627 RelationGetDescr(parentTbl),
21628 false);
21629 if (!CompareIndexInfo(childInfo, parentInfo,
21630 partIdx->rd_indcollation,
21631 parentIdx->rd_indcollation,
21632 partIdx->rd_opfamily,
21633 parentIdx->rd_opfamily,
21634 attmap))
21635 ereport(ERROR,
21636 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21637 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21638 RelationGetRelationName(partIdx),
21639 RelationGetRelationName(parentIdx)),
21640 errdetail("The index definitions do not match.")));
21641
21642 /*
21643 * If there is a constraint in the parent, make sure there is one in
21644 * the child too.
21645 */
21646 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21647 RelationGetRelid(parentIdx));
21648
21649 if (OidIsValid(constraintOid))
21650 {
21652 partIdxId);
21653 if (!OidIsValid(cldConstrId))
21654 ereport(ERROR,
21655 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21656 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21657 RelationGetRelationName(partIdx),
21658 RelationGetRelationName(parentIdx)),
21659 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21660 RelationGetRelationName(parentIdx),
21661 RelationGetRelationName(parentTbl),
21662 RelationGetRelationName(partIdx))));
21663 }
21664
21665 /*
21666 * If it's a primary key, make sure the columns in the partition are
21667 * NOT NULL.
21668 */
21669 if (parentIdx->rd_index->indisprimary)
21670 verifyPartitionIndexNotNull(childInfo, partTbl);
21671
21672 /* All good -- do it */
21673 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21674 if (OidIsValid(constraintOid))
21675 ConstraintSetParentConstraint(cldConstrId, constraintOid,
21676 RelationGetRelid(partTbl));
21677
21678 free_attrmap(attmap);
21679
21680 validatePartitionedIndex(parentIdx, parentTbl);
21681 }
21682
21683 relation_close(parentTbl, AccessShareLock);
21684 /* keep these locks till commit */
21685 relation_close(partTbl, NoLock);
21686 relation_close(partIdx, NoLock);
21687
21688 return address;
21689}
bool CompareIndexInfo(const IndexInfo *info1, const IndexInfo *info2, const Oid *collations1, const Oid *collations2, const Oid *opfamilies1, const Oid *opfamilies2, const AttrMap *attmap)
Definition: index.c:2537
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition: indexcmds.c:4409
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition: partition.c:53
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
Form_pg_index rd_index
Definition: rel.h:192
Oid * rd_opfamily
Definition: rel.h:207
Oid * rd_indcollation
Definition: rel.h:217
Definition: regguts.h:323
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:21480
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
Definition: tablecmds.c:21719
static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
Definition: tablecmds.c:21821
static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
Definition: tablecmds.c:21696
const char * name

References AccessExclusiveLock, AccessShareLock, build_attrmap_by_name(), BuildIndexInfo(), CompareIndexInfo(), ConstraintSetParentConstraint(), ereport, errcode(), errdetail(), errmsg(), ERROR, free_attrmap(), get_partition_parent(), get_relation_idx_constraint_oid(), i, IndexSetParentIndex(), InvalidOid, name, NoLock, PartitionDescData::nparts, ObjectAddressSet, OidIsValid, PartitionDescData::oids, RangeVarCallbackForAttachIndex(), RangeVarGetRelidExtended(), RelationData::rd_indcollation, RelationData::rd_index, RelationData::rd_opfamily, RelationData::rd_rel, refuseDupeIndexAttach(), relation_close(), relation_open(), RelationGetDescr, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, validatePartitionedIndex(), and verifyPartitionIndexNotNull().

Referenced by ATExecCmd().

◆ ATExecChangeOwner()

void ATExecChangeOwner ( Oid  relationOid,
Oid  newOwnerId,
bool  recursing,
LOCKMODE  lockmode 
)

Definition at line 15995 of file tablecmds.c.

15996{
15997 Relation target_rel;
15998 Relation class_rel;
15999 HeapTuple tuple;
16000 Form_pg_class tuple_class;
16001
16002 /*
16003 * Get exclusive lock till end of transaction on the target table. Use
16004 * relation_open so that we can work on indexes and sequences.
16005 */
16006 target_rel = relation_open(relationOid, lockmode);
16007
16008 /* Get its pg_class tuple, too */
16009 class_rel = table_open(RelationRelationId, RowExclusiveLock);
16010
16011 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16012 if (!HeapTupleIsValid(tuple))
16013 elog(ERROR, "cache lookup failed for relation %u", relationOid);
16014 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16015
16016 /* Can we change the ownership of this tuple? */
16017 switch (tuple_class->relkind)
16018 {
16019 case RELKIND_RELATION:
16020 case RELKIND_VIEW:
16021 case RELKIND_MATVIEW:
16022 case RELKIND_FOREIGN_TABLE:
16023 case RELKIND_PARTITIONED_TABLE:
16024 /* ok to change owner */
16025 break;
16026 case RELKIND_INDEX:
16027 if (!recursing)
16028 {
16029 /*
16030 * Because ALTER INDEX OWNER used to be allowed, and in fact
16031 * is generated by old versions of pg_dump, we give a warning
16032 * and do nothing rather than erroring out. Also, to avoid
16033 * unnecessary chatter while restoring those old dumps, say
16034 * nothing at all if the command would be a no-op anyway.
16035 */
16036 if (tuple_class->relowner != newOwnerId)
16038 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16039 errmsg("cannot change owner of index \"%s\"",
16040 NameStr(tuple_class->relname)),
16041 errhint("Change the ownership of the index's table instead.")));
16042 /* quick hack to exit via the no-op path */
16043 newOwnerId = tuple_class->relowner;
16044 }
16045 break;
16046 case RELKIND_PARTITIONED_INDEX:
16047 if (recursing)
16048 break;
16049 ereport(ERROR,
16050 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16051 errmsg("cannot change owner of index \"%s\"",
16052 NameStr(tuple_class->relname)),
16053 errhint("Change the ownership of the index's table instead.")));
16054 break;
16055 case RELKIND_SEQUENCE:
16056 if (!recursing &&
16057 tuple_class->relowner != newOwnerId)
16058 {
16059 /* if it's an owned sequence, disallow changing it by itself */
16060 Oid tableId;
16061 int32 colId;
16062
16063 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16064 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16065 ereport(ERROR,
16066 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16067 errmsg("cannot change owner of sequence \"%s\"",
16068 NameStr(tuple_class->relname)),
16069 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16070 NameStr(tuple_class->relname),
16071 get_rel_name(tableId))));
16072 }
16073 break;
16074 case RELKIND_COMPOSITE_TYPE:
16075 if (recursing)
16076 break;
16077 ereport(ERROR,
16078 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16079 errmsg("\"%s\" is a composite type",
16080 NameStr(tuple_class->relname)),
16081 /* translator: %s is an SQL ALTER command */
16082 errhint("Use %s instead.",
16083 "ALTER TYPE")));
16084 break;
16085 case RELKIND_TOASTVALUE:
16086 if (recursing)
16087 break;
16088 /* FALL THRU */
16089 default:
16090 ereport(ERROR,
16091 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16092 errmsg("cannot change owner of relation \"%s\"",
16093 NameStr(tuple_class->relname)),
16094 errdetail_relkind_not_supported(tuple_class->relkind)));
16095 }
16096
16097 /*
16098 * If the new owner is the same as the existing owner, consider the
16099 * command to have succeeded. This is for dump restoration purposes.
16100 */
16101 if (tuple_class->relowner != newOwnerId)
16102 {
16103 Datum repl_val[Natts_pg_class];
16104 bool repl_null[Natts_pg_class];
16105 bool repl_repl[Natts_pg_class];
16106 Acl *newAcl;
16107 Datum aclDatum;
16108 bool isNull;
16109 HeapTuple newtuple;
16110
16111 /* skip permission checks when recursing to index or toast table */
16112 if (!recursing)
16113 {
16114 /* Superusers can always do it */
16115 if (!superuser())
16116 {
16117 Oid namespaceOid = tuple_class->relnamespace;
16118 AclResult aclresult;
16119
16120 /* Otherwise, must be owner of the existing object */
16121 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16123 RelationGetRelationName(target_rel));
16124
16125 /* Must be able to become new owner */
16126 check_can_set_role(GetUserId(), newOwnerId);
16127
16128 /* New owner must have CREATE privilege on namespace */
16129 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16130 ACL_CREATE);
16131 if (aclresult != ACLCHECK_OK)
16132 aclcheck_error(aclresult, OBJECT_SCHEMA,
16133 get_namespace_name(namespaceOid));
16134 }
16135 }
16136
16137 memset(repl_null, false, sizeof(repl_null));
16138 memset(repl_repl, false, sizeof(repl_repl));
16139
16140 repl_repl[Anum_pg_class_relowner - 1] = true;
16141 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16142
16143 /*
16144 * Determine the modified ACL for the new owner. This is only
16145 * necessary when the ACL is non-null.
16146 */
16147 aclDatum = SysCacheGetAttr(RELOID, tuple,
16148 Anum_pg_class_relacl,
16149 &isNull);
16150 if (!isNull)
16151 {
16152 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16153 tuple_class->relowner, newOwnerId);
16154 repl_repl[Anum_pg_class_relacl - 1] = true;
16155 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16156 }
16157
16158 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16159
16160 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16161
16162 heap_freetuple(newtuple);
16163
16164 /*
16165 * We must similarly update any per-column ACLs to reflect the new
16166 * owner; for neatness reasons that's split out as a subroutine.
16167 */
16168 change_owner_fix_column_acls(relationOid,
16169 tuple_class->relowner,
16170 newOwnerId);
16171
16172 /*
16173 * Update owner dependency reference, if any. A composite type has
16174 * none, because it's tracked for the pg_type entry instead of here;
16175 * indexes and TOAST tables don't have their own entries either.
16176 */
16177 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16178 tuple_class->relkind != RELKIND_INDEX &&
16179 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16180 tuple_class->relkind != RELKIND_TOASTVALUE)
16181 changeDependencyOnOwner(RelationRelationId, relationOid,
16182 newOwnerId);
16183
16184 /*
16185 * Also change the ownership of the table's row type, if it has one
16186 */
16187 if (OidIsValid(tuple_class->reltype))
16188 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16189
16190 /*
16191 * If we are operating on a table or materialized view, also change
16192 * the ownership of any indexes and sequences that belong to the
16193 * relation, as well as its toast table (if it has one).
16194 */
16195 if (tuple_class->relkind == RELKIND_RELATION ||
16196 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16197 tuple_class->relkind == RELKIND_MATVIEW ||
16198 tuple_class->relkind == RELKIND_TOASTVALUE)
16199 {
16200 List *index_oid_list;
16201 ListCell *i;
16202
16203 /* Find all the indexes belonging to this relation */
16204 index_oid_list = RelationGetIndexList(target_rel);
16205
16206 /* For each index, recursively change its ownership */
16207 foreach(i, index_oid_list)
16208 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16209
16210 list_free(index_oid_list);
16211 }
16212
16213 /* If it has a toast table, recurse to change its ownership */
16214 if (tuple_class->reltoastrelid != InvalidOid)
16215 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16216 true, lockmode);
16217
16218 /* If it has dependent sequences, recurse to change them too */
16219 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16220 }
16221
16222 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16223
16224 ReleaseSysCache(tuple);
16225 table_close(class_rel, RowExclusiveLock);
16226 relation_close(target_rel, NoLock);
16227}
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition: acl.c:1103
void check_can_set_role(Oid member, Oid role)
Definition: acl.c:5325
#define DatumGetAclP(X)
Definition: acl.h:120
#define WARNING
Definition: elog.h:36
@ OBJECT_SCHEMA
Definition: parsenodes.h:2353
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
Definition: pg_shdepend.c:316
bool superuser(void)
Definition: superuser.c:46
void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:15995
static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
Definition: tablecmds.c:16301
static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
Definition: tablecmds.c:16236
void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
Definition: typecmds.c:3978

References ACL_CREATE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, aclnewowner(), AlterTypeOwnerInternal(), ATExecChangeOwner(), CatalogTupleUpdate(), change_owner_fix_column_acls(), change_owner_recurse_to_sequences(), changeDependencyOnOwner(), check_can_set_role(), DatumGetAclP, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, elog, ereport, errcode(), errdetail(), errdetail_relkind_not_supported(), errhint(), errmsg(), ERROR, get_namespace_name(), get_rel_name(), get_rel_relkind(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), heap_freetuple(), heap_modify_tuple(), HeapTupleIsValid, i, InvalidOid, InvokeObjectPostAlterHook, lfirst_oid, list_free(), NameStr, NoLock, object_aclcheck(), object_ownercheck(), OBJECT_SCHEMA, ObjectIdGetDatum(), OidIsValid, PointerGetDatum(), relation_close(), relation_open(), RelationGetDescr, RelationGetIndexList(), RelationGetRelationName, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), sequenceIsOwned(), superuser(), SysCacheGetAttr(), HeapTupleData::t_self, table_close(), table_open(), and WARNING.

Referenced by AlterTypeOwner_oid(), ATExecChangeOwner(), ATExecCmd(), change_owner_recurse_to_sequences(), and shdepReassignOwned_Owner().

◆ ATExecClusterOn()

static ObjectAddress ATExecClusterOn ( Relation  rel,
const char *  indexName,
LOCKMODE  lockmode 
)
static

Definition at line 16370 of file tablecmds.c.

16371{
16372 Oid indexOid;
16373 ObjectAddress address;
16374
16375 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16376
16377 if (!OidIsValid(indexOid))
16378 ereport(ERROR,
16379 (errcode(ERRCODE_UNDEFINED_OBJECT),
16380 errmsg("index \"%s\" for table \"%s\" does not exist",
16381 indexName, RelationGetRelationName(rel))));
16382
16383 /* Check index is valid to cluster on */
16384 check_index_is_clusterable(rel, indexOid, lockmode);
16385
16386 /* And do the work */
16387 mark_index_clustered(rel, indexOid, false);
16388
16389 ObjectAddressSet(address,
16390 RelationRelationId, indexOid);
16391
16392 return address;
16393}
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition: cluster.c:494
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition: cluster.c:554

References check_index_is_clusterable(), ereport, errcode(), errmsg(), ERROR, get_relname_relid(), mark_index_clustered(), ObjectAddressSet, OidIsValid, RelationData::rd_rel, and RelationGetRelationName.

Referenced by ATExecCmd().

◆ ATExecCmd()

static void ATExecCmd ( List **  wqueue,
AlteredTableInfo tab,
AlterTableCmd cmd,
LOCKMODE  lockmode,
AlterTablePass  cur_pass,
AlterTableUtilityContext context 
)
static

Definition at line 5366 of file tablecmds.c.

5369{
5371 Relation rel = tab->rel;
5372
5373 switch (cmd->subtype)
5374 {
5375 case AT_AddColumn: /* ADD COLUMN */
5376 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5377 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5378 cmd->recurse, false,
5379 lockmode, cur_pass, context);
5380 break;
5381 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5382 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5383 break;
5384 case AT_CookedColumnDefault: /* add a pre-cooked default */
5385 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5386 break;
5387 case AT_AddIdentity:
5388 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5389 cur_pass, context);
5390 Assert(cmd != NULL);
5391 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5392 break;
5393 case AT_SetIdentity:
5394 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5395 cur_pass, context);
5396 Assert(cmd != NULL);
5397 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5398 break;
5399 case AT_DropIdentity:
5400 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5401 break;
5402 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5403 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5404 break;
5405 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5406 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5407 cmd->recurse, false, lockmode);
5408 break;
5409 case AT_SetExpression:
5410 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5411 break;
5412 case AT_DropExpression:
5413 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5414 break;
5415 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5416 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5417 break;
5418 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5419 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5420 break;
5421 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5422 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5423 break;
5424 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5425 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5426 break;
5427 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5428 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5429 lockmode);
5430 break;
5431 case AT_DropColumn: /* DROP COLUMN */
5432 address = ATExecDropColumn(wqueue, rel, cmd->name,
5433 cmd->behavior, cmd->recurse, false,
5434 cmd->missing_ok, lockmode,
5435 NULL);
5436 break;
5437 case AT_AddIndex: /* ADD INDEX */
5438 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5439 lockmode);
5440 break;
5441 case AT_ReAddIndex: /* ADD INDEX */
5442 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5443 lockmode);
5444 break;
5445 case AT_ReAddStatistics: /* ADD STATISTICS */
5446 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5447 true, lockmode);
5448 break;
5449 case AT_AddConstraint: /* ADD CONSTRAINT */
5450 /* Transform the command only during initial examination */
5451 if (cur_pass == AT_PASS_ADD_CONSTR)
5452 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5453 cmd->recurse, lockmode,
5454 cur_pass, context);
5455 /* Depending on constraint type, might be no more work to do now */
5456 if (cmd != NULL)
5457 address =
5458 ATExecAddConstraint(wqueue, tab, rel,
5459 (Constraint *) cmd->def,
5460 cmd->recurse, false, lockmode);
5461 break;
5462 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5463 address =
5464 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5465 true, true, lockmode);
5466 break;
5467 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5468 * constraint */
5469 address =
5470 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5471 ((AlterDomainStmt *) cmd->def)->def,
5472 NULL);
5473 break;
5474 case AT_ReAddComment: /* Re-add existing comment */
5475 address = CommentObject((CommentStmt *) cmd->def);
5476 break;
5477 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5478 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5479 lockmode);
5480 break;
5481 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5482 address = ATExecAlterConstraint(wqueue, rel,
5484 cmd->recurse, lockmode);
5485 break;
5486 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5487 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5488 false, lockmode);
5489 break;
5490 case AT_DropConstraint: /* DROP CONSTRAINT */
5491 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5492 cmd->recurse,
5493 cmd->missing_ok, lockmode);
5494 break;
5495 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5496 /* parse transformation was done earlier */
5497 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5498 break;
5499 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5500 address =
5502 (List *) cmd->def, lockmode);
5503 break;
5504 case AT_ChangeOwner: /* ALTER OWNER */
5506 get_rolespec_oid(cmd->newowner, false),
5507 false, lockmode);
5508 break;
5509 case AT_ClusterOn: /* CLUSTER ON */
5510 address = ATExecClusterOn(rel, cmd->name, lockmode);
5511 break;
5512 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5513 ATExecDropCluster(rel, lockmode);
5514 break;
5515 case AT_SetLogged: /* SET LOGGED */
5516 case AT_SetUnLogged: /* SET UNLOGGED */
5517 break;
5518 case AT_DropOids: /* SET WITHOUT OIDS */
5519 /* nothing to do here, oid columns don't exist anymore */
5520 break;
5521 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5522
5523 /*
5524 * Only do this for partitioned tables, for which this is just a
5525 * catalog change. Tables with storage are handled by Phase 3.
5526 */
5527 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5528 tab->chgAccessMethod)
5530 break;
5531 case AT_SetTableSpace: /* SET TABLESPACE */
5532
5533 /*
5534 * Only do this for partitioned tables and indexes, for which this
5535 * is just a catalog change. Other relation types which have
5536 * storage are handled by Phase 3.
5537 */
5538 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5539 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5541
5542 break;
5543 case AT_SetRelOptions: /* SET (...) */
5544 case AT_ResetRelOptions: /* RESET (...) */
5545 case AT_ReplaceRelOptions: /* replace entire option list */
5546 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5547 break;
5548 case AT_EnableTrig: /* ENABLE TRIGGER name */
5551 cmd->recurse,
5552 lockmode);
5553 break;
5554 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5556 TRIGGER_FIRES_ALWAYS, false,
5557 cmd->recurse,
5558 lockmode);
5559 break;
5560 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5563 cmd->recurse,
5564 lockmode);
5565 break;
5566 case AT_DisableTrig: /* DISABLE TRIGGER name */
5568 TRIGGER_DISABLED, false,
5569 cmd->recurse,
5570 lockmode);
5571 break;
5572 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5575 cmd->recurse,
5576 lockmode);
5577 break;
5578 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5580 TRIGGER_DISABLED, false,
5581 cmd->recurse,
5582 lockmode);
5583 break;
5584 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5587 cmd->recurse,
5588 lockmode);
5589 break;
5590 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5592 TRIGGER_DISABLED, true,
5593 cmd->recurse,
5594 lockmode);
5595 break;
5596
5597 case AT_EnableRule: /* ENABLE RULE name */
5598 ATExecEnableDisableRule(rel, cmd->name,
5599 RULE_FIRES_ON_ORIGIN, lockmode);
5600 break;
5601 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5602 ATExecEnableDisableRule(rel, cmd->name,
5603 RULE_FIRES_ALWAYS, lockmode);
5604 break;
5605 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5606 ATExecEnableDisableRule(rel, cmd->name,
5607 RULE_FIRES_ON_REPLICA, lockmode);
5608 break;
5609 case AT_DisableRule: /* DISABLE RULE name */
5610 ATExecEnableDisableRule(rel, cmd->name,
5611 RULE_DISABLED, lockmode);
5612 break;
5613
5614 case AT_AddInherit:
5615 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5616 break;
5617 case AT_DropInherit:
5618 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5619 break;
5620 case AT_AddOf:
5621 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5622 break;
5623 case AT_DropOf:
5624 ATExecDropOf(rel, lockmode);
5625 break;
5626 case AT_ReplicaIdentity:
5627 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5628 break;
5630 ATExecSetRowSecurity(rel, true);
5631 break;
5633 ATExecSetRowSecurity(rel, false);
5634 break;
5637 break;
5640 break;
5641 case AT_GenericOptions:
5642 ATExecGenericOptions(rel, (List *) cmd->def);
5643 break;
5644 case AT_AttachPartition:
5645 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5646 cur_pass, context);
5647 Assert(cmd != NULL);
5648 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5649 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5650 context);
5651 else
5652 address = ATExecAttachPartitionIdx(wqueue, rel,
5653 ((PartitionCmd *) cmd->def)->name);
5654 break;
5655 case AT_DetachPartition:
5656 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5657 cur_pass, context);
5658 Assert(cmd != NULL);
5659 /* ATPrepCmd ensures it must be a table */
5660 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5661 address = ATExecDetachPartition(wqueue, tab, rel,
5662 ((PartitionCmd *) cmd->def)->name,
5663 ((PartitionCmd *) cmd->def)->concurrent);
5664 break;
5666 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5667 break;
5668 default: /* oops */
5669 elog(ERROR, "unrecognized alter table type: %d",
5670 (int) cmd->subtype);
5671 break;
5672 }
5673
5674 /*
5675 * Report the subcommand to interested event triggers.
5676 */
5677 if (cmd)
5678 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5679
5680 /*
5681 * Bump the command counter to ensure the next subcommand in the sequence
5682 * can see the changes so far
5683 */
5685}
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition: acl.c:5570
ObjectAddress CommentObject(CommentStmt *stmt)
Definition: comment.c:40
void EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
#define RULE_FIRES_ON_ORIGIN
Definition: rewriteDefine.h:21
#define RULE_FIRES_ON_REPLICA
Definition: rewriteDefine.h:23
#define RULE_FIRES_ALWAYS
Definition: rewriteDefine.h:22
#define RULE_DISABLED
Definition: rewriteDefine.h:24
RoleSpec * newowner
Definition: parsenodes.h:2485
DropBehavior behavior
Definition: parsenodes.h:2488
bool chgAccessMethod
Definition: tablecmds.c:192
Relation rel
Definition: tablecmds.c:182
static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName, List *options, LOCKMODE lockmode)
Definition: tablecmds.c:15878
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17184
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:18281
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12171
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:8767
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition: tablecmds.c:8116
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8453
static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:12878
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:7732
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9761
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
Definition: tablecmds.c:20825
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:18557
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:17127
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
Definition: tablecmds.c:9016
static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode, ObjectAddresses *addrs)
Definition: tablecmds.c:9249
static void ATExecSetRowSecurity(Relation rel, bool rls)
Definition: tablecmds.c:18527
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition: tablecmds.c:8201
static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
Definition: tablecmds.c:16568
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8336
static void ATExecGenericOptions(Relation rel, List *options)
Definition: tablecmds.c:18586
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8871
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode)
Definition: tablecmds.c:14672
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:16402
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
Definition: tablecmds.c:8567
static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
Definition: tablecmds.c:18139
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17748
static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:9669
static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:18413
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:21330
static ObjectAddress ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:9158
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:20163
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
Definition: tablecmds.c:16448
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:13961
static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9585
static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:18667
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:16869
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:21534
static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9648
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
Definition: tablecmds.c:16370
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:17145
#define TRIGGER_DISABLED
Definition: trigger.h:152
#define TRIGGER_FIRES_ON_REPLICA
Definition: trigger.h:151
#define TRIGGER_FIRES_ALWAYS
Definition: trigger.h:150
ObjectAddress AlterDomainAddConstraint(List *names, Node *newConstraint, ObjectAddress *constrAddr)
Definition: typecmds.c:2927

References AlterDomainAddConstraint(), Assert(), AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AddInherit, AT_AddOf, AT_AlterColumnGenericOptions, AT_AlterColumnType, AT_AlterConstraint, AT_AttachPartition, AT_ChangeOwner, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_NoForceRowSecurity, AT_PASS_ADD_CONSTR, AT_ReAddComment, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReAddIndex, AT_ReAddStatistics, AT_ReplaceRelOptions, AT_ReplicaIdentity, AT_ResetOptions, AT_ResetRelOptions, AT_SetAccessMethod, AT_SetCompression, AT_SetExpression, AT_SetIdentity, AT_SetLogged, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_SetUnLogged, AT_ValidateConstraint, ATExecAddColumn(), ATExecAddConstraint(), ATExecAddIdentity(), ATExecAddIndex(), ATExecAddIndexConstraint(), ATExecAddInherit(), ATExecAddOf(), ATExecAddStatistics(), ATExecAlterColumnGenericOptions(), ATExecAlterColumnType(), ATExecAlterConstraint(), ATExecAttachPartition(), ATExecAttachPartitionIdx(), ATExecChangeOwner(), ATExecClusterOn(), ATExecColumnDefault(), ATExecCookedColumnDefault(), ATExecDetachPartition(), ATExecDetachPartitionFinalize(), ATExecDropCluster(), ATExecDropColumn(), ATExecDropConstraint(), ATExecDropExpression(), ATExecDropIdentity(), ATExecDropInherit(), ATExecDropNotNull(), ATExecDropOf(), ATExecEnableDisableRule(), ATExecEnableDisableTrigger(), ATExecForceNoForceRowSecurity(), ATExecGenericOptions(), ATExecReplicaIdentity(), ATExecSetAccessMethodNoStorage(), ATExecSetCompression(), ATExecSetExpression(), ATExecSetIdentity(), ATExecSetNotNull(), ATExecSetOptions(), ATExecSetRelOptions(), ATExecSetRowSecurity(), ATExecSetStatistics(), ATExecSetStorage(), ATExecSetTableSpaceNoStorage(), ATExecValidateConstraint(), ATParseTransformCmd(), AlterTableCmd::behavior, castNode, AlteredTableInfo::chgAccessMethod, CommandCounterIncrement(), CommentObject(), AlterTableCmd::def, elog, ERROR, EventTriggerCollectAlterTableSubcmd(), get_rolespec_oid(), InvalidObjectAddress, AlterTableCmd::missing_ok, AlterTableCmd::name, AlteredTableInfo::newAccessMethod, AlterTableCmd::newowner, AlteredTableInfo::newTableSpace, AlterTableCmd::num, RelationData::rd_rel, AlterTableCmd::recurse, AlteredTableInfo::rel, RelationGetRelid, RULE_DISABLED, RULE_FIRES_ALWAYS, RULE_FIRES_ON_ORIGIN, RULE_FIRES_ON_REPLICA, AlterTableCmd::subtype, TRIGGER_DISABLED, TRIGGER_FIRES_ALWAYS, TRIGGER_FIRES_ON_ORIGIN, and TRIGGER_FIRES_ON_REPLICA.

Referenced by ATRewriteCatalogs().

◆ ATExecColumnDefault()

static ObjectAddress ATExecColumnDefault ( Relation  rel,
const char *  colName,
Node newDefault,
LOCKMODE  lockmode 
)
static

Definition at line 8116 of file tablecmds.c.

8118{
8119 TupleDesc tupdesc = RelationGetDescr(rel);
8121 ObjectAddress address;
8122
8123 /*
8124 * get the number of the attribute
8125 */
8126 attnum = get_attnum(RelationGetRelid(rel), colName);
8128 ereport(ERROR,
8129 (errcode(ERRCODE_UNDEFINED_COLUMN),
8130 errmsg("column \"%s\" of relation \"%s\" does not exist",
8131 colName, RelationGetRelationName(rel))));
8132
8133 /* Prevent them from altering a system attribute */
8134 if (attnum <= 0)
8135 ereport(ERROR,
8136 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8137 errmsg("cannot alter system column \"%s\"",
8138 colName)));
8139
8140 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8141 ereport(ERROR,
8142 (errcode(ERRCODE_SYNTAX_ERROR),
8143 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8144 colName, RelationGetRelationName(rel)),
8145 /* translator: %s is an SQL ALTER command */
8146 newDefault ? 0 : errhint("Use %s instead.",
8147 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8148
8149 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8150 ereport(ERROR,
8151 (errcode(ERRCODE_SYNTAX_ERROR),
8152 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8153 colName, RelationGetRelationName(rel)),
8154 newDefault ?
8155 /* translator: %s is an SQL ALTER command */
8156 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8157 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8158 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8159
8160 /*
8161 * Remove any old default for the column. We use RESTRICT here for
8162 * safety, but at present we do not expect anything to depend on the
8163 * default.
8164 *
8165 * We treat removing the existing default as an internal operation when it
8166 * is preparatory to adding a new default, but as a user-initiated
8167 * operation when the user asked for a drop.
8168 */
8170 newDefault != NULL);
8171
8172 if (newDefault)
8173 {
8174 /* SET DEFAULT */
8175 RawColumnDefault *rawEnt;
8176
8177 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8178 rawEnt->attnum = attnum;
8179 rawEnt->raw_default = newDefault;
8180 rawEnt->generated = '\0';
8181
8182 /*
8183 * This function is intended for CREATE TABLE, so it processes a
8184 * _list_ of defaults, but we just do one.
8185 */
8187 false, true, false, NULL);
8188 }
8189
8190 ObjectAddressSubSet(address, RelationRelationId,
8191 RelationGetRelid(rel), attnum);
8192 return address;
8193}
#define InvalidAttrNumber
Definition: attnum.h:23
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:950

References AddRelationNewConstraints(), RawColumnDefault::attnum, attnum, DROP_RESTRICT, ereport, errcode(), errhint(), errmsg(), ERROR, RawColumnDefault::generated, get_attnum(), InvalidAttrNumber, list_make1, NIL, ObjectAddressSubSet, palloc(), RawColumnDefault::raw_default, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RemoveAttrDefault(), and TupleDescAttr().

Referenced by ATExecCmd().

◆ ATExecCookedColumnDefault()

static ObjectAddress ATExecCookedColumnDefault ( Relation  rel,
AttrNumber  attnum,
Node newDefault 
)
static

Definition at line 8201 of file tablecmds.c.

8203{
8204 ObjectAddress address;
8205
8206 /* We assume no checking is required */
8207
8208 /*
8209 * Remove any old default for the column. We use RESTRICT here for
8210 * safety, but at present we do not expect anything to depend on the
8211 * default. (In ordinary cases, there could not be a default in place
8212 * anyway, but it's possible when combining LIKE with inheritance.)
8213 */
8215 true);
8216
8217 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8218
8219 ObjectAddressSubSet(address, RelationRelationId,
8220 RelationGetRelid(rel), attnum);
8221 return address;
8222}

References attnum, DROP_RESTRICT, ObjectAddressSubSet, RelationGetRelid, RemoveAttrDefault(), and StoreAttrDefault().

Referenced by ATExecCmd().

◆ ATExecDetachPartition()

static ObjectAddress ATExecDetachPartition ( List **  wqueue,
AlteredTableInfo tab,
Relation  rel,
RangeVar name,
bool  concurrent 
)
static

Definition at line 20825 of file tablecmds.c.

20827{
20828 Relation partRel;
20829 ObjectAddress address;
20830 Oid defaultPartOid;
20831
20832 /*
20833 * We must lock the default partition, because detaching this partition
20834 * will change its partition constraint.
20835 */
20836 defaultPartOid =
20838 if (OidIsValid(defaultPartOid))
20839 {
20840 /*
20841 * Concurrent detaching when a default partition exists is not
20842 * supported. The main problem is that the default partition
20843 * constraint would change. And there's a definitional problem: what
20844 * should happen to the tuples that are being inserted that belong to
20845 * the partition being detached? Putting them on the partition being
20846 * detached would be wrong, since they'd become "lost" after the
20847 * detaching completes but we cannot put them in the default partition
20848 * either until we alter its partition constraint.
20849 *
20850 * I think we could solve this problem if we effected the constraint
20851 * change before committing the first transaction. But the lock would
20852 * have to remain AEL and it would cause concurrent query planning to
20853 * be blocked, so changing it that way would be even worse.
20854 */
20855 if (concurrent)
20856 ereport(ERROR,
20857 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20858 errmsg("cannot detach partitions concurrently when a default partition exists")));
20859 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20860 }
20861
20862 /*
20863 * In concurrent mode, the partition is locked with share-update-exclusive
20864 * in the first transaction. This allows concurrent transactions to be
20865 * doing DML to the partition.
20866 */
20867 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20869
20870 /*
20871 * Check inheritance conditions and either delete the pg_inherits row (in
20872 * non-concurrent mode) or just set the inhdetachpending flag.
20873 */
20874 if (!concurrent)
20875 RemoveInheritance(partRel, rel, false);
20876 else
20877 MarkInheritDetached(partRel, rel);
20878
20879 /*
20880 * Ensure that foreign keys still hold after this detach. This keeps
20881 * locks on the referencing tables, which prevents concurrent transactions
20882 * from adding rows that we wouldn't see. For this to work in concurrent
20883 * mode, it is critical that the partition appears as no longer attached
20884 * for the RI queries as soon as the first transaction commits.
20885 */
20887
20888 /*
20889 * Concurrent mode has to work harder; first we add a new constraint to
20890 * the partition that matches the partition constraint. Then we close our
20891 * existing transaction, and in a new one wait for all processes to catch
20892 * up on the catalog updates we've done so far; at that point we can
20893 * complete the operation.
20894 */
20895 if (concurrent)
20896 {
20897 Oid partrelid,
20898 parentrelid;
20899 LOCKTAG tag;
20900 char *parentrelname;
20901 char *partrelname;
20902
20903 /*
20904 * Add a new constraint to the partition being detached, which
20905 * supplants the partition constraint (unless there is one already).
20906 */
20907 DetachAddConstraintIfNeeded(wqueue, partRel);
20908
20909 /*
20910 * We're almost done now; the only traces that remain are the
20911 * pg_inherits tuple and the partition's relpartbounds. Before we can
20912 * remove those, we need to wait until all transactions that know that
20913 * this is a partition are gone.
20914 */
20915
20916 /*
20917 * Remember relation OIDs to re-acquire them later; and relation names
20918 * too, for error messages if something is dropped in between.
20919 */
20920 partrelid = RelationGetRelid(partRel);
20921 parentrelid = RelationGetRelid(rel);
20922 parentrelname = MemoryContextStrdup(PortalContext,
20924 partrelname = MemoryContextStrdup(PortalContext,
20925 RelationGetRelationName(partRel));
20926
20927 /* Invalidate relcache entries for the parent -- must be before close */
20929
20930 table_close(partRel, NoLock);
20931 table_close(rel, NoLock);
20932 tab->rel = NULL;
20933
20934 /* Make updated catalog entry visible */
20937
20939
20940 /*
20941 * Now wait. This ensures that all queries that were planned
20942 * including the partition are finished before we remove the rest of
20943 * catalog entries. We don't need or indeed want to acquire this
20944 * lock, though -- that would block later queries.
20945 *
20946 * We don't need to concern ourselves with waiting for a lock on the
20947 * partition itself, since we will acquire AccessExclusiveLock below.
20948 */
20949 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20951
20952 /*
20953 * Now acquire locks in both relations again. Note they may have been
20954 * removed in the meantime, so care is required.
20955 */
20956 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20957 partRel = try_relation_open(partrelid, AccessExclusiveLock);
20958
20959 /* If the relations aren't there, something bad happened; bail out */
20960 if (rel == NULL)
20961 {
20962 if (partRel != NULL) /* shouldn't happen */
20963 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
20964 partrelname);
20965 ereport(ERROR,
20966 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20967 errmsg("partitioned table \"%s\" was removed concurrently",
20968 parentrelname)));
20969 }
20970 if (partRel == NULL)
20971 ereport(ERROR,
20972 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20973 errmsg("partition \"%s\" was removed concurrently", partrelname)));
20974
20975 tab->rel = rel;
20976 }
20977
20978 /* Do the final part of detaching */
20979 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
20980
20981 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20982
20983 /* keep our lock until commit */
20984 table_close(partRel, NoLock);
20985
20986 return address;
20987}
Oid MyDatabaseId
Definition: globals.c:95
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1631
void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
Definition: lmgr.c:905
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition: lock.h:182
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:2308
MemoryContext PortalContext
Definition: mcxt.c:174
void PopActiveSnapshot(void)
Definition: snapmgr.c:762
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:88
Definition: lock.h:166
static void MarkInheritDetached(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17790
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:17873
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:20996
static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
Definition: tablecmds.c:21365
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21896
void StartTransactionCommand(void)
Definition: xact.c:3059
void CommitTransactionCommand(void)
Definition: xact.c:3157

References AccessExclusiveLock, ATDetachCheckNoForeignKeyRefs(), CacheInvalidateRelcache(), CommitTransactionCommand(), DetachAddConstraintIfNeeded(), DetachPartitionFinalize(), elog, ereport, errcode(), errmsg(), ERROR, get_default_oid_from_partdesc(), list_make1, LockRelationOid(), MarkInheritDetached(), MemoryContextStrdup(), MyDatabaseId, name, NoLock, ObjectAddressSet, OidIsValid, PopActiveSnapshot(), PortalContext, AlteredTableInfo::rel, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, RemoveInheritance(), SET_LOCKTAG_RELATION, ShareUpdateExclusiveLock, StartTransactionCommand(), table_close(), table_openrv(), try_relation_open(), WaitForLockersMultiple(), and WARNING.

Referenced by ATExecCmd().

◆ ATExecDetachPartitionFinalize()

static ObjectAddress ATExecDetachPartitionFinalize ( Relation  rel,
RangeVar name 
)
static

Definition at line 21330 of file tablecmds.c.

21331{
21332 Relation partRel;
21333 ObjectAddress address;
21334 Snapshot snap = GetActiveSnapshot();
21335
21337
21338 /*
21339 * Wait until existing snapshots are gone. This is important if the
21340 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21341 * user could immediately run DETACH FINALIZE without actually waiting for
21342 * existing transactions. We must not complete the detach action until
21343 * all such queries are complete (otherwise we would present them with an
21344 * inconsistent view of catalogs).
21345 */
21346 WaitForOlderSnapshots(snap->xmin, false);
21347
21348 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21349
21350 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21351
21352 table_close(partRel, NoLock);
21353
21354 return address;
21355}
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:435
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:787
TransactionId xmin
Definition: snapshot.h:153

References AccessExclusiveLock, DetachPartitionFinalize(), GetActiveSnapshot(), InvalidOid, name, NoLock, ObjectAddressSet, RelationGetRelid, table_close(), table_openrv(), WaitForOlderSnapshots(), and SnapshotData::xmin.

Referenced by ATExecCmd().

◆ ATExecDropCluster()

static void ATExecDropCluster ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 16402 of file tablecmds.c.

16403{
16404 mark_index_clustered(rel, InvalidOid, false);
16405}

References InvalidOid, and mark_index_clustered().

Referenced by ATExecCmd().

◆ ATExecDropColumn()

static ObjectAddress ATExecDropColumn ( List **  wqueue,
Relation  rel,
const char *  colName,
DropBehavior  behavior,
bool  recurse,
bool  recursing,
bool  missing_ok,
LOCKMODE  lockmode,
ObjectAddresses addrs 
)
static

Definition at line 9249 of file tablecmds.c.

9254{
9255 HeapTuple tuple;
9256 Form_pg_attribute targetatt;
9258 List *children;
9259 ObjectAddress object;
9260 bool is_expr;
9261
9262 /* At top level, permission check was done in ATPrepCmd, else do it */
9263 if (recursing)
9266
9267 /* Initialize addrs on the first invocation */
9268 Assert(!recursing || addrs != NULL);
9269
9270 /* since this function recurses, it could be driven to stack overflow */
9272
9273 if (!recursing)
9274 addrs = new_object_addresses();
9275
9276 /*
9277 * get the number of the attribute
9278 */
9279 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9280 if (!HeapTupleIsValid(tuple))
9281 {
9282 if (!missing_ok)
9283 {
9284 ereport(ERROR,
9285 (errcode(ERRCODE_UNDEFINED_COLUMN),
9286 errmsg("column \"%s\" of relation \"%s\" does not exist",
9287 colName, RelationGetRelationName(rel))));
9288 }
9289 else
9290 {
9292 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9293 colName, RelationGetRelationName(rel))));
9294 return InvalidObjectAddress;
9295 }
9296 }
9297 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9298
9299 attnum = targetatt->attnum;
9300
9301 /* Can't drop a system attribute */
9302 if (attnum <= 0)
9303 ereport(ERROR,
9304 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9305 errmsg("cannot drop system column \"%s\"",
9306 colName)));
9307
9308 /*
9309 * Don't drop inherited columns, unless recursing (presumably from a drop
9310 * of the parent column)
9311 */
9312 if (targetatt->attinhcount > 0 && !recursing)
9313 ereport(ERROR,
9314 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9315 errmsg("cannot drop inherited column \"%s\"",
9316 colName)));
9317
9318 /*
9319 * Don't drop columns used in the partition key, either. (If we let this
9320 * go through, the key column's dependencies would cause a cascaded drop
9321 * of the whole table, which is surely not what the user expected.)
9322 */
9323 if (has_partition_attrs(rel,
9325 &is_expr))
9326 ereport(ERROR,
9327 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9328 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9329 colName, RelationGetRelationName(rel))));
9330
9331 ReleaseSysCache(tuple);
9332
9333 /*
9334 * Propagate to children as appropriate. Unlike most other ALTER
9335 * routines, we have to do this one level of recursion at a time; we can't
9336 * use find_all_inheritors to do it in one pass.
9337 */
9338 children =
9340
9341 if (children)
9342 {
9343 Relation attr_rel;
9344 ListCell *child;
9345
9346 /*
9347 * In case of a partitioned table, the column must be dropped from the
9348 * partitions as well.
9349 */
9350 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9351 ereport(ERROR,
9352 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9353 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9354 errhint("Do not specify the ONLY keyword.")));
9355
9356 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9357 foreach(child, children)
9358 {
9359 Oid childrelid = lfirst_oid(child);
9360 Relation childrel;
9361 Form_pg_attribute childatt;
9362
9363 /* find_inheritance_children already got lock */
9364 childrel = table_open(childrelid, NoLock);
9365 CheckAlterTableIsSafe(childrel);
9366
9367 tuple = SearchSysCacheCopyAttName(childrelid, colName);
9368 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9369 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9370 colName, childrelid);
9371 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9372
9373 if (childatt->attinhcount <= 0) /* shouldn't happen */
9374 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9375 childrelid, colName);
9376
9377 if (recurse)
9378 {
9379 /*
9380 * If the child column has other definition sources, just
9381 * decrement its inheritance count; if not, recurse to delete
9382 * it.
9383 */
9384 if (childatt->attinhcount == 1 && !childatt->attislocal)
9385 {
9386 /* Time to delete this child column, too */
9387 ATExecDropColumn(wqueue, childrel, colName,
9388 behavior, true, true,
9389 false, lockmode, addrs);
9390 }
9391 else
9392 {
9393 /* Child column must survive my deletion */
9394 childatt->attinhcount--;
9395
9396 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9397
9398 /* Make update visible */
9400 }
9401 }
9402 else
9403 {
9404 /*
9405 * If we were told to drop ONLY in this table (no recursion),
9406 * we need to mark the inheritors' attributes as locally
9407 * defined rather than inherited.
9408 */
9409 childatt->attinhcount--;
9410 childatt->attislocal = true;
9411
9412 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9413
9414 /* Make update visible */
9416 }
9417
9418 heap_freetuple(tuple);
9419
9420 table_close(childrel, NoLock);
9421 }
9422 table_close(attr_rel, RowExclusiveLock);
9423 }
9424
9425 /* Add object to delete */
9426 object.classId = RelationRelationId;
9427 object.objectId = RelationGetRelid(rel);
9428 object.objectSubId = attnum;
9429 add_exact_object_address(&object, addrs);
9430
9431 if (!recursing)
9432 {
9433 /* Recursion has ended, drop everything that was collected */
9434 performMultipleDeletions(addrs, behavior, 0);
9435 free_object_addresses(addrs);
9436 }
9437
9438 return object;
9439}
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:216
void performMultipleDeletions(const ObjectAddresses *objects, DropBehavior behavior, int flags)
Definition: dependency.c:332
bool has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
Definition: partition.c:255
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27

References add_exact_object_address(), Assert(), AT_DropColumn, ATExecDropColumn(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, attnum, bms_make_singleton(), CatalogTupleUpdate(), check_stack_depth(), CheckAlterTableIsSafe(), CommandCounterIncrement(), elog, ereport, errcode(), errhint(), errmsg(), ERROR, find_inheritance_children(), FirstLowInvalidHeapAttributeNumber, free_object_addresses(), GETSTRUCT(), has_partition_attrs(), heap_freetuple(), HeapTupleIsValid, InvalidObjectAddress, lfirst_oid, new_object_addresses(), NoLock, NOTICE, performMultipleDeletions(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCacheAttName(), SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd(), and ATExecDropColumn().

◆ ATExecDropConstraint()

static void ATExecDropConstraint ( Relation  rel,
const char *  constrName,
DropBehavior  behavior,
bool  recurse,
bool  missing_ok,
LOCKMODE  lockmode 
)
static

Definition at line 13961 of file tablecmds.c.

13964{
13965 Relation conrel;
13966 SysScanDesc scan;
13967 ScanKeyData skey[3];
13968 HeapTuple tuple;
13969 bool found = false;
13970
13971 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13972
13973 /*
13974 * Find and drop the target constraint
13975 */
13976 ScanKeyInit(&skey[0],
13977 Anum_pg_constraint_conrelid,
13978 BTEqualStrategyNumber, F_OIDEQ,
13980 ScanKeyInit(&skey[1],
13981 Anum_pg_constraint_contypid,
13982 BTEqualStrategyNumber, F_OIDEQ,
13984 ScanKeyInit(&skey[2],
13985 Anum_pg_constraint_conname,
13986 BTEqualStrategyNumber, F_NAMEEQ,
13987 CStringGetDatum(constrName));
13988 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13989 true, NULL, 3, skey);
13990
13991 /* There can be at most one matching row */
13992 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
13993 {
13994 dropconstraint_internal(rel, tuple, behavior, recurse, false,
13995 missing_ok, lockmode);
13996 found = true;
13997 }
13998
13999 systable_endscan(scan);
14000
14001 if (!found)
14002 {
14003 if (!missing_ok)
14004 ereport(ERROR,
14005 errcode(ERRCODE_UNDEFINED_OBJECT),
14006 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14007 constrName, RelationGetRelationName(rel)));
14008 else
14010 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14011 constrName, RelationGetRelationName(rel)));
14012 }
14013
14015}
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14026

References BTEqualStrategyNumber, CStringGetDatum(), dropconstraint_internal(), ereport, errcode(), errmsg(), ERROR, HeapTupleIsValid, InvalidOid, NOTICE, ObjectIdGetDatum(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecDropExpression()

static ObjectAddress ATExecDropExpression ( Relation  rel,
const char *  colName,
bool  missing_ok,
LOCKMODE  lockmode 
)
static

Definition at line 8767 of file tablecmds.c.

8768{
8769 HeapTuple tuple;
8770 Form_pg_attribute attTup;
8772 Relation attrelation;
8773 Oid attrdefoid;
8774 ObjectAddress address;
8775
8776 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8777 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8778 if (!HeapTupleIsValid(tuple))
8779 ereport(ERROR,
8780 (errcode(ERRCODE_UNDEFINED_COLUMN),
8781 errmsg("column \"%s\" of relation \"%s\" does not exist",
8782 colName, RelationGetRelationName(rel))));
8783
8784 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8785 attnum = attTup->attnum;
8786
8787 if (attnum <= 0)
8788 ereport(ERROR,
8789 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8790 errmsg("cannot alter system column \"%s\"",
8791 colName)));
8792
8793 /*
8794 * TODO: This could be done, but it would need a table rewrite to
8795 * materialize the generated values. Note that for the time being, we
8796 * still error with missing_ok, so that we don't silently leave the column
8797 * as generated.
8798 */
8799 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8800 ereport(ERROR,
8801 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8802 errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8803 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8804 colName, RelationGetRelationName(rel))));
8805
8806 if (!attTup->attgenerated)
8807 {
8808 if (!missing_ok)
8809 ereport(ERROR,
8810 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8811 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8812 colName, RelationGetRelationName(rel))));
8813 else
8814 {
8816 (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8817 colName, RelationGetRelationName(rel))));
8818 heap_freetuple(tuple);
8819 table_close(attrelation, RowExclusiveLock);
8820 return InvalidObjectAddress;
8821 }
8822 }
8823
8824 /*
8825 * Mark the column as no longer generated. (The atthasdef flag needs to
8826 * get cleared too, but RemoveAttrDefault will handle that.)
8827 */
8828 attTup->attgenerated = '\0';
8829 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8830
8831 InvokeObjectPostAlterHook(RelationRelationId,
8832 RelationGetRelid(rel),
8833 attnum);
8834 heap_freetuple(tuple);
8835
8836 table_close(attrelation, RowExclusiveLock);
8837
8838 /*
8839 * Drop the dependency records of the GENERATED expression, in particular
8840 * its INTERNAL dependency on the column, which would otherwise cause
8841 * dependency.c to refuse to perform the deletion.
8842 */
8843 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8844 if (!OidIsValid(attrdefoid))
8845 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8846 RelationGetRelid(rel), attnum);
8847 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8848
8849 /* Make above changes visible */
8851
8852 /*
8853 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8854 * safety, but at present we do not expect anything to depend on the
8855 * default.
8856 */
8858 false, false);
8859
8860 ObjectAddressSubSet(address, RelationRelationId,
8861 RelationGetRelid(rel), attnum);
8862 return address;
8863}

References attnum, CatalogTupleUpdate(), CommandCounterIncrement(), deleteDependencyRecordsFor(), DROP_RESTRICT, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, GetAttrDefaultOid(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidObjectAddress, InvokeObjectPostAlterHook, NOTICE, ObjectAddressSubSet, OidIsValid, RelationGetRelationName, RelationGetRelid, RemoveAttrDefault(), RowExclusiveLock, SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecDropIdentity()

static ObjectAddress ATExecDropIdentity ( Relation  rel,
const char *  colName,
bool  missing_ok,
LOCKMODE  lockmode,
bool  recurse,
bool  recursing 
)
static

Definition at line 8453 of file tablecmds.c.

8455{
8456 HeapTuple tuple;
8457 Form_pg_attribute attTup;
8459 Relation attrelation;
8460 ObjectAddress address;
8461 Oid seqid;
8462 ObjectAddress seqaddress;
8463 bool ispartitioned;
8464
8465 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8466 if (ispartitioned && !recurse)
8467 ereport(ERROR,
8468 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8469 errmsg("cannot drop identity from a column of only the partitioned table"),
8470 errhint("Do not specify the ONLY keyword.")));
8471
8472 if (rel->rd_rel->relispartition && !recursing)
8473 ereport(ERROR,
8474 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8475 errmsg("cannot drop identity from a column of a partition"));
8476
8477 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8478 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8479 if (!HeapTupleIsValid(tuple))
8480 ereport(ERROR,
8481 (errcode(ERRCODE_UNDEFINED_COLUMN),
8482 errmsg("column \"%s\" of relation \"%s\" does not exist",
8483 colName, RelationGetRelationName(rel))));
8484
8485 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8486 attnum = attTup->attnum;
8487
8488 if (attnum <= 0)
8489 ereport(ERROR,
8490 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8491 errmsg("cannot alter system column \"%s\"",
8492 colName)));
8493
8494 if (!attTup->attidentity)
8495 {
8496 if (!missing_ok)
8497 ereport(ERROR,
8498 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8499 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8500 colName, RelationGetRelationName(rel))));
8501 else
8502 {
8504 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8505 colName, RelationGetRelationName(rel))));
8506 heap_freetuple(tuple);
8507 table_close(attrelation, RowExclusiveLock);
8508 return InvalidObjectAddress;
8509 }
8510 }
8511
8512 attTup->attidentity = '\0';
8513 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8514
8515 InvokeObjectPostAlterHook(RelationRelationId,
8516 RelationGetRelid(rel),
8517 attTup->attnum);
8518 ObjectAddressSubSet(address, RelationRelationId,
8519 RelationGetRelid(rel), attnum);
8520 heap_freetuple(tuple);
8521
8522 table_close(attrelation, RowExclusiveLock);
8523
8524 /*
8525 * Recurse to drop the identity from column in partitions. Identity is
8526 * not inherited in regular inheritance children so ignore them.
8527 */
8528 if (recurse && ispartitioned)
8529 {
8530 List *children;
8531 ListCell *lc;
8532
8533 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8534
8535 foreach(lc, children)
8536 {
8537 Relation childrel;
8538
8539 childrel = table_open(lfirst_oid(lc), NoLock);
8540 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8541 table_close(childrel, NoLock);
8542 }
8543 }
8544
8545 if (!recursing)
8546 {
8547 /* drop the internal sequence */
8548 seqid = getIdentitySequence(rel, attnum, false);
8549 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8550 RelationRelationId, DEPENDENCY_INTERNAL);
8552 seqaddress.classId = RelationRelationId;
8553 seqaddress.objectId = seqid;
8554 seqaddress.objectSubId = 0;
8556 }
8557
8558 return address;
8559}
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:273
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:92
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:351
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:945

References ATExecDropIdentity(), attnum, CatalogTupleUpdate(), ObjectAddress::classId, CommandCounterIncrement(), deleteDependencyRecordsForClass(), DEPENDENCY_INTERNAL, DROP_RESTRICT, ereport, errcode(), errhint(), errmsg(), ERROR, find_inheritance_children(), getIdentitySequence(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidObjectAddress, InvokeObjectPostAlterHook, lfirst_oid, NoLock, NOTICE, ObjectAddressSubSet, ObjectAddress::objectId, ObjectAddress::objectSubId, PERFORM_DELETION_INTERNAL, performDeletion(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd(), ATExecDropIdentity(), and DetachPartitionFinalize().

◆ ATExecDropInherit()

static ObjectAddress ATExecDropInherit ( Relation  rel,
RangeVar parent,
LOCKMODE  lockmode 
)
static

Definition at line 17748 of file tablecmds.c.

17749{
17750 ObjectAddress address;
17751 Relation parent_rel;
17752
17753 if (rel->rd_rel->relispartition)
17754 ereport(ERROR,
17755 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17756 errmsg("cannot change inheritance of a partition")));
17757
17758 /*
17759 * AccessShareLock on the parent is probably enough, seeing that DROP
17760 * TABLE doesn't lock parent tables at all. We need some lock since we'll
17761 * be inspecting the parent's schema.
17762 */
17763 parent_rel = table_openrv(parent, AccessShareLock);
17764
17765 /*
17766 * We don't bother to check ownership of the parent table --- ownership of
17767 * the child is presumed enough rights.
17768 */
17769
17770 /* Off to RemoveInheritance() where most of the work happens */
17771 RemoveInheritance(rel, parent_rel, false);
17772
17773 ObjectAddressSet(address, RelationRelationId,
17774 RelationGetRelid(parent_rel));
17775
17776 /* keep our lock on the parent relation until commit */
17777 table_close(parent_rel, NoLock);
17778
17779 return address;
17780}

References AccessShareLock, ereport, errcode(), errmsg(), ERROR, NoLock, ObjectAddressSet, RelationData::rd_rel, RelationGetRelid, RemoveInheritance(), table_close(), and table_openrv().

Referenced by ATExecCmd().

◆ ATExecDropNotNull()

static ObjectAddress ATExecDropNotNull ( Relation  rel,
const char *  colName,
bool  recurse,
LOCKMODE  lockmode 
)
static

Definition at line 7732 of file tablecmds.c.

7734{
7735 HeapTuple tuple;
7736 HeapTuple conTup;
7737 Form_pg_attribute attTup;
7739 Relation attr_rel;
7740 ObjectAddress address;
7741
7742 /*
7743 * lookup the attribute
7744 */
7745 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7746
7747 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7748 if (!HeapTupleIsValid(tuple))
7749 ereport(ERROR,
7750 (errcode(ERRCODE_UNDEFINED_COLUMN),
7751 errmsg("column \"%s\" of relation \"%s\" does not exist",
7752 colName, RelationGetRelationName(rel))));
7753 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7754 attnum = attTup->attnum;
7755 ObjectAddressSubSet(address, RelationRelationId,
7756 RelationGetRelid(rel), attnum);
7757
7758 /* If the column is already nullable there's nothing to do. */
7759 if (!attTup->attnotnull)
7760 {
7761 table_close(attr_rel, RowExclusiveLock);
7762 return InvalidObjectAddress;
7763 }
7764
7765 /* Prevent them from altering a system attribute */
7766 if (attnum <= 0)
7767 ereport(ERROR,
7768 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7769 errmsg("cannot alter system column \"%s\"",
7770 colName)));
7771
7772 if (attTup->attidentity)
7773 ereport(ERROR,
7774 (errcode(ERRCODE_SYNTAX_ERROR),
7775 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7776 colName, RelationGetRelationName(rel))));
7777
7778 /*
7779 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7780 */
7781 if (rel->rd_rel->relispartition)
7782 {
7783 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7784 Relation parent = table_open(parentId, AccessShareLock);
7785 TupleDesc tupDesc = RelationGetDescr(parent);
7786 AttrNumber parent_attnum;
7787
7788 parent_attnum = get_attnum(parentId, colName);
7789 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7790 ereport(ERROR,
7791 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7792 errmsg("column \"%s\" is marked NOT NULL in parent table",
7793 colName)));
7795 }
7796
7797 /*
7798 * Find the constraint that makes this column NOT NULL, and drop it.
7799 * dropconstraint_internal() resets attnotnull.
7800 */
7802 if (conTup == NULL)
7803 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7804 colName, RelationGetRelationName(rel));
7805
7806 /* The normal case: we have a pg_constraint row, remove it */
7807 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7808 false, lockmode);
7809 heap_freetuple(conTup);
7810
7811 InvokeObjectPostAlterHook(RelationRelationId,
7812 RelationGetRelid(rel), attnum);
7813
7814 table_close(attr_rel, RowExclusiveLock);
7815
7816 return address;
7817}
bool attnotnull
Definition: pg_attribute.h:123
HeapTuple findNotNullConstraintAttnum(Oid relid, AttrNumber attnum)

References AccessShareLock, attnotnull, attnum, DROP_RESTRICT, dropconstraint_internal(), elog, ereport, errcode(), errmsg(), ERROR, findNotNullConstraintAttnum(), get_attnum(), get_partition_parent(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidObjectAddress, InvokeObjectPostAlterHook, ObjectAddressSubSet, RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttName(), table_close(), table_open(), and TupleDescAttr().

Referenced by ATExecCmd().

◆ ATExecDropOf()

static void ATExecDropOf ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 18281 of file tablecmds.c.

18282{
18283 Oid relid = RelationGetRelid(rel);
18284 Relation relationRelation;
18285 HeapTuple tuple;
18286
18287 if (!OidIsValid(rel->rd_rel->reloftype))
18288 ereport(ERROR,
18289 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18290 errmsg("\"%s\" is not a typed table",
18292
18293 /*
18294 * We don't bother to check ownership of the type --- ownership of the
18295 * table is presumed enough rights. No lock required on the type, either.
18296 */
18297
18298 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18300
18301 /* Clear pg_class.reloftype */
18302 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18303 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18304 if (!HeapTupleIsValid(tuple))
18305 elog(ERROR, "cache lookup failed for relation %u", relid);
18306 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18307 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18308
18309 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18310
18311 heap_freetuple(tuple);
18312 table_close(relationRelation, RowExclusiveLock);
18313}

References CatalogTupleUpdate(), DEPENDENCY_NORMAL, drop_parent_dependency(), elog, ereport, errcode(), errmsg(), ERROR, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidOid, InvokeObjectPostAlterHook, ObjectIdGetDatum(), OidIsValid, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecEnableDisableRule()

static void ATExecEnableDisableRule ( Relation  rel,
const char *  rulename,
char  fires_when,
LOCKMODE  lockmode 
)
static

Definition at line 17145 of file tablecmds.c.

17147{
17148 EnableDisableRule(rel, rulename, fires_when);
17149
17150 InvokeObjectPostAlterHook(RelationRelationId,
17151 RelationGetRelid(rel), 0);
17152}
void EnableDisableRule(Relation rel, const char *rulename, char fires_when)

References EnableDisableRule(), InvokeObjectPostAlterHook, and RelationGetRelid.

Referenced by ATExecCmd().

◆ ATExecEnableDisableTrigger()

static void ATExecEnableDisableTrigger ( Relation  rel,
const char *  trigname,
char  fires_when,
bool  skip_system,
bool  recurse,
LOCKMODE  lockmode 
)
static

Definition at line 17127 of file tablecmds.c.

17130{
17131 EnableDisableTrigger(rel, trigname, InvalidOid,
17132 fires_when, skip_system, recurse,
17133 lockmode);
17134
17135 InvokeObjectPostAlterHook(RelationRelationId,
17136 RelationGetRelid(rel), 0);
17137}
void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: trigger.c:1726

References EnableDisableTrigger(), InvalidOid, InvokeObjectPostAlterHook, and RelationGetRelid.

Referenced by ATExecCmd().

◆ ATExecForceNoForceRowSecurity()

static void ATExecForceNoForceRowSecurity ( Relation  rel,
bool  force_rls 
)
static

Definition at line 18557 of file tablecmds.c.

18558{
18559 Relation pg_class;
18560 Oid relid;
18561 HeapTuple tuple;
18562
18563 relid = RelationGetRelid(rel);
18564
18565 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18566
18567 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18568
18569 if (!HeapTupleIsValid(tuple))
18570 elog(ERROR, "cache lookup failed for relation %u", relid);
18571
18572 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18573 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18574
18575 InvokeObjectPostAlterHook(RelationRelationId,
18576 RelationGetRelid(rel), 0);
18577
18578 table_close(pg_class, RowExclusiveLock);
18579 heap_freetuple(tuple);
18580}

References CatalogTupleUpdate(), elog, ERROR, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, ObjectIdGetDatum(), RelationGetRelid, RowExclusiveLock, SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecGenericOptions()

static void ATExecGenericOptions ( Relation  rel,
List options 
)
static

Definition at line 18586 of file tablecmds.c.

18587{
18588 Relation ftrel;
18589 ForeignServer *server;
18590 ForeignDataWrapper *fdw;
18591 HeapTuple tuple;
18592 bool isnull;
18593 Datum repl_val[Natts_pg_foreign_table];
18594 bool repl_null[Natts_pg_foreign_table];
18595 bool repl_repl[Natts_pg_foreign_table];
18596 Datum datum;
18597 Form_pg_foreign_table tableform;
18598
18599 if (options == NIL)
18600 return;
18601
18602 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18603
18604 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18605 ObjectIdGetDatum(rel->rd_id));
18606 if (!HeapTupleIsValid(tuple))
18607 ereport(ERROR,
18608 (errcode(ERRCODE_UNDEFINED_OBJECT),
18609 errmsg("foreign table \"%s\" does not exist",
18611 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18612 server = GetForeignServer(tableform->ftserver);
18613 fdw = GetForeignDataWrapper(server->fdwid);
18614
18615 memset(repl_val, 0, sizeof(repl_val));
18616 memset(repl_null, false, sizeof(repl_null));
18617 memset(repl_repl, false, sizeof(repl_repl));
18618
18619 /* Extract the current options */
18620 datum = SysCacheGetAttr(FOREIGNTABLEREL,
18621 tuple,
18622 Anum_pg_foreign_table_ftoptions,
18623 &isnull);
18624 if (isnull)
18625 datum = PointerGetDatum(NULL);
18626
18627 /* Transform the options */
18628 datum = transformGenericOptions(ForeignTableRelationId,
18629 datum,
18630 options,
18631 fdw->fdwvalidator);
18632
18633 if (PointerIsValid(DatumGetPointer(datum)))
18634 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18635 else
18636 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18637
18638 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18639
18640 /* Everything looks good - update the tuple */
18641
18642 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18643 repl_val, repl_null, repl_repl);
18644
18645 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18646
18647 /*
18648 * Invalidate relcache so that all sessions will refresh any cached plans
18649 * that might depend on the old options.
18650 */
18652
18653 InvokeObjectPostAlterHook(ForeignTableRelationId,
18654 RelationGetRelid(rel), 0);
18655
18657
18658 heap_freetuple(tuple);
18659}

References CacheInvalidateRelcache(), CatalogTupleUpdate(), DatumGetPointer(), ereport, errcode(), errmsg(), ERROR, ForeignServer::fdwid, ForeignDataWrapper::fdwvalidator, GetForeignDataWrapper(), GetForeignServer(), GETSTRUCT(), heap_freetuple(), heap_modify_tuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, NIL, ObjectIdGetDatum(), PointerGetDatum(), PointerIsValid, RelationData::rd_id, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopy1, SysCacheGetAttr(), HeapTupleData::t_self, table_close(), table_open(), and transformGenericOptions().

Referenced by ATExecCmd().

◆ ATExecReplicaIdentity()

static void ATExecReplicaIdentity ( Relation  rel,
ReplicaIdentityStmt stmt,
LOCKMODE  lockmode 
)
static

Definition at line 18413 of file tablecmds.c.

18414{
18415 Oid indexOid;
18416 Relation indexRel;
18417 int key;
18418
18419 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18420 {
18421 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18422 return;
18423 }
18424 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18425 {
18426 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18427 return;
18428 }
18429 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18430 {
18431 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18432 return;
18433 }
18434 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18435 {
18436 /* fallthrough */ ;
18437 }
18438 else
18439 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18440
18441 /* Check that the index exists */
18442 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18443 if (!OidIsValid(indexOid))
18444 ereport(ERROR,
18445 (errcode(ERRCODE_UNDEFINED_OBJECT),
18446 errmsg("index \"%s\" for table \"%s\" does not exist",
18447 stmt->name, RelationGetRelationName(rel))));
18448
18449 indexRel = index_open(indexOid, ShareLock);
18450
18451 /* Check that the index is on the relation we're altering. */
18452 if (indexRel->rd_index == NULL ||
18453 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18454 ereport(ERROR,
18455 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18456 errmsg("\"%s\" is not an index for table \"%s\"",
18457 RelationGetRelationName(indexRel),
18459
18460 /*
18461 * The AM must support uniqueness, and the index must in fact be unique.
18462 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18463 * exclusion), we can use that too.
18464 */
18465 if ((!indexRel->rd_indam->amcanunique ||
18466 !indexRel->rd_index->indisunique) &&
18467 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18468 ereport(ERROR,
18469 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18470 errmsg("cannot use non-unique index \"%s\" as replica identity",
18471 RelationGetRelationName(indexRel))));
18472 /* Deferred indexes are not guaranteed to be always unique. */
18473 if (!indexRel->rd_index->indimmediate)
18474 ereport(ERROR,
18475 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18476 errmsg("cannot use non-immediate index \"%s\" as replica identity",
18477 RelationGetRelationName(indexRel))));
18478 /* Expression indexes aren't supported. */
18479 if (RelationGetIndexExpressions(indexRel) != NIL)
18480 ereport(ERROR,
18481 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18482 errmsg("cannot use expression index \"%s\" as replica identity",
18483 RelationGetRelationName(indexRel))));
18484 /* Predicate indexes aren't supported. */
18485 if (RelationGetIndexPredicate(indexRel) != NIL)
18486 ereport(ERROR,
18487 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18488 errmsg("cannot use partial index \"%s\" as replica identity",
18489 RelationGetRelationName(indexRel))));
18490
18491 /* Check index for nullable columns. */
18492 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18493 {
18494 int16 attno = indexRel->rd_index->indkey.values[key];
18495 Form_pg_attribute attr;
18496
18497 /*
18498 * Reject any other system columns. (Going forward, we'll disallow
18499 * indexes containing such columns in the first place, but they might
18500 * exist in older branches.)
18501 */
18502 if (attno <= 0)
18503 ereport(ERROR,
18504 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18505 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18506 RelationGetRelationName(indexRel), attno)));
18507
18508 attr = TupleDescAttr(rel->rd_att, attno - 1);
18509 if (!attr->attnotnull)
18510 ereport(ERROR,
18511 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18512 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18513 RelationGetRelationName(indexRel),
18514 NameStr(attr->attname))));
18515 }
18516
18517 /* This index is suitable for use as a replica identity. Mark it. */
18518 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18519
18520 index_close(indexRel, NoLock);
18521}
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:535
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5207
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5094
bool amcanunique
Definition: amapi.h:256
struct IndexAmRoutine * rd_indam
Definition: rel.h:206
TupleDesc rd_att
Definition: rel.h:112
static void relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, bool is_internal)
Definition: tablecmds.c:18325

References IndexAmRoutine::amcanunique, elog, ereport, errcode(), errmsg(), ERROR, get_relname_relid(), index_close(), index_open(), IndexRelationGetNumberOfKeyAttributes, InvalidOid, sort-test::key, NameStr, NIL, NoLock, OidIsValid, RelationData::rd_att, RelationData::rd_indam, RelationData::rd_index, RelationData::rd_rel, relation_mark_replica_identity(), RelationGetIndexExpressions(), RelationGetIndexPredicate(), RelationGetRelationName, RelationGetRelid, ShareLock, stmt, and TupleDescAttr().

Referenced by ATExecCmd().

◆ ATExecSetAccessMethodNoStorage()

static void ATExecSetAccessMethodNoStorage ( Relation  rel,
Oid  newAccessMethodId 
)
static

Definition at line 16448 of file tablecmds.c.

16449{
16450 Relation pg_class;
16451 Oid oldAccessMethodId;
16452 HeapTuple tuple;
16453 Form_pg_class rd_rel;
16454 Oid reloid = RelationGetRelid(rel);
16455
16456 /*
16457 * Shouldn't be called on relations having storage; these are processed in
16458 * phase 3.
16459 */
16460 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16461
16462 /* Get a modifiable copy of the relation's pg_class row. */
16463 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16464
16465 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16466 if (!HeapTupleIsValid(tuple))
16467 elog(ERROR, "cache lookup failed for relation %u", reloid);
16468 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16469
16470 /* Update the pg_class row. */
16471 oldAccessMethodId = rd_rel->relam;
16472 rd_rel->relam = newAccessMethodId;
16473
16474 /* Leave if no update required */
16475 if (rd_rel->relam == oldAccessMethodId)
16476 {
16477 heap_freetuple(tuple);
16478 table_close(pg_class, RowExclusiveLock);
16479 return;
16480 }
16481
16482 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16483
16484 /*
16485 * Update the dependency on the new access method. No dependency is added
16486 * if the new access method is InvalidOid (default case). Be very careful
16487 * that this has to compare the previous value stored in pg_class with the
16488 * new one.
16489 */
16490 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16491 {
16492 ObjectAddress relobj,
16493 referenced;
16494
16495 /*
16496 * New access method is defined and there was no dependency
16497 * previously, so record a new one.
16498 */
16499 ObjectAddressSet(relobj, RelationRelationId, reloid);
16500 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16501 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16502 }
16503 else if (OidIsValid(oldAccessMethodId) &&
16504 !OidIsValid(rd_rel->relam))
16505 {
16506 /*
16507 * There was an access method defined, and no new one, so just remove
16508 * the existing dependency.
16509 */
16510 deleteDependencyRecordsForClass(RelationRelationId, reloid,
16511 AccessMethodRelationId,
16513 }
16514 else
16515 {
16516 Assert(OidIsValid(oldAccessMethodId) &&
16517 OidIsValid(rd_rel->relam));
16518
16519 /* Both are valid, so update the dependency */
16520 changeDependencyFor(RelationRelationId, reloid,
16521 AccessMethodRelationId,
16522 oldAccessMethodId, rd_rel->relam);
16523 }
16524
16525 /* make the relam and dependency changes visible */
16527
16528 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16529
16530 heap_freetuple(tuple);
16531 table_close(pg_class, RowExclusiveLock);
16532}

References Assert(), CatalogTupleUpdate(), changeDependencyFor(), CommandCounterIncrement(), deleteDependencyRecordsForClass(), DEPENDENCY_NORMAL, elog, ERROR, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, RelationData::rd_rel, recordDependencyOn(), RelationGetRelid, RowExclusiveLock, SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecSetCompression()

static ObjectAddress ATExecSetCompression ( Relation  rel,
const char *  column,
Node newValue,
LOCKMODE  lockmode 
)
static

Definition at line 18667 of file tablecmds.c.

18671{
18672 Relation attrel;
18673 HeapTuple tuple;
18674 Form_pg_attribute atttableform;
18676 char *compression;
18677 char cmethod;
18678 ObjectAddress address;
18679
18680 compression = strVal(newValue);
18681
18682 attrel = table_open(AttributeRelationId, RowExclusiveLock);
18683
18684 /* copy the cache entry so we can scribble on it below */
18685 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18686 if (!HeapTupleIsValid(tuple))
18687 ereport(ERROR,
18688 (errcode(ERRCODE_UNDEFINED_COLUMN),
18689 errmsg("column \"%s\" of relation \"%s\" does not exist",
18690 column, RelationGetRelationName(rel))));
18691
18692 /* prevent them from altering a system attribute */
18693 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18694 attnum = atttableform->attnum;
18695 if (attnum <= 0)
18696 ereport(ERROR,
18697 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18698 errmsg("cannot alter system column \"%s\"", column)));
18699
18700 /*
18701 * Check that column type is compressible, then get the attribute
18702 * compression method code
18703 */
18704 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18705
18706 /* update pg_attribute entry */
18707 atttableform->attcompression = cmethod;
18708 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18709
18710 InvokeObjectPostAlterHook(RelationRelationId,
18711 RelationGetRelid(rel),
18712 attnum);
18713
18714 /*
18715 * Apply the change to indexes as well (only for simple index columns,
18716 * matching behavior of index.c ConstructTupleDescriptor()).
18717 */
18718 SetIndexStorageProperties(rel, attrel, attnum,
18719 false, 0,
18720 true, cmethod,
18721 lockmode);
18722
18723 heap_freetuple(tuple);
18724
18726
18727 /* make changes visible */
18729
18730 ObjectAddressSubSet(address, RelationRelationId,
18731 RelationGetRelid(rel), attnum);
18732 return address;
18733}
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:21945
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:9095

References attnum, CatalogTupleUpdate(), CommandCounterIncrement(), ereport, errcode(), errmsg(), ERROR, GetAttributeCompression(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, ObjectAddressSubSet, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttName(), SetIndexStorageProperties(), strVal, HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecSetExpression()

static ObjectAddress ATExecSetExpression ( AlteredTableInfo tab,
Relation  rel,
const char *  colName,
Node newExpr,
LOCKMODE  lockmode 
)
static

Definition at line 8567 of file tablecmds.c.

8569{
8570 HeapTuple tuple;
8571 Form_pg_attribute attTup;
8573 char attgenerated;
8574 bool rewrite;
8575 Oid attrdefoid;
8576 ObjectAddress address;
8577 Expr *defval;
8579 RawColumnDefault *rawEnt;
8580
8581 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8582 if (!HeapTupleIsValid(tuple))
8583 ereport(ERROR,
8584 (errcode(ERRCODE_UNDEFINED_COLUMN),
8585 errmsg("column \"%s\" of relation \"%s\" does not exist",
8586 colName, RelationGetRelationName(rel))));
8587
8588 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8589
8590 attnum = attTup->attnum;
8591 if (attnum <= 0)
8592 ereport(ERROR,
8593 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8594 errmsg("cannot alter system column \"%s\"",
8595 colName)));
8596
8597 attgenerated = attTup->attgenerated;
8598 if (!attgenerated)
8599 ereport(ERROR,
8600 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8601 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8602 colName, RelationGetRelationName(rel))));
8603
8604 /*
8605 * TODO: This could be done, just need to recheck any constraints
8606 * afterwards.
8607 */
8608 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8609 rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8610 ereport(ERROR,
8611 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8612 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables with check constraints"),
8613 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8614 colName, RelationGetRelationName(rel))));
8615
8616 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8617 tab->verify_new_notnull = true;
8618
8619 /*
8620 * We need to prevent this because a change of expression could affect a
8621 * row filter and inject expressions that are not permitted in a row
8622 * filter. XXX We could try to have a more precise check to catch only
8623 * publications with row filters, or even re-verify the row filter
8624 * expressions.
8625 */
8626 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8628 ereport(ERROR,
8629 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8630 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables that are part of a publication"),
8631 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8632 colName, RelationGetRelationName(rel))));
8633
8634 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8635
8636 ReleaseSysCache(tuple);
8637
8638 if (rewrite)
8639 {
8640 /*
8641 * Clear all the missing values if we're rewriting the table, since
8642 * this renders them pointless.
8643 */
8645
8646 /* make sure we don't conflict with later attribute modifications */
8648
8649 /*
8650 * Find everything that depends on the column (constraints, indexes,
8651 * etc), and record enough information to let us recreate the objects
8652 * after rewrite.
8653 */
8655 }
8656
8657 /*
8658 * Drop the dependency records of the GENERATED expression, in particular
8659 * its INTERNAL dependency on the column, which would otherwise cause
8660 * dependency.c to refuse to perform the deletion.
8661 */
8662 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8663 if (!OidIsValid(attrdefoid))
8664 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8665 RelationGetRelid(rel), attnum);
8666 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8667
8668 /* Make above changes visible */
8670
8671 /*
8672 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8673 * safety, but at present we do not expect anything to depend on the
8674 * expression.
8675 */
8677 false, false);
8678
8679 /* Prepare to store the new expression, in the catalogs */
8680 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8681 rawEnt->attnum = attnum;
8682 rawEnt->raw_default = newExpr;
8683 rawEnt->generated = attgenerated;
8684
8685 /* Store the generated expression */
8687 false, true, false, NULL);
8688
8689 /* Make above new expression visible */
8691
8692 if (rewrite)
8693 {
8694 /* Prepare for table rewrite */
8695 defval = (Expr *) build_column_default(rel, attnum);
8696
8698 newval->attnum = attnum;
8699 newval->expr = expression_planner(defval);
8700 newval->is_generated = true;
8701
8702 tab->newvals = lappend(tab->newvals, newval);
8704 }
8705
8706 /* Drop any pg_statistic entry for the column */
8708
8709 InvokeObjectPostAlterHook(RelationRelationId,
8710 RelationGetRelid(rel), attnum);
8711
8712 ObjectAddressSubSet(address, RelationRelationId,
8713 RelationGetRelid(rel), attnum);
8714 return address;
8715}
List * GetRelationPublications(Oid relid)
uint16 num_check
Definition: tupdesc.h:44
TupleConstr * constr
Definition: tupdesc.h:141

References AddRelationNewConstraints(), AT_REWRITE_DEFAULT_VAL, AT_SetExpression, RawColumnDefault::attnum, attnum, build_column_default(), CommandCounterIncrement(), TupleDescData::constr, deleteDependencyRecordsFor(), DROP_RESTRICT, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, expression_planner(), RawColumnDefault::generated, GetAttrDefaultOid(), GetRelationPublications(), GETSTRUCT(), HeapTupleIsValid, InvokeObjectPostAlterHook, lappend(), list_make1, newval, AlteredTableInfo::newvals, NIL, TupleConstr::num_check, ObjectAddressSubSet, OidIsValid, palloc(), palloc0(), RawColumnDefault::raw_default, RelationData::rd_att, RelationClearMissing(), RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RememberAllDependentForRebuilding(), RemoveAttrDefault(), RemoveStatistics(), AlteredTableInfo::rewrite, SearchSysCacheAttName(), and AlteredTableInfo::verify_new_notnull.

Referenced by ATExecCmd().

◆ ATExecSetIdentity()

static ObjectAddress ATExecSetIdentity ( Relation  rel,
const char *  colName,
Node def,
LOCKMODE  lockmode,
bool  recurse,
bool  recursing 
)
static

Definition at line 8336 of file tablecmds.c.

8338{
8340 DefElem *generatedEl = NULL;
8341 HeapTuple tuple;
8342 Form_pg_attribute attTup;
8344 Relation attrelation;
8345 ObjectAddress address;
8346 bool ispartitioned;
8347
8348 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8349 if (ispartitioned && !recurse)
8350 ereport(ERROR,
8351 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8352 errmsg("cannot change identity column of only the partitioned table"),
8353 errhint("Do not specify the ONLY keyword.")));
8354
8355 if (rel->rd_rel->relispartition && !recursing)
8356 ereport(ERROR,
8357 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8358 errmsg("cannot change identity column of a partition"));
8359
8360 foreach(option, castNode(List, def))
8361 {
8362 DefElem *defel = lfirst_node(DefElem, option);
8363
8364 if (strcmp(defel->defname, "generated") == 0)
8365 {
8366 if (generatedEl)
8367 ereport(ERROR,
8368 (errcode(ERRCODE_SYNTAX_ERROR),
8369 errmsg("conflicting or redundant options")));
8370 generatedEl = defel;
8371 }
8372 else
8373 elog(ERROR, "option \"%s\" not recognized",
8374 defel->defname);
8375 }
8376
8377 /*
8378 * Even if there is nothing to change here, we run all the checks. There
8379 * will be a subsequent ALTER SEQUENCE that relies on everything being
8380 * there.
8381 */
8382
8383 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8384 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8385 if (!HeapTupleIsValid(tuple))
8386 ereport(ERROR,
8387 (errcode(ERRCODE_UNDEFINED_COLUMN),
8388 errmsg("column \"%s\" of relation \"%s\" does not exist",
8389 colName, RelationGetRelationName(rel))));
8390
8391 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8392 attnum = attTup->attnum;
8393
8394 if (attnum <= 0)
8395 ereport(ERROR,
8396 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8397 errmsg("cannot alter system column \"%s\"",
8398 colName)));
8399
8400 if (!attTup->attidentity)
8401 ereport(ERROR,
8402 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8403 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8404 colName, RelationGetRelationName(rel))));
8405
8406 if (generatedEl)
8407 {
8408 attTup->attidentity = defGetInt32(generatedEl);
8409 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8410
8411 InvokeObjectPostAlterHook(RelationRelationId,
8412 RelationGetRelid(rel),
8413 attTup->attnum);
8414 ObjectAddressSubSet(address, RelationRelationId,
8415 RelationGetRelid(rel), attnum);
8416 }
8417 else
8418 address = InvalidObjectAddress;
8419
8420 heap_freetuple(tuple);
8421 table_close(attrelation, RowExclusiveLock);
8422
8423 /*
8424 * Recurse to propagate the identity change to partitions. Identity is not
8425 * inherited in regular inheritance children.
8426 */
8427 if (generatedEl && recurse && ispartitioned)
8428 {
8429 List *children;
8430 ListCell *lc;
8431
8432 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8433
8434 foreach(lc, children)
8435 {
8436 Relation childrel;
8437
8438 childrel = table_open(lfirst_oid(lc), NoLock);
8439 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8440 table_close(childrel, NoLock);
8441 }
8442 }
8443
8444 return address;
8445}
int32 defGetInt32(DefElem *def)
Definition: define.c:149
#define lfirst_node(type, lc)
Definition: pg_list.h:176
char * defname
Definition: parsenodes.h:826

References ATExecSetIdentity(), attnum, castNode, CatalogTupleUpdate(), defGetInt32(), DefElem::defname, elog, ereport, errcode(), errhint(), errmsg(), ERROR, find_inheritance_children(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidObjectAddress, InvokeObjectPostAlterHook, lfirst_node, lfirst_oid, NoLock, ObjectAddressSubSet, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd(), and ATExecSetIdentity().

◆ ATExecSetNotNull()

static ObjectAddress ATExecSetNotNull ( List **  wqueue,
Relation  rel,
char *  conName,
char *  colName,
bool  recurse,
bool  recursing,
LOCKMODE  lockmode 
)
static

Definition at line 7903 of file tablecmds.c.

7905{
7906 HeapTuple tuple;
7908 ObjectAddress address;
7909 Constraint *constraint;
7910 CookedConstraint *ccon;
7911 List *cooked;
7912 bool is_no_inherit = false;
7913
7914 /* Guard against stack overflow due to overly deep inheritance tree. */
7916
7917 /* At top level, permission check was done in ATPrepCmd, else do it */
7918 if (recursing)
7919 {
7922 Assert(conName != NULL);
7923 }
7924
7925 attnum = get_attnum(RelationGetRelid(rel), colName);
7927 ereport(ERROR,
7928 (errcode(ERRCODE_UNDEFINED_COLUMN),
7929 errmsg("column \"%s\" of relation \"%s\" does not exist",
7930 colName, RelationGetRelationName(rel))));
7931
7932 /* Prevent them from altering a system attribute */
7933 if (attnum <= 0)
7934 ereport(ERROR,
7935 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7936 errmsg("cannot alter system column \"%s\"",
7937 colName)));
7938
7939 /* See if there's already a constraint */
7941 if (HeapTupleIsValid(tuple))
7942 {
7944 bool changed = false;
7945
7946 /*
7947 * Don't let a NO INHERIT constraint be changed into inherit.
7948 */
7949 if (conForm->connoinherit && recurse)
7950 ereport(ERROR,
7951 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7952 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7953 NameStr(conForm->conname),
7955
7956 /*
7957 * If we find an appropriate constraint, we're almost done, but just
7958 * need to change some properties on it: if we're recursing, increment
7959 * coninhcount; if not, set conislocal if not already set.
7960 */
7961 if (recursing)
7962 {
7963 if (pg_add_s16_overflow(conForm->coninhcount, 1,
7964 &conForm->coninhcount))
7965 ereport(ERROR,
7966 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7967 errmsg("too many inheritance parents"));
7968 changed = true;
7969 }
7970 else if (!conForm->conislocal)
7971 {
7972 conForm->conislocal = true;
7973 changed = true;
7974 }
7975 else if (!conForm->convalidated)
7976 {
7977 /*
7978 * Flip attnotnull and convalidated, and also validate the
7979 * constraint.
7980 */
7981 return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
7982 recurse, recursing, lockmode);
7983 }
7984
7985 if (changed)
7986 {
7987 Relation constr_rel;
7988
7989 constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7990
7991 CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7992 ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7993 table_close(constr_rel, RowExclusiveLock);
7994 }
7995
7996 if (changed)
7997 return address;
7998 else
7999 return InvalidObjectAddress;
8000 }
8001
8002 /*
8003 * If we're asked not to recurse, and children exist, raise an error for
8004 * partitioned tables. For inheritance, we act as if NO INHERIT had been
8005 * specified.
8006 */
8007 if (!recurse &&
8009 NoLock) != NIL)
8010 {
8011 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8012 ereport(ERROR,
8013 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8014 errmsg("constraint must be added to child tables too"),
8015 errhint("Do not specify the ONLY keyword."));
8016 else
8017 is_no_inherit = true;
8018 }
8019
8020 /*
8021 * No constraint exists; we must add one. First determine a name to use,
8022 * if we haven't already.
8023 */
8024 if (!recursing)
8025 {
8026 Assert(conName == NULL);
8028 colName, "not_null",
8030 NIL);
8031 }
8032
8033 constraint = makeNotNullConstraint(makeString(colName));
8034 constraint->is_no_inherit = is_no_inherit;
8035 constraint->conname = conName;
8036
8037 /* and do it */
8038 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8039 false, !recursing, false, NULL);
8040 ccon = linitial(cooked);
8041 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8042
8043 InvokeObjectPostAlterHook(RelationRelationId,
8044 RelationGetRelid(rel), attnum);
8045
8046 /* Mark pg_attribute.attnotnull for the column and queue validation */
8047 set_attnotnull(wqueue, rel, attnum, true, true);
8048
8049 /*
8050 * Recurse to propagate the constraint to children that don't have one.
8051 */
8052 if (recurse)
8053 {
8054 List *children;
8055
8057 lockmode);
8058
8059 foreach_oid(childoid, children)
8060 {
8061 Relation childrel = table_open(childoid, NoLock);
8062
8064
8065 ATExecSetNotNull(wqueue, childrel, conName, colName,
8066 recurse, true, lockmode);
8067 table_close(childrel, NoLock);
8068 }
8069 }
8070
8071 return address;
8072}
Constraint * makeNotNullConstraint(String *colname)
Definition: makefuncs.c:493
String * makeString(char *str)
Definition: value.c:63

References AddRelationNewConstraints(), Assert(), AT_AddConstraint, ATExecSetNotNull(), ATExecValidateConstraint(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, attnum, CatalogTupleUpdate(), check_stack_depth(), ChooseConstraintName(), CommandCounterIncrement(), Constraint::conname, CookedConstraint::conoid, ereport, errcode(), errhint(), errmsg(), ERROR, find_inheritance_children(), findNotNullConstraintAttnum(), foreach_oid, get_attnum(), GETSTRUCT(), HeapTupleIsValid, InvalidAttrNumber, InvalidObjectAddress, InvokeObjectPostAlterHook, Constraint::is_no_inherit, linitial, list_make1, makeNotNullConstraint(), makeString(), NameStr, NIL, NoLock, ObjectAddressSet, pg_add_s16_overflow(), RelationData::rd_rel, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, set_attnotnull(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecAlterConstrInheritability(), ATExecCmd(), and ATExecSetNotNull().

◆ ATExecSetOptions()

static ObjectAddress ATExecSetOptions ( Relation  rel,
const char *  colName,
Node options,
bool  isReset,
LOCKMODE  lockmode 
)
static

Definition at line 9016 of file tablecmds.c.

9018{
9019 Relation attrelation;
9020 HeapTuple tuple,
9021 newtuple;
9022 Form_pg_attribute attrtuple;
9024 Datum datum,
9025 newOptions;
9026 bool isnull;
9027 ObjectAddress address;
9028 Datum repl_val[Natts_pg_attribute];
9029 bool repl_null[Natts_pg_attribute];
9030 bool repl_repl[Natts_pg_attribute];
9031
9032 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9033
9034 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9035
9036 if (!HeapTupleIsValid(tuple))
9037 ereport(ERROR,
9038 (errcode(ERRCODE_UNDEFINED_COLUMN),
9039 errmsg("column \"%s\" of relation \"%s\" does not exist",
9040 colName, RelationGetRelationName(rel))));
9041 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9042
9043 attnum = attrtuple->attnum;
9044 if (attnum <= 0)
9045 ereport(ERROR,
9046 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9047 errmsg("cannot alter system column \"%s\"",
9048 colName)));
9049
9050 /* Generate new proposed attoptions (text array) */
9051 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9052 &isnull);
9053 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9054 castNode(List, options), NULL, NULL,
9055 false, isReset);
9056 /* Validate new options */
9057 (void) attribute_reloptions(newOptions, true);
9058
9059 /* Build new tuple. */
9060 memset(repl_null, false, sizeof(repl_null));
9061 memset(repl_repl, false, sizeof(repl_repl));
9062 if (newOptions != (Datum) 0)
9063 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9064 else
9065 repl_null[Anum_pg_attribute_attoptions - 1] = true;
9066 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9067 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9068 repl_val, repl_null, repl_repl);
9069
9070 /* Update system catalog. */
9071 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9072
9073 InvokeObjectPostAlterHook(RelationRelationId,
9074 RelationGetRelid(rel),
9075 attrtuple->attnum);
9076 ObjectAddressSubSet(address, RelationRelationId,
9077 RelationGetRelid(rel), attnum);
9078
9079 heap_freetuple(newtuple);
9080
9081 ReleaseSysCache(tuple);
9082
9083 table_close(attrelation, RowExclusiveLock);
9084
9085 return address;
9086}
Datum transformRelOptions(Datum oldOptions, List *defList, const char *namspace, const char *const validnsps[], bool acceptOidsOff, bool isReset)
Definition: reloptions.c:1167
bytea * attribute_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2096

References attnum, attribute_reloptions(), castNode, CatalogTupleUpdate(), ereport, errcode(), errmsg(), ERROR, GETSTRUCT(), heap_freetuple(), heap_modify_tuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, ObjectAddressSubSet, RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCacheAttName(), SysCacheGetAttr(), HeapTupleData::t_self, table_close(), table_open(), and transformRelOptions().

Referenced by ATExecCmd().

◆ ATExecSetRelOptions()

static void ATExecSetRelOptions ( Relation  rel,
List defList,
AlterTableType  operation,
LOCKMODE  lockmode 
)
static

Definition at line 16568 of file tablecmds.c.

16570{
16571 Oid relid;
16572 Relation pgclass;
16573 HeapTuple tuple;
16574 HeapTuple newtuple;
16575 Datum datum;
16576 Datum newOptions;
16577 Datum repl_val[Natts_pg_class];
16578 bool repl_null[Natts_pg_class];
16579 bool repl_repl[Natts_pg_class];
16580 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16581
16582 if (defList == NIL && operation != AT_ReplaceRelOptions)
16583 return; /* nothing to do */
16584
16585 pgclass = table_open(RelationRelationId, RowExclusiveLock);
16586
16587 /* Fetch heap tuple */
16588 relid = RelationGetRelid(rel);
16589 tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16590 if (!HeapTupleIsValid(tuple))
16591 elog(ERROR, "cache lookup failed for relation %u", relid);
16592
16593 if (operation == AT_ReplaceRelOptions)
16594 {
16595 /*
16596 * If we're supposed to replace the reloptions list, we just pretend
16597 * there were none before.
16598 */
16599 datum = (Datum) 0;
16600 }
16601 else
16602 {
16603 bool isnull;
16604
16605 /* Get the old reloptions */
16606 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16607 &isnull);
16608 if (isnull)
16609 datum = (Datum) 0;
16610 }
16611
16612 /* Generate new proposed reloptions (text array) */
16613 newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16614 operation == AT_ResetRelOptions);
16615
16616 /* Validate */
16617 switch (rel->rd_rel->relkind)
16618 {
16619 case RELKIND_RELATION:
16620 case RELKIND_MATVIEW:
16621 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16622 break;
16623 case RELKIND_PARTITIONED_TABLE:
16624 (void) partitioned_table_reloptions(newOptions, true);
16625 break;
16626 case RELKIND_VIEW:
16627 (void) view_reloptions(newOptions, true);
16628 break;
16629 case RELKIND_INDEX:
16630 case RELKIND_PARTITIONED_INDEX:
16631 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16632 break;
16633 case RELKIND_TOASTVALUE:
16634 /* fall through to error -- shouldn't ever get here */
16635 default:
16636 ereport(ERROR,
16637 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16638 errmsg("cannot set options for relation \"%s\"",
16640 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16641 break;
16642 }
16643
16644 /* Special-case validation of view options */
16645 if (rel->rd_rel->relkind == RELKIND_VIEW)
16646 {
16647 Query *view_query = get_view_query(rel);
16648 List *view_options = untransformRelOptions(newOptions);
16649 ListCell *cell;
16650 bool check_option = false;
16651
16652 foreach(cell, view_options)
16653 {
16654 DefElem *defel = (DefElem *) lfirst(cell);
16655
16656 if (strcmp(defel->defname, "check_option") == 0)
16657 check_option = true;
16658 }
16659
16660 /*
16661 * If the check option is specified, look to see if the view is
16662 * actually auto-updatable or not.
16663 */
16664 if (check_option)
16665 {
16666 const char *view_updatable_error =
16667 view_query_is_auto_updatable(view_query, true);
16668
16669 if (view_updatable_error)
16670 ereport(ERROR,
16671 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16672 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16673 errhint("%s", _(view_updatable_error))));
16674 }
16675 }
16676
16677 /*
16678 * All we need do here is update the pg_class row; the new options will be
16679 * propagated into relcaches during post-commit cache inval.
16680 */
16681 memset(repl_val, 0, sizeof(repl_val));
16682 memset(repl_null, false, sizeof(repl_null));
16683 memset(repl_repl, false, sizeof(repl_repl));
16684
16685 if (newOptions != (Datum) 0)
16686 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16687 else
16688 repl_null[Anum_pg_class_reloptions - 1] = true;
16689
16690 repl_repl[Anum_pg_class_reloptions - 1] = true;
16691
16692 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16693 repl_val, repl_null, repl_repl);
16694
16695 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16696 UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16697
16698 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16699
16700 heap_freetuple(newtuple);
16701
16702 ReleaseSysCache(tuple);
16703
16704 /* repeat the whole exercise for the toast table, if there's one */
16705 if (OidIsValid(rel->rd_rel->reltoastrelid))
16706 {
16707 Relation toastrel;
16708 Oid toastid = rel->rd_rel->reltoastrelid;
16709
16710 toastrel = table_open(toastid, lockmode);
16711
16712 /* Fetch heap tuple */
16713 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16714 if (!HeapTupleIsValid(tuple))
16715 elog(ERROR, "cache lookup failed for relation %u", toastid);
16716
16717 if (operation == AT_ReplaceRelOptions)
16718 {
16719 /*
16720 * If we're supposed to replace the reloptions list, we just
16721 * pretend there were none before.
16722 */
16723 datum = (Datum) 0;
16724 }
16725 else
16726 {
16727 bool isnull;
16728
16729 /* Get the old reloptions */
16730 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16731 &isnull);
16732 if (isnull)
16733 datum = (Datum) 0;
16734 }
16735
16736 newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16737 false, operation == AT_ResetRelOptions);
16738
16739 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16740
16741 memset(repl_val, 0, sizeof(repl_val));
16742 memset(repl_null, false, sizeof(repl_null));
16743 memset(repl_repl, false, sizeof(repl_repl));
16744
16745 if (newOptions != (Datum) 0)
16746 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16747 else
16748 repl_null[Anum_pg_class_reloptions - 1] = true;
16749
16750 repl_repl[Anum_pg_class_reloptions - 1] = true;
16751
16752 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16753 repl_val, repl_null, repl_repl);
16754
16755 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16756
16757 InvokeObjectPostAlterHookArg(RelationRelationId,
16758 RelationGetRelid(toastrel), 0,
16759 InvalidOid, true);
16760
16761 heap_freetuple(newtuple);
16762
16763 ReleaseSysCache(tuple);
16764
16765 table_close(toastrel, NoLock);
16766 }
16767
16768 table_close(pgclass, RowExclusiveLock);
16769}
#define _(x)
Definition: elog.c:91
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:200
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1342
bytea * view_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2025
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
Definition: reloptions.c:2081
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2011
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
Definition: reloptions.c:2046
#define HEAP_RELOPT_NAMESPACES
Definition: reloptions.h:61
Query * get_view_query(Relation view)
const char * view_query_is_auto_updatable(Query *viewquery, bool check_cols)
amoptions_function amoptions
Definition: amapi.h:302
HeapTuple SearchSysCacheLocked1(int cacheId, Datum key1)
Definition: syscache.c:287

References _, IndexAmRoutine::amoptions, AT_ReplaceRelOptions, AT_ResetRelOptions, CatalogTupleUpdate(), DefElem::defname, elog, ereport, errcode(), errdetail_relkind_not_supported(), errhint(), errmsg(), ERROR, get_view_query(), heap_freetuple(), heap_modify_tuple(), HEAP_RELOPT_NAMESPACES, heap_reloptions(), HeapTupleIsValid, index_reloptions(), InplaceUpdateTupleLock, InvalidOid, InvokeObjectPostAlterHook, InvokeObjectPostAlterHookArg, lfirst, NIL, NoLock, ObjectIdGetDatum(), OidIsValid, partitioned_table_reloptions(), RelationData::rd_indam, RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), SearchSysCacheLocked1(), SysCacheGetAttr(), HeapTupleData::t_self, table_close(), table_open(), transformRelOptions(), UnlockTuple(), untransformRelOptions(), view_query_is_auto_updatable(), and view_reloptions().

Referenced by ATExecCmd().

◆ ATExecSetRowSecurity()

static void ATExecSetRowSecurity ( Relation  rel,
bool  rls 
)
static

Definition at line 18527 of file tablecmds.c.

18528{
18529 Relation pg_class;
18530 Oid relid;
18531 HeapTuple tuple;
18532
18533 relid = RelationGetRelid(rel);
18534
18535 /* Pull the record for this relation and update it */
18536 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18537
18538 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18539
18540 if (!HeapTupleIsValid(tuple))
18541 elog(ERROR, "cache lookup failed for relation %u", relid);
18542
18543 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18544 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18545
18546 InvokeObjectPostAlterHook(RelationRelationId,
18547 RelationGetRelid(rel), 0);
18548
18549 table_close(pg_class, RowExclusiveLock);
18550 heap_freetuple(tuple);
18551}

References CatalogTupleUpdate(), elog, ERROR, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, ObjectIdGetDatum(), RelationGetRelid, RowExclusiveLock, SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecSetStatistics()

static ObjectAddress ATExecSetStatistics ( Relation  rel,
const char *  colName,
int16  colNum,
Node newValue,
LOCKMODE  lockmode 
)
static

Definition at line 8871 of file tablecmds.c.

8872{
8873 int newtarget = 0;
8874 bool newtarget_default;
8875 Relation attrelation;
8876 HeapTuple tuple,
8877 newtuple;
8878 Form_pg_attribute attrtuple;
8880 ObjectAddress address;
8881 Datum repl_val[Natts_pg_attribute];
8882 bool repl_null[Natts_pg_attribute];
8883 bool repl_repl[Natts_pg_attribute];
8884
8885 /*
8886 * We allow referencing columns by numbers only for indexes, since table
8887 * column numbers could contain gaps if columns are later dropped.
8888 */
8889 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8890 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8891 !colName)
8892 ereport(ERROR,
8893 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8894 errmsg("cannot refer to non-index column by number")));
8895
8896 /* -1 was used in previous versions for the default setting */
8897 if (newValue && intVal(newValue) != -1)
8898 {
8899 newtarget = intVal(newValue);
8900 newtarget_default = false;
8901 }
8902 else
8903 newtarget_default = true;
8904
8905 if (!newtarget_default)
8906 {
8907 /*
8908 * Limit target to a sane range
8909 */
8910 if (newtarget < 0)
8911 {
8912 ereport(ERROR,
8913 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8914 errmsg("statistics target %d is too low",
8915 newtarget)));
8916 }
8917 else if (newtarget > MAX_STATISTICS_TARGET)
8918 {
8919 newtarget = MAX_STATISTICS_TARGET;
8921 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8922 errmsg("lowering statistics target to %d",
8923 newtarget)));
8924 }
8925 }
8926
8927 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8928
8929 if (colName)
8930 {
8931 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8932
8933 if (!HeapTupleIsValid(tuple))
8934 ereport(ERROR,
8935 (errcode(ERRCODE_UNDEFINED_COLUMN),
8936 errmsg("column \"%s\" of relation \"%s\" does not exist",
8937 colName, RelationGetRelationName(rel))));
8938 }
8939 else
8940 {
8941 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8942
8943 if (!HeapTupleIsValid(tuple))
8944 ereport(ERROR,
8945 (errcode(ERRCODE_UNDEFINED_COLUMN),
8946 errmsg("column number %d of relation \"%s\" does not exist",
8947 colNum, RelationGetRelationName(rel))));
8948 }
8949
8950 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8951
8952 attnum = attrtuple->attnum;
8953 if (attnum <= 0)
8954 ereport(ERROR,
8955 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8956 errmsg("cannot alter system column \"%s\"",
8957 colName)));
8958
8959 /*
8960 * Prevent this as long as the ANALYZE code skips virtual generated
8961 * columns.
8962 */
8963 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8964 ereport(ERROR,
8965 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8966 errmsg("cannot alter statistics on virtual generated column \"%s\"",
8967 colName)));
8968
8969 if (rel->rd_rel->relkind == RELKIND_INDEX ||
8970 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8971 {
8972 if (attnum > rel->rd_index->indnkeyatts)
8973 ereport(ERROR,
8974 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8975 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8976 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8977 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8978 ereport(ERROR,
8979 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8980 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8981 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8982 errhint("Alter statistics on table column instead.")));
8983 }
8984
8985 /* Build new tuple. */
8986 memset(repl_null, false, sizeof(repl_null));
8987 memset(repl_repl, false, sizeof(repl_repl));
8988 if (!newtarget_default)
8989 repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8990 else
8991 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8992 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8993 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8994 repl_val, repl_null, repl_repl);
8995 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8996
8997 InvokeObjectPostAlterHook(RelationRelationId,
8998 RelationGetRelid(rel),
8999 attrtuple->attnum);
9000 ObjectAddressSubSet(address, RelationRelationId,
9001 RelationGetRelid(rel), attnum);
9002
9003 heap_freetuple(newtuple);
9004
9005 ReleaseSysCache(tuple);
9006
9007 table_close(attrelation, RowExclusiveLock);
9008
9009 return address;
9010}
HeapTuple SearchSysCacheAttNum(Oid relid, int16 attnum)
Definition: syscache.c:543
#define MAX_STATISTICS_TARGET
Definition: vacuum.h:324
#define intVal(v)
Definition: value.h:79

References attnum, CatalogTupleUpdate(), ereport, errcode(), errhint(), errmsg(), ERROR, GETSTRUCT(), heap_freetuple(), heap_modify_tuple(), HeapTupleIsValid, intVal, InvokeObjectPostAlterHook, MAX_STATISTICS_TARGET, NameStr, ObjectAddressSubSet, RelationData::rd_index, RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCacheAttName(), SearchSysCacheAttNum(), HeapTupleData::t_self, table_close(), table_open(), and WARNING.

Referenced by ATExecCmd().

◆ ATExecSetStorage()

static ObjectAddress ATExecSetStorage ( Relation  rel,
const char *  colName,
Node newValue,
LOCKMODE  lockmode 
)
static

Definition at line 9158 of file tablecmds.c.

9159{
9160 Relation attrelation;
9161 HeapTuple tuple;
9162 Form_pg_attribute attrtuple;
9164 ObjectAddress address;
9165
9166 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9167
9168 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9169
9170 if (!HeapTupleIsValid(tuple))
9171 ereport(ERROR,
9172 (errcode(ERRCODE_UNDEFINED_COLUMN),
9173 errmsg("column \"%s\" of relation \"%s\" does not exist",
9174 colName, RelationGetRelationName(rel))));
9175 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9176
9177 attnum = attrtuple->attnum;
9178 if (attnum <= 0)
9179 ereport(ERROR,
9180 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9181 errmsg("cannot alter system column \"%s\"",
9182 colName)));
9183
9184 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9185
9186 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9187
9188 InvokeObjectPostAlterHook(RelationRelationId,
9189 RelationGetRelid(rel),
9190 attrtuple->attnum);
9191
9192 /*
9193 * Apply the change to indexes as well (only for simple index columns,
9194 * matching behavior of index.c ConstructTupleDescriptor()).
9195 */
9196 SetIndexStorageProperties(rel, attrelation, attnum,
9197 true, attrtuple->attstorage,
9198 false, 0,
9199 lockmode);
9200
9201 heap_freetuple(tuple);
9202
9203 table_close(attrelation, RowExclusiveLock);
9204
9205 ObjectAddressSubSet(address, RelationRelationId,
9206 RelationGetRelid(rel), attnum);
9207 return address;
9208}
static char GetAttributeStorage(Oid atttypid, const char *storagemode)
Definition: tablecmds.c:21983

References attnum, CatalogTupleUpdate(), ereport, errcode(), errmsg(), ERROR, GetAttributeStorage(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, ObjectAddressSubSet, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttName(), SetIndexStorageProperties(), strVal, HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecSetTableSpace()

static void ATExecSetTableSpace ( Oid  tableOid,
Oid  newTableSpace,
LOCKMODE  lockmode 
)
static

Definition at line 16776 of file tablecmds.c.

16777{
16778 Relation rel;
16779 Oid reltoastrelid;
16780 RelFileNumber newrelfilenumber;
16781 RelFileLocator newrlocator;
16782 List *reltoastidxids = NIL;
16783 ListCell *lc;
16784
16785 /*
16786 * Need lock here in case we are recursing to toast table or index
16787 */
16788 rel = relation_open(tableOid, lockmode);
16789
16790 /* Check first if relation can be moved to new tablespace */
16791 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16792 {
16793 InvokeObjectPostAlterHook(RelationRelationId,
16794 RelationGetRelid(rel), 0);
16795 relation_close(rel, NoLock);
16796 return;
16797 }
16798
16799 reltoastrelid = rel->rd_rel->reltoastrelid;
16800 /* Fetch the list of indexes on toast relation if necessary */
16801 if (OidIsValid(reltoastrelid))
16802 {
16803 Relation toastRel = relation_open(reltoastrelid, lockmode);
16804
16805 reltoastidxids = RelationGetIndexList(toastRel);
16806 relation_close(toastRel, lockmode);
16807 }
16808
16809 /*
16810 * Relfilenumbers are not unique in databases across tablespaces, so we
16811 * need to allocate a new one in the new tablespace.
16812 */
16813 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16814 rel->rd_rel->relpersistence);
16815
16816 /* Open old and new relation */
16817 newrlocator = rel->rd_locator;
16818 newrlocator.relNumber = newrelfilenumber;
16819 newrlocator.spcOid = newTableSpace;
16820
16821 /* hand off to AM to actually create new rel storage and copy the data */
16822 if (rel->rd_rel->relkind == RELKIND_INDEX)
16823 {
16824 index_copy_data(rel, newrlocator);
16825 }
16826 else
16827 {
16828 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16829 table_relation_copy_data(rel, &newrlocator);
16830 }
16831
16832 /*
16833 * Update the pg_class row.
16834 *
16835 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16836 * executed on pg_class or its indexes (the above copy wouldn't contain
16837 * the updated pg_class entry), but that's forbidden with
16838 * CheckRelationTableSpaceMove().
16839 */
16840 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16841
16842 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16843
16845
16846 relation_close(rel, NoLock);
16847
16848 /* Make sure the reltablespace change is visible */
16850
16851 /* Move associated toast relation and/or indexes, too */
16852 if (OidIsValid(reltoastrelid))
16853 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16854 foreach(lc, reltoastidxids)
16855 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16856
16857 /* Clean up */
16858 list_free(reltoastidxids);
16859}
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition: catalog.c:559
void RelationAssumeNewRelfilelocator(Relation relation)
Definition: relcache.c:3976
Oid RelFileNumber
Definition: relpath.h:25
RelFileNumber relNumber
static void table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
Definition: tableam.h:1611
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:16776
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3683
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3740
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:17070

References Assert(), ATExecSetTableSpace(), CheckRelationTableSpaceMove(), CommandCounterIncrement(), GetNewRelFileNumber(), index_copy_data(), InvokeObjectPostAlterHook, lfirst_oid, list_free(), NIL, NoLock, OidIsValid, RelationData::rd_locator, RelationData::rd_rel, relation_close(), relation_open(), RelationAssumeNewRelfilelocator(), RelationGetIndexList(), RelationGetRelid, RelFileLocator::relNumber, SetRelationTableSpace(), RelFileLocator::spcOid, and table_relation_copy_data().

Referenced by ATExecSetTableSpace(), and ATRewriteTables().

◆ ATExecSetTableSpaceNoStorage()

static void ATExecSetTableSpaceNoStorage ( Relation  rel,
Oid  newTableSpace 
)
static

Definition at line 16869 of file tablecmds.c.

16870{
16871 /*
16872 * Shouldn't be called on relations having storage; these are processed in
16873 * phase 3.
16874 */
16875 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16876
16877 /* check if relation can be moved to its new tablespace */
16878 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16879 {
16880 InvokeObjectPostAlterHook(RelationRelationId,
16881 RelationGetRelid(rel),
16882 0);
16883 return;
16884 }
16885
16886 /* Update can be done, so change reltablespace */
16887 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16888
16889 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16890
16891 /* Make sure the reltablespace change is visible */
16893}

References Assert(), CheckRelationTableSpaceMove(), CommandCounterIncrement(), InvalidOid, InvokeObjectPostAlterHook, RelationData::rd_rel, RelationGetRelid, and SetRelationTableSpace().

Referenced by ATExecCmd().

◆ ATExecValidateConstraint()

static ObjectAddress ATExecValidateConstraint ( List **  wqueue,
Relation  rel,
char *  constrName,
bool  recurse,
bool  recursing,
LOCKMODE  lockmode 
)
static

Definition at line 12878 of file tablecmds.c.

12880{
12881 Relation conrel;
12882 SysScanDesc scan;
12883 ScanKeyData skey[3];
12884 HeapTuple tuple;
12886 ObjectAddress address;
12887
12888 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12889
12890 /*
12891 * Find and check the target constraint
12892 */
12893 ScanKeyInit(&skey[0],
12894 Anum_pg_constraint_conrelid,
12895 BTEqualStrategyNumber, F_OIDEQ,
12897 ScanKeyInit(&skey[1],
12898 Anum_pg_constraint_contypid,
12899 BTEqualStrategyNumber, F_OIDEQ,
12901 ScanKeyInit(&skey[2],
12902 Anum_pg_constraint_conname,
12903 BTEqualStrategyNumber, F_NAMEEQ,
12904 CStringGetDatum(constrName));
12905 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12906 true, NULL, 3, skey);
12907
12908 /* There can be at most one matching row */
12909 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12910 ereport(ERROR,
12911 (errcode(ERRCODE_UNDEFINED_OBJECT),
12912 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12913 constrName, RelationGetRelationName(rel))));
12914
12915 con = (Form_pg_constraint) GETSTRUCT(tuple);
12916 if (con->contype != CONSTRAINT_FOREIGN &&
12917 con->contype != CONSTRAINT_CHECK &&
12918 con->contype != CONSTRAINT_NOTNULL)
12919 ereport(ERROR,
12920 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12921 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key, check, or not-null constraint",
12922 constrName, RelationGetRelationName(rel)));
12923
12924 if (!con->conenforced)
12925 ereport(ERROR,
12926 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12927 errmsg("cannot validate NOT ENFORCED constraint")));
12928
12929 if (!con->convalidated)
12930 {
12931 if (con->contype == CONSTRAINT_FOREIGN)
12932 {
12933 QueueFKConstraintValidation(wqueue, conrel, rel, tuple, lockmode);
12934 }
12935 else if (con->contype == CONSTRAINT_CHECK)
12936 {
12937 QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12938 tuple, recurse, recursing, lockmode);
12939 }
12940 else if (con->contype == CONSTRAINT_NOTNULL)
12941 {
12942 QueueNNConstraintValidation(wqueue, conrel, rel,
12943 tuple, recurse, recursing, lockmode);
12944 }
12945
12946 ObjectAddressSet(address, ConstraintRelationId, con->oid);
12947 }
12948 else
12949 address = InvalidObjectAddress; /* already validated */
12950
12951 systable_endscan(scan);
12952
12954
12955 return address;
12956}
static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13169
static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12966
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13066

References BTEqualStrategyNumber, CStringGetDatum(), ereport, errcode(), errmsg(), ERROR, GETSTRUCT(), HeapTupleIsValid, InvalidObjectAddress, InvalidOid, ObjectAddressSet, ObjectIdGetDatum(), QueueCheckConstraintValidation(), QueueFKConstraintValidation(), QueueNNConstraintValidation(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecCmd(), ATExecSetNotNull(), QueueCheckConstraintValidation(), and QueueNNConstraintValidation().

◆ ATGetQueueEntry()

static AlteredTableInfo * ATGetQueueEntry ( List **  wqueue,
Relation  rel 
)
static

Definition at line 6552 of file tablecmds.c.

6553{
6554 Oid relid = RelationGetRelid(rel);
6555 AlteredTableInfo *tab;
6556 ListCell *ltab;
6557
6558 foreach(ltab, *wqueue)
6559 {
6560 tab = (AlteredTableInfo *) lfirst(ltab);
6561 if (tab->relid == relid)
6562 return tab;
6563 }
6564
6565 /*
6566 * Not there, so add it. Note that we make a copy of the relation's
6567 * existing descriptor before anything interesting can happen to it.
6568 */
6569 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6570 tab->relid = relid;
6571 tab->rel = NULL; /* set later */
6572 tab->relkind = rel->rd_rel->relkind;
6575 tab->chgAccessMethod = false;
6577 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6578 tab->chgPersistence = false;
6579
6580 *wqueue = lappend(*wqueue, tab);
6581
6582 return tab;
6583}
char newrelpersistence
Definition: tablecmds.c:197
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:333

References AlteredTableInfo::chgAccessMethod, AlteredTableInfo::chgPersistence, CreateTupleDescCopyConstr(), InvalidOid, lappend(), lfirst, AlteredTableInfo::newAccessMethod, AlteredTableInfo::newrelpersistence, AlteredTableInfo::newTableSpace, AlteredTableInfo::oldDesc, palloc0(), RelationData::rd_rel, AlteredTableInfo::rel, RelationGetDescr, RelationGetRelid, AlteredTableInfo::relid, and AlteredTableInfo::relkind.

Referenced by addFkRecurseReferencing(), ATAddCheckNNConstraint(), ATExecAddColumn(), ATExecAlterConstrEnforceability(), ATPostAlterTypeParse(), ATPrepCmd(), DetachAddConstraintIfNeeded(), QueueCheckConstraintValidation(), QueueFKConstraintValidation(), QueueNNConstraintValidation(), QueuePartitionConstraintValidation(), and set_attnotnull().

◆ ATParseTransformCmd()

static AlterTableCmd * ATParseTransformCmd ( List **  wqueue,
AlteredTableInfo tab,
Relation  rel,
AlterTableCmd cmd,
bool  recurse,
LOCKMODE  lockmode,
AlterTablePass  cur_pass,
AlterTableUtilityContext context 
)
static

Definition at line 5701 of file tablecmds.c.

5704{
5705 AlterTableCmd *newcmd = NULL;
5707 List *beforeStmts;
5708 List *afterStmts;
5709 ListCell *lc;
5710
5711 /* Gin up an AlterTableStmt with just this subcommand and this table */
5712 atstmt->relation =
5715 -1);
5716 atstmt->relation->inh = recurse;
5717 atstmt->cmds = list_make1(cmd);
5718 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5719 atstmt->missing_ok = false;
5720
5721 /* Transform the AlterTableStmt */
5723 atstmt,
5724 context->queryString,
5725 &beforeStmts,
5726 &afterStmts);
5727
5728 /* Execute any statements that should happen before these subcommand(s) */
5729 foreach(lc, beforeStmts)
5730 {
5731 Node *stmt = (Node *) lfirst(lc);
5732
5735 }
5736
5737 /* Examine the transformed subcommands and schedule them appropriately */
5738 foreach(lc, atstmt->cmds)
5739 {
5741 AlterTablePass pass;
5742
5743 /*
5744 * This switch need only cover the subcommand types that can be added
5745 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5746 * executing the subcommand immediately, as a substitute for the
5747 * original subcommand. (Note, however, that this does cause
5748 * AT_AddConstraint subcommands to be rescheduled into later passes,
5749 * which is important for index and foreign key constraints.)
5750 *
5751 * We assume we needn't do any phase-1 checks for added subcommands.
5752 */
5753 switch (cmd2->subtype)
5754 {
5755 case AT_AddIndex:
5756 pass = AT_PASS_ADD_INDEX;
5757 break;
5760 break;
5761 case AT_AddConstraint:
5762 /* Recursion occurs during execution phase */
5763 if (recurse)
5764 cmd2->recurse = true;
5765 switch (castNode(Constraint, cmd2->def)->contype)
5766 {
5767 case CONSTR_NOTNULL:
5768 pass = AT_PASS_COL_ATTRS;
5769 break;
5770 case CONSTR_PRIMARY:
5771 case CONSTR_UNIQUE:
5772 case CONSTR_EXCLUSION:
5774 break;
5775 default:
5777 break;
5778 }
5779 break;
5781 /* This command never recurses */
5782 /* No command-specific prep needed */
5783 pass = AT_PASS_MISC;
5784 break;
5785 default:
5786 pass = cur_pass;
5787 break;
5788 }
5789
5790 if (pass < cur_pass)
5791 {
5792 /* Cannot schedule into a pass we already finished */
5793 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5794 pass);
5795 }
5796 else if (pass > cur_pass)
5797 {
5798 /* OK, queue it up for later */
5799 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5800 }
5801 else
5802 {
5803 /*
5804 * We should see at most one subcommand for the current pass,
5805 * which is the transformed version of the original subcommand.
5806 */
5807 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5808 {
5809 /* Found the transformed version of our subcommand */
5810 newcmd = cmd2;
5811 }
5812 else
5813 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5814 pass);
5815 }
5816 }
5817
5818 /* Queue up any after-statements to happen at the end */
5819 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5820
5821 return newcmd;
5822}
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
AlterTableStmt * transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, const char *queryString, List **beforeStmts, List **afterStmts)
RangeVar * relation
Definition: parsenodes.h:2401
ObjectType objtype
Definition: parsenodes.h:2403
List * afterStmts
Definition: tablecmds.c:189
List * subcmds[AT_NUM_PASSES]
Definition: tablecmds.c:185
bool inh
Definition: primnodes.h:86
void ProcessUtilityForAlterTable(Node *stmt, AlterTableUtilityContext *context)
Definition: utility.c:1959

References AlteredTableInfo::afterStmts, AT_AddConstraint, AT_AddIndex, AT_AddIndexConstraint, AT_AlterColumnGenericOptions, AT_PASS_ADD_INDEX, AT_PASS_ADD_INDEXCONSTR, AT_PASS_ADD_OTHERCONSTR, AT_PASS_COL_ATTRS, AT_PASS_MISC, castNode, AlterTableStmt::cmds, CommandCounterIncrement(), CONSTR_EXCLUSION, CONSTR_NOTNULL, CONSTR_PRIMARY, CONSTR_UNIQUE, AlterTableCmd::def, elog, ERROR, get_namespace_name(), RangeVar::inh, lappend(), lfirst, lfirst_node, list_concat(), list_make1, makeNode, makeRangeVar(), AlterTableStmt::missing_ok, OBJECT_TABLE, AlterTableStmt::objtype, ProcessUtilityForAlterTable(), pstrdup(), AlterTableUtilityContext::queryString, AlterTableCmd::recurse, AlterTableStmt::relation, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, stmt, AlteredTableInfo::subcmds, AlterTableCmd::subtype, and transformAlterTableStmt().

Referenced by ATExecAddColumn(), ATExecCmd(), and ATPrepCmd().

◆ ATPostAlterTypeCleanup()

static void ATPostAlterTypeCleanup ( List **  wqueue,
AlteredTableInfo tab,
LOCKMODE  lockmode 
)
static

Definition at line 15382 of file tablecmds.c.

15383{
15384 ObjectAddress obj;
15385 ObjectAddresses *objects;
15386 ListCell *def_item;
15387 ListCell *oid_item;
15388
15389 /*
15390 * Collect all the constraints and indexes to drop so we can process them
15391 * in a single call. That way we don't have to worry about dependencies
15392 * among them.
15393 */
15394 objects = new_object_addresses();
15395
15396 /*
15397 * Re-parse the index and constraint definitions, and attach them to the
15398 * appropriate work queue entries. We do this before dropping because in
15399 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
15400 * lock on the table the constraint is attached to, and we need to get
15401 * that before reparsing/dropping.
15402 *
15403 * We can't rely on the output of deparsing to tell us which relation to
15404 * operate on, because concurrent activity might have made the name
15405 * resolve differently. Instead, we've got to use the OID of the
15406 * constraint or index we're processing to figure out which relation to
15407 * operate on.
15408 */
15409 forboth(oid_item, tab->changedConstraintOids,
15410 def_item, tab->changedConstraintDefs)
15411 {
15412 Oid oldId = lfirst_oid(oid_item);
15413 HeapTuple tup;
15415 Oid relid;
15416 Oid confrelid;
15417 char contype;
15418 bool conislocal;
15419
15420 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15421 if (!HeapTupleIsValid(tup)) /* should not happen */
15422 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15423 con = (Form_pg_constraint) GETSTRUCT(tup);
15424 if (OidIsValid(con->conrelid))
15425 relid = con->conrelid;
15426 else
15427 {
15428 /* must be a domain constraint */
15429 relid = get_typ_typrelid(getBaseType(con->contypid));
15430 if (!OidIsValid(relid))
15431 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15432 }
15433 confrelid = con->confrelid;
15434 contype = con->contype;
15435 conislocal = con->conislocal;
15436 ReleaseSysCache(tup);
15437
15438 ObjectAddressSet(obj, ConstraintRelationId, oldId);
15439 add_exact_object_address(&obj, objects);
15440
15441 /*
15442 * If the constraint is inherited (only), we don't want to inject a
15443 * new definition here; it'll get recreated when
15444 * ATAddCheckNNConstraint recurses from adding the parent table's
15445 * constraint. But we had to carry the info this far so that we can
15446 * drop the constraint below.
15447 */
15448 if (!conislocal)
15449 continue;
15450
15451 /*
15452 * When rebuilding an FK constraint that references the table we're
15453 * modifying, we might not yet have any lock on the FK's table, so get
15454 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
15455 * step, so there's no value in asking for anything weaker.
15456 */
15457 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
15459
15460 ATPostAlterTypeParse(oldId, relid, confrelid,
15461 (char *) lfirst(def_item),
15462 wqueue, lockmode, tab->rewrite);
15463 }
15464 forboth(oid_item, tab->changedIndexOids,
15465 def_item, tab->changedIndexDefs)
15466 {
15467 Oid oldId = lfirst_oid(oid_item);
15468 Oid relid;
15469
15470 relid = IndexGetRelation(oldId, false);
15471 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15472 (char *) lfirst(def_item),
15473 wqueue, lockmode, tab->rewrite);
15474
15475 ObjectAddressSet(obj, RelationRelationId, oldId);
15476 add_exact_object_address(&obj, objects);
15477 }
15478
15479 /* add dependencies for new statistics */
15480 forboth(oid_item, tab->changedStatisticsOids,
15481 def_item, tab->changedStatisticsDefs)
15482 {
15483 Oid oldId = lfirst_oid(oid_item);
15484 Oid relid;
15485
15486 relid = StatisticsGetRelation(oldId, false);
15487 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15488 (char *) lfirst(def_item),
15489 wqueue, lockmode, tab->rewrite);
15490
15491 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15492 add_exact_object_address(&obj, objects);
15493 }
15494
15495 /*
15496 * Queue up command to restore replica identity index marking
15497 */
15498 if (tab->replicaIdentityIndex)
15499 {
15502
15503 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15504 subcmd->name = tab->replicaIdentityIndex;
15506 cmd->def = (Node *) subcmd;
15507
15508 /* do it after indexes and constraints */
15511 }
15512
15513 /*
15514 * Queue up command to restore marking of index used for cluster.
15515 */
15516 if (tab->clusterOnIndex)
15517 {
15519
15520 cmd->subtype = AT_ClusterOn;
15521 cmd->name = tab->clusterOnIndex;
15522
15523 /* do it after indexes and constraints */
15526 }
15527
15528 /*
15529 * It should be okay to use DROP_RESTRICT here, since nothing else should
15530 * be depending on these objects.
15531 */
15533
15534 free_object_addresses(objects);
15535
15536 /*
15537 * The objects will get recreated during subsequent passes over the work
15538 * queue.
15539 */
15540}
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3583
Oid get_typ_typrelid(Oid typid)
Definition: lsyscache.c:2871
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition: statscmds.c:916
List * changedConstraintDefs
Definition: tablecmds.c:203
List * changedStatisticsDefs
Definition: tablecmds.c:209
char * clusterOnIndex
Definition: tablecmds.c:207
char * replicaIdentityIndex
Definition: tablecmds.c:206
List * changedStatisticsOids
Definition: tablecmds.c:208
List * changedIndexDefs
Definition: tablecmds.c:205
List * changedIndexOids
Definition: tablecmds.c:204
List * changedConstraintOids
Definition: tablecmds.c:202
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
Definition: tablecmds.c:15551

References AccessExclusiveLock, add_exact_object_address(), ATPostAlterTypeParse(), AlteredTableInfo::changedConstraintDefs, AlteredTableInfo::changedConstraintOids, elog, ERROR, forboth, get_typ_typrelid(), getBaseType(), GETSTRUCT(), HeapTupleIsValid, lfirst, lfirst_oid, LockRelationOid(), new_object_addresses(), ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), AlteredTableInfo::relid, AlteredTableInfo::rewrite, and SearchSysCache1().

Referenced by ATRewriteCatalogs().

◆ ATPostAlterTypeParse()

static void ATPostAlterTypeParse ( Oid  oldId,
Oid  oldRelId,
Oid  refRelId,
char *  cmd,
List **  wqueue,
LOCKMODE  lockmode,
bool  rewrite 
)
static

Definition at line 15551 of file tablecmds.c.

15553{
15554 List *raw_parsetree_list;
15555 List *querytree_list;
15556 ListCell *list_item;
15557 Relation rel;
15558
15559 /*
15560 * We expect that we will get only ALTER TABLE and CREATE INDEX
15561 * statements. Hence, there is no need to pass them through
15562 * parse_analyze_*() or the rewriter, but instead we need to pass them
15563 * through parse_utilcmd.c to make them ready for execution.
15564 */
15565 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15566 querytree_list = NIL;
15567 foreach(list_item, raw_parsetree_list)
15568 {
15569 RawStmt *rs = lfirst_node(RawStmt, list_item);
15570 Node *stmt = rs->stmt;
15571
15572 if (IsA(stmt, IndexStmt))
15573 querytree_list = lappend(querytree_list,
15574 transformIndexStmt(oldRelId,
15575 (IndexStmt *) stmt,
15576 cmd));
15577 else if (IsA(stmt, AlterTableStmt))
15578 {
15579 List *beforeStmts;
15580 List *afterStmts;
15581
15582 stmt = (Node *) transformAlterTableStmt(oldRelId,
15583 (AlterTableStmt *) stmt,
15584 cmd,
15585 &beforeStmts,
15586 &afterStmts);
15587 querytree_list = list_concat(querytree_list, beforeStmts);
15588 querytree_list = lappend(querytree_list, stmt);
15589 querytree_list = list_concat(querytree_list, afterStmts);
15590 }
15591 else if (IsA(stmt, CreateStatsStmt))
15592 querytree_list = lappend(querytree_list,
15593 transformStatsStmt(oldRelId,
15595 cmd));
15596 else
15597 querytree_list = lappend(querytree_list, stmt);
15598 }
15599
15600 /* Caller should already have acquired whatever lock we need. */
15601 rel = relation_open(oldRelId, NoLock);
15602
15603 /*
15604 * Attach each generated command to the proper place in the work queue.
15605 * Note this could result in creation of entirely new work-queue entries.
15606 *
15607 * Also note that we have to tweak the command subtypes, because it turns
15608 * out that re-creation of indexes and constraints has to act a bit
15609 * differently from initial creation.
15610 */
15611 foreach(list_item, querytree_list)
15612 {
15613 Node *stm = (Node *) lfirst(list_item);
15614 AlteredTableInfo *tab;
15615
15616 tab = ATGetQueueEntry(wqueue, rel);
15617
15618 if (IsA(stm, IndexStmt))
15619 {
15620 IndexStmt *stmt = (IndexStmt *) stm;
15621 AlterTableCmd *newcmd;
15622
15623 if (!rewrite)
15624 TryReuseIndex(oldId, stmt);
15625 stmt->reset_default_tblspc = true;
15626 /* keep the index's comment */
15627 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15628
15629 newcmd = makeNode(AlterTableCmd);
15630 newcmd->subtype = AT_ReAddIndex;
15631 newcmd->def = (Node *) stmt;
15633 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15634 }
15635 else if (IsA(stm, AlterTableStmt))
15636 {
15638 ListCell *lcmd;
15639
15640 foreach(lcmd, stmt->cmds)
15641 {
15643
15644 if (cmd->subtype == AT_AddIndex)
15645 {
15646 IndexStmt *indstmt;
15647 Oid indoid;
15648
15649 indstmt = castNode(IndexStmt, cmd->def);
15650 indoid = get_constraint_index(oldId);
15651
15652 if (!rewrite)
15653 TryReuseIndex(indoid, indstmt);
15654 /* keep any comment on the index */
15655 indstmt->idxcomment = GetComment(indoid,
15656 RelationRelationId, 0);
15657 indstmt->reset_default_tblspc = true;
15658
15659 cmd->subtype = AT_ReAddIndex;
15661 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15662
15663 /* recreate any comment on the constraint */
15666 oldId,
15667 rel,
15668 NIL,
15669 indstmt->idxname);
15670 }
15671 else if (cmd->subtype == AT_AddConstraint)
15672 {
15673 Constraint *con = castNode(Constraint, cmd->def);
15674
15675 con->old_pktable_oid = refRelId;
15676 /* rewriting neither side of a FK */
15677 if (con->contype == CONSTR_FOREIGN &&
15678 !rewrite && tab->rewrite == 0)
15679 TryReuseForeignKey(oldId, con);
15680 con->reset_default_tblspc = true;
15684
15685 /*
15686 * Recreate any comment on the constraint. If we have
15687 * recreated a primary key, then transformTableConstraint
15688 * has added an unnamed not-null constraint here; skip
15689 * this in that case.
15690 */
15691 if (con->conname)
15694 oldId,
15695 rel,
15696 NIL,
15697 con->conname);
15698 else
15699 Assert(con->contype == CONSTR_NOTNULL);
15700 }
15701 else
15702 elog(ERROR, "unexpected statement subtype: %d",
15703 (int) cmd->subtype);
15704 }
15705 }
15706 else if (IsA(stm, AlterDomainStmt))
15707 {
15709
15710 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
15711 {
15712 Constraint *con = castNode(Constraint, stmt->def);
15714
15716 cmd->def = (Node *) stmt;
15719
15720 /* recreate any comment on the constraint */
15723 oldId,
15724 NULL,
15725 stmt->typeName,
15726 con->conname);
15727 }
15728 else
15729 elog(ERROR, "unexpected statement subtype: %d",
15730 (int) stmt->subtype);
15731 }
15732 else if (IsA(stm, CreateStatsStmt))
15733 {
15735 AlterTableCmd *newcmd;
15736
15737 /* keep the statistics object's comment */
15738 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15739
15740 newcmd = makeNode(AlterTableCmd);
15741 newcmd->subtype = AT_ReAddStatistics;
15742 newcmd->def = (Node *) stmt;
15743 tab->subcmds[AT_PASS_MISC] =
15744 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15745 }
15746 else
15747 elog(ERROR, "unexpected statement type: %d",
15748 (int) nodeTag(stm));
15749 }
15750
15751 relation_close(rel, NoLock);
15752}
List * raw_parser(const char *str, RawParseMode mode)
Definition: parser.c:42
char * GetComment(Oid oid, Oid classoid, int32 subid)
Definition: comment.c:410
Oid get_constraint_index(Oid conoid)
Definition: lsyscache.c:1205
#define nodeTag(nodeptr)
Definition: nodes.h:139
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
CreateStatsStmt * transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
@ RAW_PARSE_DEFAULT
Definition: parser.h:39
bool reset_default_tblspc
Definition: parsenodes.h:2847
bool reset_default_tblspc
Definition: parsenodes.h:3471
char * idxname
Definition: parsenodes.h:3445
char * idxcomment
Definition: parsenodes.h:3455
Node * stmt
Definition: parsenodes.h:2071
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:15809
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:15838
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:15765

References Assert(), AT_AddConstraint, AT_AddIndex, AT_PASS_MISC, AT_PASS_OLD_CONSTR, AT_PASS_OLD_INDEX, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReAddIndex, AT_ReAddStatistics, ATGetQueueEntry(), castNode, Constraint::conname, CONSTR_FOREIGN, CONSTR_NOTNULL, Constraint::contype, AlterTableCmd::def, elog, ERROR, get_constraint_index(), GetComment(), IndexStmt::idxcomment, IndexStmt::idxname, IsA, lappend(), lfirst, lfirst_node, list_concat(), makeNode, NIL, nodeTag, NoLock, Constraint::old_pktable_oid, RAW_PARSE_DEFAULT, raw_parser(), RebuildConstraintComment(), relation_close(), relation_open(), Constraint::reset_default_tblspc, IndexStmt::reset_default_tblspc, AlteredTableInfo::rewrite, RawStmt::stmt, stmt, AlteredTableInfo::subcmds, AlterTableCmd::subtype, transformAlterTableStmt(), transformIndexStmt(), transformStatsStmt(), TryReuseForeignKey(), and TryReuseIndex().

Referenced by ATPostAlterTypeCleanup().

◆ ATPrepAddColumn()

static void ATPrepAddColumn ( List **  wqueue,
Relation  rel,
bool  recurse,
bool  recursing,
bool  is_view,
AlterTableCmd cmd,
LOCKMODE  lockmode,
AlterTableUtilityContext context 
)
static

Definition at line 7183 of file tablecmds.c.

7186{
7187 if (rel->rd_rel->reloftype && !recursing)
7188 ereport(ERROR,
7189 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7190 errmsg("cannot add column to typed table")));
7191
7192 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7193 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7194
7195 if (recurse && !is_view)
7196 cmd->recurse = true;
7197}
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6881

References ATTypedTableRecursion(), ereport, errcode(), errmsg(), ERROR, RelationData::rd_rel, and AlterTableCmd::recurse.

Referenced by ATPrepCmd().

◆ ATPrepAddInherit()

static void ATPrepAddInherit ( Relation  child_rel)
static

Definition at line 17162 of file tablecmds.c.

17163{
17164 if (child_rel->rd_rel->reloftype)
17165 ereport(ERROR,
17166 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17167 errmsg("cannot change inheritance of typed table")));
17168
17169 if (child_rel->rd_rel->relispartition)
17170 ereport(ERROR,
17171 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17172 errmsg("cannot change inheritance of a partition")));
17173
17174 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17175 ereport(ERROR,
17176 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17177 errmsg("cannot change inheritance of partitioned table")));
17178}

References ereport, errcode(), errmsg(), ERROR, and RelationData::rd_rel.

Referenced by ATPrepCmd().

◆ ATPrepAddPrimaryKey()

static void ATPrepAddPrimaryKey ( List **  wqueue,
Relation  rel,
AlterTableCmd cmd,
bool  recurse,
LOCKMODE  lockmode,
AlterTableUtilityContext context 
)
static

Definition at line 9464 of file tablecmds.c.

9467{
9468 Constraint *pkconstr;
9469 List *children = NIL;
9470 bool got_children = false;
9471
9472 pkconstr = castNode(Constraint, cmd->def);
9473 if (pkconstr->contype != CONSTR_PRIMARY)
9474 return;
9475
9476 /* Verify that columns are not-null, or request that they be made so */
9477 foreach_node(String, column, pkconstr->keys)
9478 {
9479 AlterTableCmd *newcmd;
9480 Constraint *nnconstr;
9481 HeapTuple tuple;
9482
9483 /*
9484 * First check if a suitable constraint exists. If it does, we don't
9485 * need to request another one. We do need to bail out if it's not
9486 * valid, though.
9487 */
9488 tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9489 if (tuple != NULL)
9490 {
9491 verifyNotNullPKCompatible(tuple, strVal(column));
9492
9493 /* All good with this one; don't request another */
9494 heap_freetuple(tuple);
9495 continue;
9496 }
9497 else if (!recurse)
9498 {
9499 /*
9500 * No constraint on this column. Asked not to recurse, we won't
9501 * create one here, but verify that all children have one.
9502 */
9503 if (!got_children)
9504 {
9506 lockmode);
9507 /* only search for children on the first time through */
9508 got_children = true;
9509 }
9510
9511 foreach_oid(childrelid, children)
9512 {
9513 HeapTuple tup;
9514
9515 tup = findNotNullConstraint(childrelid, strVal(column));
9516 if (!tup)
9517 ereport(ERROR,
9518 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9519 strVal(column), get_rel_name(childrelid)));
9520 /* verify it's good enough */
9521 verifyNotNullPKCompatible(tup, strVal(column));
9522 }
9523 }
9524
9525 /* This column is not already not-null, so add it to the queue */
9526 nnconstr = makeNotNullConstraint(column);
9527
9528 newcmd = makeNode(AlterTableCmd);
9529 newcmd->subtype = AT_AddConstraint;
9530 /* note we force recurse=true here; see above */
9531 newcmd->recurse = true;
9532 newcmd->def = (Node *) nnconstr;
9533
9534 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9535 }
9536}
List * keys
Definition: parsenodes.h:2837
Definition: value.h:64
static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
Definition: tablecmds.c:9543

References AT_AddConstraint, ATPrepCmd(), castNode, CONSTR_PRIMARY, Constraint::contype, AlterTableCmd::def, ereport, errmsg(), ERROR, find_inheritance_children(), findNotNullConstraint(), foreach_node, foreach_oid, get_rel_name(), heap_freetuple(), Constraint::keys, makeNode, makeNotNullConstraint(), NIL, AlterTableCmd::recurse, RelationGetRelid, strVal, AlterTableCmd::subtype, and verifyNotNullPKCompatible().

Referenced by ATPrepCmd().

◆ ATPrepAlterColumnType()

static void ATPrepAlterColumnType ( List **  wqueue,
AlteredTableInfo tab,
Relation  rel,
bool  recurse,
bool  recursing,
AlterTableCmd cmd,
LOCKMODE  lockmode,
AlterTableUtilityContext context 
)
static

Definition at line 14322 of file tablecmds.c.

14327{
14328 char *colName = cmd->name;
14329 ColumnDef *def = (ColumnDef *) cmd->def;
14330 TypeName *typeName = def->typeName;
14331 Node *transform = def->cooked_default;
14332 HeapTuple tuple;
14333 Form_pg_attribute attTup;
14335 Oid targettype;
14336 int32 targettypmod;
14337 Oid targetcollid;
14339 ParseState *pstate = make_parsestate(NULL);
14340 AclResult aclresult;
14341 bool is_expr;
14342
14343 pstate->p_sourcetext = context->queryString;
14344
14345 if (rel->rd_rel->reloftype && !recursing)
14346 ereport(ERROR,
14347 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14348 errmsg("cannot alter column type of typed table"),
14349 parser_errposition(pstate, def->location)));
14350
14351 /* lookup the attribute so we can check inheritance status */
14352 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14353 if (!HeapTupleIsValid(tuple))
14354 ereport(ERROR,
14355 (errcode(ERRCODE_UNDEFINED_COLUMN),
14356 errmsg("column \"%s\" of relation \"%s\" does not exist",
14357 colName, RelationGetRelationName(rel)),
14358 parser_errposition(pstate, def->location)));
14359 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14360 attnum = attTup->attnum;
14361
14362 /* Can't alter a system attribute */
14363 if (attnum <= 0)
14364 ereport(ERROR,
14365 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14366 errmsg("cannot alter system column \"%s\"", colName),
14367 parser_errposition(pstate, def->location)));
14368
14369 /*
14370 * Cannot specify USING when altering type of a generated column, because
14371 * that would violate the generation expression.
14372 */
14373 if (attTup->attgenerated && def->cooked_default)
14374 ereport(ERROR,
14375 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14376 errmsg("cannot specify USING when altering type of generated column"),
14377 errdetail("Column \"%s\" is a generated column.", colName),
14378 parser_errposition(pstate, def->location)));
14379
14380 /*
14381 * Don't alter inherited columns. At outer level, there had better not be
14382 * any inherited definition; when recursing, we assume this was checked at
14383 * the parent level (see below).
14384 */
14385 if (attTup->attinhcount > 0 && !recursing)
14386 ereport(ERROR,
14387 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14388 errmsg("cannot alter inherited column \"%s\"", colName),
14389 parser_errposition(pstate, def->location)));
14390
14391 /* Don't alter columns used in the partition key */
14392 if (has_partition_attrs(rel,
14394 &is_expr))
14395 ereport(ERROR,
14396 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14397 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14398 colName, RelationGetRelationName(rel)),
14399 parser_errposition(pstate, def->location)));
14400
14401 /* Look up the target type */
14402 typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14403
14404 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14405 if (aclresult != ACLCHECK_OK)
14406 aclcheck_error_type(aclresult, targettype);
14407
14408 /* And the collation */
14409 targetcollid = GetColumnDefCollation(pstate, def, targettype);
14410
14411 /* make sure datatype is legal for a column */
14412 CheckAttributeType(colName, targettype, targetcollid,
14413 list_make1_oid(rel->rd_rel->reltype),
14414 0);
14415
14416 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14417 {
14418 /* do nothing */
14419 }
14420 else if (tab->relkind == RELKIND_RELATION ||
14421 tab->relkind == RELKIND_PARTITIONED_TABLE)
14422 {
14423 /*
14424 * Set up an expression to transform the old data value to the new
14425 * type. If a USING option was given, use the expression as
14426 * transformed by transformAlterTableStmt, else just take the old
14427 * value and try to coerce it. We do this first so that type
14428 * incompatibility can be detected before we waste effort, and because
14429 * we need the expression to be parsed against the original table row
14430 * type.
14431 */
14432 if (!transform)
14433 {
14434 transform = (Node *) makeVar(1, attnum,
14435 attTup->atttypid, attTup->atttypmod,
14436 attTup->attcollation,
14437 0);
14438 }
14439
14440 transform = coerce_to_target_type(pstate,
14441 transform, exprType(transform),
14442 targettype, targettypmod,
14445 -1);
14446 if (transform == NULL)
14447 {
14448 /* error text depends on whether USING was specified or not */
14449 if (def->cooked_default != NULL)
14450 ereport(ERROR,
14451 (errcode(ERRCODE_DATATYPE_MISMATCH),
14452 errmsg("result of USING clause for column \"%s\""
14453 " cannot be cast automatically to type %s",
14454 colName, format_type_be(targettype)),
14455 errhint("You might need to add an explicit cast.")));
14456 else
14457 ereport(ERROR,
14458 (errcode(ERRCODE_DATATYPE_MISMATCH),
14459 errmsg("column \"%s\" cannot be cast automatically to type %s",
14460 colName, format_type_be(targettype)),
14461 !attTup->attgenerated ?
14462 /* translator: USING is SQL, don't translate it */
14463 errhint("You might need to specify \"USING %s::%s\".",
14464 quote_identifier(colName),
14465 format_type_with_typemod(targettype,
14466 targettypmod)) : 0));
14467 }
14468
14469 /* Fix collations after all else */
14470 assign_expr_collations(pstate, transform);
14471
14472 /* Plan the expr now so we can accurately assess the need to rewrite. */
14473 transform = (Node *) expression_planner((Expr *) transform);
14474
14475 /*
14476 * Add a work queue item to make ATRewriteTable update the column
14477 * contents.
14478 */
14480 newval->attnum = attnum;
14481 newval->expr = (Expr *) transform;
14482 newval->is_generated = false;
14483
14484 tab->newvals = lappend(tab->newvals, newval);
14485 if (ATColumnChangeRequiresRewrite(transform, attnum))
14487 }
14488 else if (transform)
14489 ereport(ERROR,
14490 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14491 errmsg("\"%s\" is not a table",
14493
14494 if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14495 {
14496 /*
14497 * For relations or columns without storage, do this check now.
14498 * Regular tables will check it later when the table is being
14499 * rewritten.
14500 */
14501 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14502 }
14503
14504 ReleaseSysCache(tuple);
14505
14506 /*
14507 * Recurse manually by queueing a new command for each child, if
14508 * necessary. We cannot apply ATSimpleRecursion here because we need to
14509 * remap attribute numbers in the USING expression, if any.
14510 *
14511 * If we are told not to recurse, there had better not be any child
14512 * tables; else the alter would put them out of step.
14513 */
14514 if (recurse)
14515 {
14516 Oid relid = RelationGetRelid(rel);
14517 List *child_oids,
14518 *child_numparents;
14519 ListCell *lo,
14520 *li;
14521
14522 child_oids = find_all_inheritors(relid, lockmode,
14523 &child_numparents);
14524
14525 /*
14526 * find_all_inheritors does the recursive search of the inheritance
14527 * hierarchy, so all we have to do is process all of the relids in the
14528 * list that it returns.
14529 */
14530 forboth(lo, child_oids, li, child_numparents)
14531 {
14532 Oid childrelid = lfirst_oid(lo);
14533 int numparents = lfirst_int(li);
14534 Relation childrel;
14535 HeapTuple childtuple;
14536 Form_pg_attribute childattTup;
14537
14538 if (childrelid == relid)
14539 continue;
14540
14541 /* find_all_inheritors already got lock */
14542 childrel = relation_open(childrelid, NoLock);
14543 CheckAlterTableIsSafe(childrel);
14544
14545 /*
14546 * Verify that the child doesn't have any inherited definitions of
14547 * this column that came from outside this inheritance hierarchy.
14548 * (renameatt makes a similar test, though in a different way
14549 * because of its different recursion mechanism.)
14550 */
14551 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14552 colName);
14553 if (!HeapTupleIsValid(childtuple))
14554 ereport(ERROR,
14555 (errcode(ERRCODE_UNDEFINED_COLUMN),
14556 errmsg("column \"%s\" of relation \"%s\" does not exist",
14557 colName, RelationGetRelationName(childrel))));
14558 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14559
14560 if (childattTup->attinhcount > numparents)
14561 ereport(ERROR,
14562 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14563 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14564 colName, RelationGetRelationName(childrel))));
14565
14566 ReleaseSysCache(childtuple);
14567
14568 /*
14569 * Remap the attribute numbers. If no USING expression was
14570 * specified, there is no need for this step.
14571 */
14572 if (def->cooked_default)
14573 {
14574 AttrMap *attmap;
14575 bool found_whole_row;
14576
14577 /* create a copy to scribble on */
14578 cmd = copyObject(cmd);
14579
14580 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14581 RelationGetDescr(rel),
14582 false);
14583 ((ColumnDef *) cmd->def)->cooked_default =
14585 1, 0,
14586 attmap,
14587 InvalidOid, &found_whole_row);
14588 if (found_whole_row)
14589 ereport(ERROR,
14590 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14591 errmsg("cannot convert whole-row table reference"),
14592 errdetail("USING expression contains a whole-row table reference.")));
14593 pfree(attmap);
14594 }
14595 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14596 relation_close(childrel, NoLock);
14597 }
14598 }
14599 else if (!recursing &&
14601 ereport(ERROR,
14602 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14603 errmsg("type of inherited column \"%s\" must be changed in child tables too",
14604 colName)));
14605
14606 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14607 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14608}
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition: aclchk.c:2958
#define AT_REWRITE_COLUMN_REWRITE
Definition: event_trigger.h:42
char * format_type_with_typemod(Oid type_oid, int32 typemod)
Definition: format_type.c:362
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
void assign_expr_collations(ParseState *pstate, Node *expr)
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
#define ACL_USAGE
Definition: parsenodes.h:84
#define lfirst_int(lc)
Definition: pg_list.h:173
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:13019
Node * cooked_default
Definition: parsenodes.h:747
ParseLoc location
Definition: parsenodes.h:756
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
Definition: tablecmds.c:14625
void find_composite_type_dependencies(Oid typeOid, Relation origRelation, const char *origTypeName)
Definition: tablecmds.c:6926

References ACL_USAGE, aclcheck_error_type(), ACLCHECK_OK, assign_expr_collations(), AT_REWRITE_COLUMN_REWRITE, ATColumnChangeRequiresRewrite(), ATPrepCmd(), attnum, ATTypedTableRecursion(), bms_make_singleton(), build_attrmap_by_name(), CheckAlterTableIsSafe(), CheckAttributeType(), COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, ColumnDef::cooked_default, copyObject, AlterTableCmd::def, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, expression_planner(), exprType(), find_all_inheritors(), find_composite_type_dependencies(), find_inheritance_children(), FirstLowInvalidHeapAttributeNumber, forboth, format_type_be(), format_type_with_typemod(), GetColumnDefCollation(), GETSTRUCT(), GetUserId(), has_partition_attrs(), HeapTupleIsValid, InvalidOid, lappend(), lfirst_int, lfirst_oid, list_make1_oid, ColumnDef::location, make_parsestate(), makeVar(), map_variable_attnos(), AlterTableCmd::name, newval, AlteredTableInfo::newvals, NIL, NoLock, object_aclcheck(), ParseState::p_sourcetext, palloc0(), parser_errposition(), pfree(), AlterTableUtilityContext::queryString, quote_identifier(), RelationData::rd_rel, relation_close(), relation_open(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), AlteredTableInfo::relkind, AlteredTableInfo::rewrite, SearchSysCacheAttName(), ColumnDef::typeName, and typenameTypeIdAndMod().

Referenced by ATPrepCmd().

◆ ATPrepChangePersistence()

static void ATPrepChangePersistence ( AlteredTableInfo tab,
Relation  rel,
bool  toLogged 
)
static

Definition at line 18744 of file tablecmds.c.

18745{
18746 Relation pg_constraint;
18747 HeapTuple tuple;
18748 SysScanDesc scan;
18749 ScanKeyData skey[1];
18750
18751 /*
18752 * Disallow changing status for a temp table. Also verify whether we can
18753 * get away with doing nothing; in such cases we don't need to run the
18754 * checks below, either.
18755 */
18756 switch (rel->rd_rel->relpersistence)
18757 {
18758 case RELPERSISTENCE_TEMP:
18759 ereport(ERROR,
18760 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18761 errmsg("cannot change logged status of table \"%s\" because it is temporary",
18763 errtable(rel)));
18764 break;
18765 case RELPERSISTENCE_PERMANENT:
18766 if (toLogged)
18767 /* nothing to do */
18768 return;
18769 break;
18770 case RELPERSISTENCE_UNLOGGED:
18771 if (!toLogged)
18772 /* nothing to do */
18773 return;
18774 break;
18775 }
18776
18777 /*
18778 * Check that the table is not part of any publication when changing to
18779 * UNLOGGED, as UNLOGGED tables can't be published.
18780 */
18781 if (!toLogged &&
18783 ereport(ERROR,
18784 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18785 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18787 errdetail("Unlogged relations cannot be replicated.")));
18788
18789 /*
18790 * Check existing foreign key constraints to preserve the invariant that
18791 * permanent tables cannot reference unlogged ones. Self-referencing
18792 * foreign keys can safely be ignored.
18793 */
18794 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18795
18796 /*
18797 * Scan conrelid if changing to permanent, else confrelid. This also
18798 * determines whether a useful index exists.
18799 */
18800 ScanKeyInit(&skey[0],
18801 toLogged ? Anum_pg_constraint_conrelid :
18802 Anum_pg_constraint_confrelid,
18803 BTEqualStrategyNumber, F_OIDEQ,
18805 scan = systable_beginscan(pg_constraint,
18806 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18807 true, NULL, 1, skey);
18808
18809 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18810 {
18812
18813 if (con->contype == CONSTRAINT_FOREIGN)
18814 {
18815 Oid foreignrelid;
18816 Relation foreignrel;
18817
18818 /* the opposite end of what we used as scankey */
18819 foreignrelid = toLogged ? con->confrelid : con->conrelid;
18820
18821 /* ignore if self-referencing */
18822 if (RelationGetRelid(rel) == foreignrelid)
18823 continue;
18824
18825 foreignrel = relation_open(foreignrelid, AccessShareLock);
18826
18827 if (toLogged)
18828 {
18829 if (!RelationIsPermanent(foreignrel))
18830 ereport(ERROR,
18831 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18832 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18834 RelationGetRelationName(foreignrel)),
18835 errtableconstraint(rel, NameStr(con->conname))));
18836 }
18837 else
18838 {
18839 if (RelationIsPermanent(foreignrel))
18840 ereport(ERROR,
18841 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18842 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18844 RelationGetRelationName(foreignrel)),
18845 errtableconstraint(rel, NameStr(con->conname))));
18846 }
18847
18848 relation_close(foreignrel, AccessShareLock);
18849 }
18850 }
18851
18852 systable_endscan(scan);
18853
18854 table_close(pg_constraint, AccessShareLock);
18855
18856 /* force rewrite if necessary; see comment in ATRewriteTables */
18858 if (toLogged)
18859 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18860 else
18861 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18862 tab->chgPersistence = true;
18863}
#define AT_REWRITE_ALTER_PERSISTENCE
Definition: event_trigger.h:40
int errtableconstraint(Relation rel, const char *conname)
Definition: relcache.c:6100
int errtable(Relation rel)
Definition: relcache.c:6046

References AccessShareLock, AT_REWRITE_ALTER_PERSISTENCE, BTEqualStrategyNumber, AlteredTableInfo::chgPersistence, ereport, errcode(), errdetail(), errmsg(), ERROR, errtable(), errtableconstraint(), GetRelationPublications(), GETSTRUCT(), HeapTupleIsValid, InvalidOid, NameStr, AlteredTableInfo::newrelpersistence, NIL, ObjectIdGetDatum(), RelationData::rd_rel, relation_close(), relation_open(), RelationGetRelationName, RelationGetRelid, RelationIsPermanent, AlteredTableInfo::rewrite, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATPrepCmd().

◆ ATPrepCmd()

static void ATPrepCmd ( List **  wqueue,
Relation  rel,
AlterTableCmd cmd,
bool  recurse,
bool  recursing,
LOCKMODE  lockmode,
AlterTableUtilityContext context 
)
static

Definition at line 4895 of file tablecmds.c.

4898{
4899 AlteredTableInfo *tab;
4901
4902 /* Find or create work queue entry for this table */
4903 tab = ATGetQueueEntry(wqueue, rel);
4904
4905 /*
4906 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4907 * partitions that are pending detach.
4908 */
4909 if (rel->rd_rel->relispartition &&
4912 ereport(ERROR,
4913 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4914 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4916 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4917
4918 /*
4919 * Copy the original subcommand for each table, so we can scribble on it.
4920 * This avoids conflicts when different child tables need to make
4921 * different parse transformations (for example, the same column may have
4922 * different column numbers in different children).
4923 */
4924 cmd = copyObject(cmd);
4925
4926 /*
4927 * Do permissions and relkind checking, recursion to child tables if
4928 * needed, and any additional phase-1 processing needed. (But beware of
4929 * adding any processing that looks at table details that another
4930 * subcommand could change. In some cases we reject multiple subcommands
4931 * that could try to change the same state in contrary ways.)
4932 */
4933 switch (cmd->subtype)
4934 {
4935 case AT_AddColumn: /* ADD COLUMN */
4936 ATSimplePermissions(cmd->subtype, rel,
4939 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4940 lockmode, context);
4941 /* Recursion occurs during execution phase */
4942 pass = AT_PASS_ADD_COL;
4943 break;
4944 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4946 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4947 lockmode, context);
4948 /* Recursion occurs during execution phase */
4949 pass = AT_PASS_ADD_COL;
4950 break;
4951 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4952
4953 /*
4954 * We allow defaults on views so that INSERT into a view can have
4955 * default-ish behavior. This works because the rewriter
4956 * substitutes default values into INSERTs before it expands
4957 * rules.
4958 */
4959 ATSimplePermissions(cmd->subtype, rel,
4962 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4963 /* No command-specific prep needed */
4965 break;
4966 case AT_CookedColumnDefault: /* add a pre-cooked default */
4967 /* This is currently used only in CREATE TABLE */
4968 /* (so the permission check really isn't necessary) */
4969 ATSimplePermissions(cmd->subtype, rel,
4971 /* This command never recurses */
4973 break;
4974 case AT_AddIdentity:
4975 ATSimplePermissions(cmd->subtype, rel,
4978 /* Set up recursion for phase 2; no other prep needed */
4979 if (recurse)
4980 cmd->recurse = true;
4982 break;
4983 case AT_SetIdentity:
4984 ATSimplePermissions(cmd->subtype, rel,
4987 /* Set up recursion for phase 2; no other prep needed */
4988 if (recurse)
4989 cmd->recurse = true;
4990 /* This should run after AddIdentity, so do it in MISC pass */
4991 pass = AT_PASS_MISC;
4992 break;
4993 case AT_DropIdentity:
4994 ATSimplePermissions(cmd->subtype, rel,
4997 /* Set up recursion for phase 2; no other prep needed */
4998 if (recurse)
4999 cmd->recurse = true;
5000 pass = AT_PASS_DROP;
5001 break;
5002 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5003 ATSimplePermissions(cmd->subtype, rel,
5005 /* Set up recursion for phase 2; no other prep needed */
5006 if (recurse)
5007 cmd->recurse = true;
5008 pass = AT_PASS_DROP;
5009 break;
5010 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5011 ATSimplePermissions(cmd->subtype, rel,
5013 /* Set up recursion for phase 2; no other prep needed */
5014 if (recurse)
5015 cmd->recurse = true;
5016 pass = AT_PASS_COL_ATTRS;
5017 break;
5018 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5019 ATSimplePermissions(cmd->subtype, rel,
5021 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5023 break;
5024 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5025 ATSimplePermissions(cmd->subtype, rel,
5027 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5028 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5029 pass = AT_PASS_DROP;
5030 break;
5031 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5032 ATSimplePermissions(cmd->subtype, rel,
5035 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5036 /* No command-specific prep needed */
5037 pass = AT_PASS_MISC;
5038 break;
5039 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5040 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5041 ATSimplePermissions(cmd->subtype, rel,
5044 /* This command never recurses */
5045 pass = AT_PASS_MISC;
5046 break;
5047 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5048 ATSimplePermissions(cmd->subtype, rel,
5051 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5052 /* No command-specific prep needed */
5053 pass = AT_PASS_MISC;
5054 break;
5055 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5056 ATSimplePermissions(cmd->subtype, rel,
5058 /* This command never recurses */
5059 /* No command-specific prep needed */
5060 pass = AT_PASS_MISC;
5061 break;
5062 case AT_DropColumn: /* DROP COLUMN */
5063 ATSimplePermissions(cmd->subtype, rel,
5066 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5067 lockmode, context);
5068 /* Recursion occurs during execution phase */
5069 pass = AT_PASS_DROP;
5070 break;
5071 case AT_AddIndex: /* ADD INDEX */
5073 /* This command never recurses */
5074 /* No command-specific prep needed */
5075 pass = AT_PASS_ADD_INDEX;
5076 break;
5077 case AT_AddConstraint: /* ADD CONSTRAINT */
5078 ATSimplePermissions(cmd->subtype, rel,
5080 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5081 if (recurse)
5082 {
5083 /* recurses at exec time; lock descendants and set flag */
5084 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5085 cmd->recurse = true;
5086 }
5087 pass = AT_PASS_ADD_CONSTR;
5088 break;
5089 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5091 /* This command never recurses */
5092 /* No command-specific prep needed */
5094 break;
5095 case AT_DropConstraint: /* DROP CONSTRAINT */
5096 ATSimplePermissions(cmd->subtype, rel,
5098 ATCheckPartitionsNotInUse(rel, lockmode);
5099 /* Other recursion occurs during execution phase */
5100 /* No command-specific prep needed except saving recurse flag */
5101 if (recurse)
5102 cmd->recurse = true;
5103 pass = AT_PASS_DROP;
5104 break;
5105 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5106 ATSimplePermissions(cmd->subtype, rel,
5109 /* See comments for ATPrepAlterColumnType */
5110 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5111 AT_PASS_UNSET, context);
5112 Assert(cmd != NULL);
5113 /* Performs own recursion */
5114 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5115 lockmode, context);
5116 pass = AT_PASS_ALTER_TYPE;
5117 break;
5120 /* This command never recurses */
5121 /* No command-specific prep needed */
5122 pass = AT_PASS_MISC;
5123 break;
5124 case AT_ChangeOwner: /* ALTER OWNER */
5125 /* This command never recurses */
5126 /* No command-specific prep needed */
5127 pass = AT_PASS_MISC;
5128 break;
5129 case AT_ClusterOn: /* CLUSTER ON */
5130 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5131 ATSimplePermissions(cmd->subtype, rel,
5133 /* These commands never recurse */
5134 /* No command-specific prep needed */
5135 pass = AT_PASS_MISC;
5136 break;
5137 case AT_SetLogged: /* SET LOGGED */
5138 case AT_SetUnLogged: /* SET UNLOGGED */
5140 if (tab->chgPersistence)
5141 ereport(ERROR,
5142 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5143 errmsg("cannot change persistence setting twice")));
5144 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5145 pass = AT_PASS_MISC;
5146 break;
5147 case AT_DropOids: /* SET WITHOUT OIDS */
5148 ATSimplePermissions(cmd->subtype, rel,
5150 pass = AT_PASS_DROP;
5151 break;
5152 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5153 ATSimplePermissions(cmd->subtype, rel,
5155
5156 /* check if another access method change was already requested */
5157 if (tab->chgAccessMethod)
5158 ereport(ERROR,
5159 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5160 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5161
5162 ATPrepSetAccessMethod(tab, rel, cmd->name);
5163 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5164 break;
5165 case AT_SetTableSpace: /* SET TABLESPACE */
5168 /* This command never recurses */
5169 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5170 pass = AT_PASS_MISC; /* doesn't actually matter */
5171 break;
5172 case AT_SetRelOptions: /* SET (...) */
5173 case AT_ResetRelOptions: /* RESET (...) */
5174 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5175 ATSimplePermissions(cmd->subtype, rel,
5178 /* This command never recurses */
5179 /* No command-specific prep needed */
5180 pass = AT_PASS_MISC;
5181 break;
5182 case AT_AddInherit: /* INHERIT */
5183 ATSimplePermissions(cmd->subtype, rel,
5185 /* This command never recurses */
5186 ATPrepAddInherit(rel);
5187 pass = AT_PASS_MISC;
5188 break;
5189 case AT_DropInherit: /* NO INHERIT */
5190 ATSimplePermissions(cmd->subtype, rel,
5192 /* This command never recurses */
5193 /* No command-specific prep needed */
5194 pass = AT_PASS_MISC;
5195 break;
5196 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5197 ATSimplePermissions(cmd->subtype, rel,
5199 /* Recursion occurs during execution phase */
5200 if (recurse)
5201 cmd->recurse = true;
5202 pass = AT_PASS_MISC;
5203 break;
5204 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5205 ATSimplePermissions(cmd->subtype, rel,
5207 /* Recursion occurs during execution phase */
5208 /* No command-specific prep needed except saving recurse flag */
5209 if (recurse)
5210 cmd->recurse = true;
5211 pass = AT_PASS_MISC;
5212 break;
5213 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5214 ATSimplePermissions(cmd->subtype, rel,
5216 pass = AT_PASS_MISC;
5217 /* This command never recurses */
5218 /* No command-specific prep needed */
5219 break;
5220 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5223 case AT_EnableTrigAll:
5224 case AT_EnableTrigUser:
5225 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5226 case AT_DisableTrigAll:
5227 case AT_DisableTrigUser:
5228 ATSimplePermissions(cmd->subtype, rel,
5230 /* Set up recursion for phase 2; no other prep needed */
5231 if (recurse)
5232 cmd->recurse = true;
5233 pass = AT_PASS_MISC;
5234 break;
5235 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5238 case AT_DisableRule:
5239 case AT_AddOf: /* OF */
5240 case AT_DropOf: /* NOT OF */
5245 ATSimplePermissions(cmd->subtype, rel,
5247 /* These commands never recurse */
5248 /* No command-specific prep needed */
5249 pass = AT_PASS_MISC;
5250 break;
5251 case AT_GenericOptions:
5253 /* No command-specific prep needed */
5254 pass = AT_PASS_MISC;
5255 break;
5256 case AT_AttachPartition:
5257 ATSimplePermissions(cmd->subtype, rel,
5259 /* No command-specific prep needed */
5260 pass = AT_PASS_MISC;
5261 break;
5262 case AT_DetachPartition:
5264 /* No command-specific prep needed */
5265 pass = AT_PASS_MISC;
5266 break;
5269 /* No command-specific prep needed */
5270 pass = AT_PASS_MISC;
5271 break;
5272 default: /* oops */
5273 elog(ERROR, "unrecognized alter table type: %d",
5274 (int) cmd->subtype);
5275 pass = AT_PASS_UNSET; /* keep compiler quiet */
5276 break;
5277 }
5278 Assert(pass > AT_PASS_UNSET);
5279
5280 /* Add the subcommand to the appropriate list for phase 2 */
5281 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5282}
bool PartitionHasPendingDetach(Oid partoid)
Definition: pg_inherits.c:620
#define ATT_SEQUENCE
Definition: tablecmds.c:335
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
Definition: tablecmds.c:16538
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:6851
#define ATT_INDEX
Definition: tablecmds.c:331
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:8721
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9221
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:7183
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6806
#define ATT_PARTITIONED_INDEX
Definition: tablecmds.c:334
static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
Definition: tablecmds.c:18744
static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9464
#define ATT_VIEW
Definition: tablecmds.c:329
static void ATPrepAddInherit(Relation child_rel)
Definition: tablecmds.c:17162
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:14322
#define ATT_COMPOSITE_TYPE
Definition: tablecmds.c:332
#define ATT_MATVIEW
Definition: tablecmds.c:330
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
Definition: tablecmds.c:16414

References Assert(), AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AddInherit, AT_AddOf, AT_AlterColumnGenericOptions, AT_AlterColumnType, AT_AlterConstraint, AT_AttachPartition, AT_ChangeOwner, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_NoForceRowSecurity, AT_PASS_ADD_COL, AT_PASS_ADD_CONSTR, AT_PASS_ADD_INDEX, AT_PASS_ADD_INDEXCONSTR, AT_PASS_ADD_OTHERCONSTR, AT_PASS_ALTER_TYPE, AT_PASS_COL_ATTRS, AT_PASS_DROP, AT_PASS_MISC, AT_PASS_SET_EXPRESSION, AT_PASS_UNSET, AT_ReplaceRelOptions, AT_ReplicaIdentity, AT_ResetOptions, AT_ResetRelOptions, AT_SetAccessMethod, AT_SetCompression, AT_SetExpression, AT_SetIdentity, AT_SetLogged, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_SetUnLogged, AT_ValidateConstraint, ATCheckPartitionsNotInUse(), ATGetQueueEntry(), ATParseTransformCmd(), ATPrepAddColumn(), ATPrepAddInherit(), ATPrepAddPrimaryKey(), ATPrepAlterColumnType(), ATPrepChangePersistence(), ATPrepDropColumn(), ATPrepDropExpression(), ATPrepSetAccessMethod(), ATPrepSetTableSpace(), ATSimplePermissions(), ATSimpleRecursion(), ATT_COMPOSITE_TYPE, ATT_FOREIGN_TABLE, ATT_INDEX, ATT_MATVIEW, ATT_PARTITIONED_INDEX, ATT_PARTITIONED_TABLE, ATT_SEQUENCE, ATT_TABLE, ATT_VIEW, AlteredTableInfo::chgAccessMethod, AlteredTableInfo::chgPersistence, copyObject, AlterTableCmd::def, elog, ereport, errcode(), errhint(), errmsg(), ERROR, find_all_inheritors(), lappend(), AlterTableCmd::name, PartitionHasPendingDetach(), RelationData::rd_rel, AlterTableCmd::recurse, RelationGetRelationName, RelationGetRelid, AlteredTableInfo::subcmds, and AlterTableCmd::subtype.

Referenced by ATController(), ATPrepAddPrimaryKey(), ATPrepAlterColumnType(), ATSimpleRecursion(), and ATTypedTableRecursion().

◆ ATPrepDropColumn()

static void ATPrepDropColumn ( List **  wqueue,
Relation  rel,
bool  recurse,
bool  recursing,
AlterTableCmd cmd,
LOCKMODE  lockmode,
AlterTableUtilityContext context 
)
static

Definition at line 9221 of file tablecmds.c.

9224{
9225 if (rel->rd_rel->reloftype && !recursing)
9226 ereport(ERROR,
9227 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9228 errmsg("cannot drop column from typed table")));
9229
9230 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9231 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9232
9233 if (recurse)
9234 cmd->recurse = true;
9235}

References ATTypedTableRecursion(), ereport, errcode(), errmsg(), ERROR, RelationData::rd_rel, and AlterTableCmd::recurse.

Referenced by ATPrepCmd().

◆ ATPrepDropExpression()

static void ATPrepDropExpression ( Relation  rel,
AlterTableCmd cmd,
bool  recurse,
bool  recursing,
LOCKMODE  lockmode 
)
static

Definition at line 8721 of file tablecmds.c.

8722{
8723 /*
8724 * Reject ONLY if there are child tables. We could implement this, but it
8725 * is a bit complicated. GENERATED clauses must be attached to the column
8726 * definition and cannot be added later like DEFAULT, so if a child table
8727 * has a generation expression that the parent does not have, the child
8728 * column will necessarily be an attislocal column. So to implement ONLY
8729 * here, we'd need extra code to update attislocal of the direct child
8730 * tables, somewhat similar to how DROP COLUMN does it, so that the
8731 * resulting state can be properly dumped and restored.
8732 */
8733 if (!recurse &&
8735 ereport(ERROR,
8736 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8737 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8738
8739 /*
8740 * Cannot drop generation expression from inherited columns.
8741 */
8742 if (!recursing)
8743 {
8744 HeapTuple tuple;
8745 Form_pg_attribute attTup;
8746
8748 if (!HeapTupleIsValid(tuple))
8749 ereport(ERROR,
8750 (errcode(ERRCODE_UNDEFINED_COLUMN),
8751 errmsg("column \"%s\" of relation \"%s\" does not exist",
8752 cmd->name, RelationGetRelationName(rel))));
8753
8754 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8755
8756 if (attTup->attinhcount > 0)
8757 ereport(ERROR,
8758 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8759 errmsg("cannot drop generation expression from inherited column")));
8760 }
8761}

References ereport, errcode(), errmsg(), ERROR, find_inheritance_children(), GETSTRUCT(), HeapTupleIsValid, AlterTableCmd::name, RelationGetRelationName, RelationGetRelid, and SearchSysCacheCopyAttName().

Referenced by ATPrepCmd().

◆ ATPrepSetAccessMethod()

static void ATPrepSetAccessMethod ( AlteredTableInfo tab,
Relation  rel,
const char *  amname 
)
static

Definition at line 16414 of file tablecmds.c.

16415{
16416 Oid amoid;
16417
16418 /*
16419 * Look up the access method name and check that it differs from the
16420 * table's current AM. If DEFAULT was specified for a partitioned table
16421 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16422 */
16423 if (amname != NULL)
16424 amoid = get_table_am_oid(amname, false);
16425 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16426 amoid = InvalidOid;
16427 else
16429
16430 /* if it's a match, phase 3 doesn't need to do anything */
16431 if (rel->rd_rel->relam == amoid)
16432 return;
16433
16434 /* Save info for Phase 3 to do the real work */
16436 tab->newAccessMethod = amoid;
16437 tab->chgAccessMethod = true;
16438}
Oid get_table_am_oid(const char *amname, bool missing_ok)
Definition: amcmds.c:173
#define AT_REWRITE_ACCESS_METHOD
Definition: event_trigger.h:43
char * default_table_access_method
Definition: tableam.c:49

References AT_REWRITE_ACCESS_METHOD, AlteredTableInfo::chgAccessMethod, default_table_access_method, get_table_am_oid(), InvalidOid, AlteredTableInfo::newAccessMethod, RelationData::rd_rel, and AlteredTableInfo::rewrite.

Referenced by ATPrepCmd().

◆ ATPrepSetTableSpace()

static void ATPrepSetTableSpace ( AlteredTableInfo tab,
Relation  rel,
const char *  tablespacename,
LOCKMODE  lockmode 
)
static

Definition at line 16538 of file tablecmds.c.

16539{
16540 Oid tablespaceId;
16541
16542 /* Check that the tablespace exists */
16543 tablespaceId = get_tablespace_oid(tablespacename, false);
16544
16545 /* Check permissions except when moving to database's default */
16546 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16547 {
16548 AclResult aclresult;
16549
16550 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16551 if (aclresult != ACLCHECK_OK)
16552 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16553 }
16554
16555 /* Save info for Phase 3 to do the real work */
16556 if (OidIsValid(tab->newTableSpace))
16557 ereport(ERROR,
16558 (errcode(ERRCODE_SYNTAX_ERROR),
16559 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16560
16561 tab->newTableSpace = tablespaceId;
16562}

References ACL_CREATE, aclcheck_error(), ACLCHECK_OK, ereport, errcode(), errmsg(), ERROR, get_tablespace_oid(), GetUserId(), MyDatabaseTableSpace, AlteredTableInfo::newTableSpace, object_aclcheck(), OBJECT_TABLESPACE, and OidIsValid.

Referenced by ATPrepCmd().

◆ ATRewriteCatalogs()

static void ATRewriteCatalogs ( List **  wqueue,
LOCKMODE  lockmode,
AlterTableUtilityContext context 
)
static

Definition at line 5292 of file tablecmds.c.

5294{
5295 ListCell *ltab;
5296
5297 /*
5298 * We process all the tables "in parallel", one pass at a time. This is
5299 * needed because we may have to propagate work from one table to another
5300 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5301 * re-adding of the foreign key constraint to the other table). Work can
5302 * only be propagated into later passes, however.
5303 */
5304 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5305 {
5306 /* Go through each table that needs to be processed */
5307 foreach(ltab, *wqueue)
5308 {
5309 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5310 List *subcmds = tab->subcmds[pass];
5311 ListCell *lcmd;
5312
5313 if (subcmds == NIL)
5314 continue;
5315
5316 /*
5317 * Open the relation and store it in tab. This allows subroutines
5318 * close and reopen, if necessary. Appropriate lock was obtained
5319 * by phase 1, needn't get it again.
5320 */
5321 tab->rel = relation_open(tab->relid, NoLock);
5322
5323 foreach(lcmd, subcmds)
5324 ATExecCmd(wqueue, tab,
5326 lockmode, pass, context);
5327
5328 /*
5329 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5330 * (this is not done in ATExecAlterColumnType since it should be
5331 * done only once if multiple columns of a table are altered).
5332 */
5333 if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5334 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5335
5336 if (tab->rel)
5337 {
5338 relation_close(tab->rel, NoLock);
5339 tab->rel = NULL;
5340 }
5341 }
5342 }
5343
5344 /* Check to see if a toast table must be added. */
5345 foreach(ltab, *wqueue)
5346 {
5347 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5348
5349 /*
5350 * If the table is source table of ATTACH PARTITION command, we did
5351 * not modify anything about it that will change its toasting
5352 * requirement, so no need to check.
5353 */
5354 if (((tab->relkind == RELKIND_RELATION ||
5355 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5356 tab->partition_constraint == NULL) ||
5357 tab->relkind == RELKIND_MATVIEW)
5358 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5359 }
5360}
Expr * partition_constraint
Definition: tablecmds.c:198
#define AT_NUM_PASSES
Definition: tablecmds.c:166
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
Definition: tablecmds.c:15382
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5366
void AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
Definition: toasting.c:58

References AlterTableCreateToastTable(), AT_NUM_PASSES, AT_PASS_ALTER_TYPE, AT_PASS_SET_EXPRESSION, ATExecCmd(), ATPostAlterTypeCleanup(), lfirst, lfirst_node, NIL, NoLock, AlteredTableInfo::partition_constraint, AlteredTableInfo::rel, relation_close(), relation_open(), AlteredTableInfo::relid, AlteredTableInfo::relkind, and AlteredTableInfo::subcmds.

Referenced by ATController().

◆ ATRewriteTable()

static void ATRewriteTable ( AlteredTableInfo tab,
Oid  OIDNewHeap 
)
static

Definition at line 6116 of file tablecmds.c.

6117{
6118 Relation oldrel;
6119 Relation newrel;
6120 TupleDesc oldTupDesc;
6121 TupleDesc newTupDesc;
6122 bool needscan = false;
6123 List *notnull_attrs;
6124 List *notnull_virtual_attrs;
6125 int i;
6126 ListCell *l;
6127 EState *estate;
6128 CommandId mycid;
6129 BulkInsertState bistate;
6130 int ti_options;
6131 ExprState *partqualstate = NULL;
6132
6133 /*
6134 * Open the relation(s). We have surely already locked the existing
6135 * table.
6136 */
6137 oldrel = table_open(tab->relid, NoLock);
6138 oldTupDesc = tab->oldDesc;
6139 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6140
6141 if (OidIsValid(OIDNewHeap))
6142 {
6144 false));
6145 newrel = table_open(OIDNewHeap, NoLock);
6146 }
6147 else
6148 newrel = NULL;
6149
6150 /*
6151 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6152 * is empty, so don't bother using it.
6153 */
6154 if (newrel)
6155 {
6156 mycid = GetCurrentCommandId(true);
6157 bistate = GetBulkInsertState();
6158 ti_options = TABLE_INSERT_SKIP_FSM;
6159 }
6160 else
6161 {
6162 /* keep compiler quiet about using these uninitialized */
6163 mycid = 0;
6164 bistate = NULL;
6165 ti_options = 0;
6166 }
6167
6168 /*
6169 * Generate the constraint and default execution states
6170 */
6171
6172 estate = CreateExecutorState();
6173
6174 /* Build the needed expression execution states */
6175 foreach(l, tab->constraints)
6176 {
6177 NewConstraint *con = lfirst(l);
6178
6179 switch (con->contype)
6180 {
6181 case CONSTR_CHECK:
6182 needscan = true;
6183 con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6184 break;
6185 case CONSTR_FOREIGN:
6186 /* Nothing to do here */
6187 break;
6188 default:
6189 elog(ERROR, "unrecognized constraint type: %d",
6190 (int) con->contype);
6191 }
6192 }
6193
6194 /* Build expression execution states for partition check quals */
6195 if (tab->partition_constraint)
6196 {
6197 needscan = true;
6198 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6199 }
6200
6201 foreach(l, tab->newvals)
6202 {
6203 NewColumnValue *ex = lfirst(l);
6204
6205 /* expr already planned */
6206 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6207 }
6208
6209 notnull_attrs = notnull_virtual_attrs = NIL;
6210 if (newrel || tab->verify_new_notnull)
6211 {
6212 /*
6213 * If we are rebuilding the tuples OR if we added any new but not
6214 * verified not-null constraints, check all *valid* not-null
6215 * constraints. This is a bit of overkill but it minimizes risk of
6216 * bugs.
6217 *
6218 * notnull_attrs does *not* collect attribute numbers for valid
6219 * not-null constraints over virtual generated columns; instead, they
6220 * are collected in notnull_virtual_attrs for verification elsewhere.
6221 */
6222 for (i = 0; i < newTupDesc->natts; i++)
6223 {
6224 CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6225
6226 if (attr->attnullability == ATTNULLABLE_VALID &&
6227 !attr->attisdropped)
6228 {
6229 Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6230
6231 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6232 notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6233 else
6234 notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6235 wholeatt->attnum);
6236 }
6237 }
6238 if (notnull_attrs || notnull_virtual_attrs)
6239 needscan = true;
6240 }
6241
6242 if (newrel || needscan)
6243 {
6244 ExprContext *econtext;
6245 TupleTableSlot *oldslot;
6246 TupleTableSlot *newslot;
6247 TableScanDesc scan;
6248 MemoryContext oldCxt;
6249 List *dropped_attrs = NIL;
6250 ListCell *lc;
6251 Snapshot snapshot;
6252 ResultRelInfo *rInfo = NULL;
6253
6254 /*
6255 * When adding or changing a virtual generated column with a not-null
6256 * constraint, we need to evaluate whether the generation expression
6257 * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6258 * prepare a dummy ResultRelInfo.
6259 */
6260 if (notnull_virtual_attrs != NIL)
6261 {
6262 MemoryContext oldcontext;
6263
6264 Assert(newTupDesc->constr->has_generated_virtual);
6265 Assert(newTupDesc->constr->has_not_null);
6266 oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6267 rInfo = makeNode(ResultRelInfo);
6268 InitResultRelInfo(rInfo,
6269 oldrel,
6270 0, /* dummy rangetable index */
6271 NULL,
6272 estate->es_instrument);
6273 MemoryContextSwitchTo(oldcontext);
6274 }
6275
6276 if (newrel)
6278 (errmsg_internal("rewriting table \"%s\"",
6279 RelationGetRelationName(oldrel))));
6280 else
6282 (errmsg_internal("verifying table \"%s\"",
6283 RelationGetRelationName(oldrel))));
6284
6285 if (newrel)
6286 {
6287 /*
6288 * All predicate locks on the tuples or pages are about to be made
6289 * invalid, because we move tuples around. Promote them to
6290 * relation locks.
6291 */
6293 }
6294
6295 econtext = GetPerTupleExprContext(estate);
6296
6297 /*
6298 * Create necessary tuple slots. When rewriting, two slots are needed,
6299 * otherwise one suffices. In the case where one slot suffices, we
6300 * need to use the new tuple descriptor, otherwise some constraints
6301 * can't be evaluated. Note that even when the tuple layout is the
6302 * same and no rewrite is required, the tupDescs might not be
6303 * (consider ADD COLUMN without a default).
6304 */
6305 if (tab->rewrite)
6306 {
6307 Assert(newrel != NULL);
6308 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6309 table_slot_callbacks(oldrel));
6310 newslot = MakeSingleTupleTableSlot(newTupDesc,
6311 table_slot_callbacks(newrel));
6312
6313 /*
6314 * Set all columns in the new slot to NULL initially, to ensure
6315 * columns added as part of the rewrite are initialized to NULL.
6316 * That is necessary as tab->newvals will not contain an
6317 * expression for columns with a NULL default, e.g. when adding a
6318 * column without a default together with a column with a default
6319 * requiring an actual rewrite.
6320 */
6321 ExecStoreAllNullTuple(newslot);
6322 }
6323 else
6324 {
6325 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6326 table_slot_callbacks(oldrel));
6327 newslot = NULL;
6328 }
6329
6330 /*
6331 * Any attributes that are dropped according to the new tuple
6332 * descriptor can be set to NULL. We precompute the list of dropped
6333 * attributes to avoid needing to do so in the per-tuple loop.
6334 */
6335 for (i = 0; i < newTupDesc->natts; i++)
6336 {
6337 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6338 dropped_attrs = lappend_int(dropped_attrs, i);
6339 }
6340
6341 /*
6342 * Scan through the rows, generating a new row if needed and then
6343 * checking all the constraints.
6344 */
6345 snapshot = RegisterSnapshot(GetLatestSnapshot());
6346 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6347
6348 /*
6349 * Switch to per-tuple memory context and reset it for each tuple
6350 * produced, so we don't leak memory.
6351 */
6353
6354 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6355 {
6356 TupleTableSlot *insertslot;
6357
6358 if (tab->rewrite > 0)
6359 {
6360 /* Extract data from old tuple */
6361 slot_getallattrs(oldslot);
6362 ExecClearTuple(newslot);
6363
6364 /* copy attributes */
6365 memcpy(newslot->tts_values, oldslot->tts_values,
6366 sizeof(Datum) * oldslot->tts_nvalid);
6367 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6368 sizeof(bool) * oldslot->tts_nvalid);
6369
6370 /* Set dropped attributes to null in new tuple */
6371 foreach(lc, dropped_attrs)
6372 newslot->tts_isnull[lfirst_int(lc)] = true;
6373
6374 /*
6375 * Constraints and GENERATED expressions might reference the
6376 * tableoid column, so fill tts_tableOid with the desired
6377 * value. (We must do this each time, because it gets
6378 * overwritten with newrel's OID during storing.)
6379 */
6380 newslot->tts_tableOid = RelationGetRelid(oldrel);
6381
6382 /*
6383 * Process supplied expressions to replace selected columns.
6384 *
6385 * First, evaluate expressions whose inputs come from the old
6386 * tuple.
6387 */
6388 econtext->ecxt_scantuple = oldslot;
6389
6390 foreach(l, tab->newvals)
6391 {
6392 NewColumnValue *ex = lfirst(l);
6393
6394 if (ex->is_generated)
6395 continue;
6396
6397 newslot->tts_values[ex->attnum - 1]
6398 = ExecEvalExpr(ex->exprstate,
6399 econtext,
6400 &newslot->tts_isnull[ex->attnum - 1]);
6401 }
6402
6403 ExecStoreVirtualTuple(newslot);
6404
6405 /*
6406 * Now, evaluate any expressions whose inputs come from the
6407 * new tuple. We assume these columns won't reference each
6408 * other, so that there's no ordering dependency.
6409 */
6410 econtext->ecxt_scantuple = newslot;
6411
6412 foreach(l, tab->newvals)
6413 {
6414 NewColumnValue *ex = lfirst(l);
6415
6416 if (!ex->is_generated)
6417 continue;
6418
6419 newslot->tts_values[ex->attnum - 1]
6420 = ExecEvalExpr(ex->exprstate,
6421 econtext,
6422 &newslot->tts_isnull[ex->attnum - 1]);
6423 }
6424
6425 insertslot = newslot;
6426 }
6427 else
6428 {
6429 /*
6430 * If there's no rewrite, old and new table are guaranteed to
6431 * have the same AM, so we can just use the old slot to verify
6432 * new constraints etc.
6433 */
6434 insertslot = oldslot;
6435 }
6436
6437 /* Now check any constraints on the possibly-changed tuple */
6438 econtext->ecxt_scantuple = insertslot;
6439
6440 foreach_int(attn, notnull_attrs)
6441 {
6442 if (slot_attisnull(insertslot, attn))
6443 {
6444 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6445
6446 ereport(ERROR,
6447 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6448 errmsg("column \"%s\" of relation \"%s\" contains null values",
6449 NameStr(attr->attname),
6450 RelationGetRelationName(oldrel)),
6451 errtablecol(oldrel, attn)));
6452 }
6453 }
6454
6455 if (notnull_virtual_attrs != NIL)
6456 {
6458
6459 attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6460 estate,
6461 notnull_virtual_attrs);
6463 {
6464 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6465
6466 ereport(ERROR,
6467 errcode(ERRCODE_NOT_NULL_VIOLATION),
6468 errmsg("column \"%s\" of relation \"%s\" contains null values",
6469 NameStr(attr->attname),
6470 RelationGetRelationName(oldrel)),
6471 errtablecol(oldrel, attnum));
6472 }
6473 }
6474
6475 foreach(l, tab->constraints)
6476 {
6477 NewConstraint *con = lfirst(l);
6478
6479 switch (con->contype)
6480 {
6481 case CONSTR_CHECK:
6482 if (!ExecCheck(con->qualstate, econtext))
6483 ereport(ERROR,
6484 (errcode(ERRCODE_CHECK_VIOLATION),
6485 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6486 con->name,
6487 RelationGetRelationName(oldrel)),
6488 errtableconstraint(oldrel, con->name)));
6489 break;
6490 case CONSTR_NOTNULL:
6491 case CONSTR_FOREIGN:
6492 /* Nothing to do here */
6493 break;
6494 default:
6495 elog(ERROR, "unrecognized constraint type: %d",
6496 (int) con->contype);
6497 }
6498 }
6499
6500 if (partqualstate && !ExecCheck(partqualstate, econtext))
6501 {
6502 if (tab->validate_default)
6503 ereport(ERROR,
6504 (errcode(ERRCODE_CHECK_VIOLATION),
6505 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6506 RelationGetRelationName(oldrel)),
6507 errtable(oldrel)));
6508 else
6509 ereport(ERROR,
6510 (errcode(ERRCODE_CHECK_VIOLATION),
6511 errmsg("partition constraint of relation \"%s\" is violated by some row",
6512 RelationGetRelationName(oldrel)),
6513 errtable(oldrel)));
6514 }
6515
6516 /* Write the tuple out to the new relation */
6517 if (newrel)
6518 table_tuple_insert(newrel, insertslot, mycid,
6519 ti_options, bistate);
6520
6521 ResetExprContext(econtext);
6522
6524 }
6525
6526 MemoryContextSwitchTo(oldCxt);
6527 table_endscan(scan);
6528 UnregisterSnapshot(snapshot);
6529
6531 if (newslot)
6533 }
6534
6535 FreeExecutorState(estate);
6536
6537 table_close(oldrel, NoLock);
6538 if (newrel)
6539 {
6540 FreeBulkInsertState(bistate);
6541
6542 table_finish_bulk_insert(newrel, ti_options);
6543
6544 table_close(newrel, NoLock);
6545 }
6546}
uint32 CommandId
Definition: c.h:637
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1158
#define DEBUG1
Definition: elog.h:30
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:143
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:872
void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, ResultRelInfo *partition_root_rri, int instrument_options)
Definition: execMain.c:1329
AttrNumber ExecRelGenVirtualNotNull(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, List *notnull_virtual_attrs)
Definition: execMain.c:2170
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1427
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1443
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1741
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1765
#define ResetExprContext(econtext)
Definition: executor.h:672
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:683
BulkInsertState GetBulkInsertState(void)
Definition: heapam.c:1989
void FreeBulkInsertState(BulkInsertState bistate)
Definition: heapam.c:2006
List * lappend_int(List *list, int datum)
Definition: list.c:357
bool CheckRelationOidLockedByMe(Oid relid, LOCKMODE lockmode, bool orstronger)
Definition: lmgr.c:351
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
#define foreach_int(var, lst)
Definition: pg_list.h:470
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3123
int errtablecol(Relation rel, int attnum)
Definition: relcache.c:6063
Node * expand_generated_columns_in_expr(Node *node, Relation rel, int rt_index)
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:342
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:853
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:811
bool validate_default
Definition: tablecmds.c:200
bool attisdropped
Definition: tupdesc.h:77
char attnullability
Definition: tupdesc.h:79
int es_instrument
Definition: execnodes.h:718
MemoryContext es_query_cxt
Definition: execnodes.h:708
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:268
AttrNumber attnum
Definition: tablecmds.c:236
bool is_generated
Definition: tablecmds.c:239
ExprState * exprstate
Definition: tablecmds.c:238
ExprState * qualstate
Definition: tablecmds.c:223
bool has_generated_virtual
Definition: tupdesc.h:47
bool has_not_null
Definition: tupdesc.h:45
Oid tts_tableOid
Definition: tuptable.h:130
AttrNumber tts_nvalid
Definition: tuptable.h:120
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:59
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key)
Definition: tableam.h:870
#define TABLE_INSERT_SKIP_FSM
Definition: tableam.h:253
static void table_finish_bulk_insert(Relation rel, int options)
Definition: tableam.h:1555
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, int options, struct BulkInsertStateData *bistate)
Definition: tableam.h:1362
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1015
#define ATTNULLABLE_VALID
Definition: tupdesc.h:86
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:175
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:458
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:372
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition: tuptable.h:385
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:829

References AccessExclusiveLock, Assert(), CompactAttribute::attisdropped, CompactAttribute::attnullability, ATTNULLABLE_VALID, NewColumnValue::attnum, attnum, CHECK_FOR_INTERRUPTS, CheckRelationOidLockedByMe(), TupleDescData::constr, CONSTR_CHECK, CONSTR_FOREIGN, CONSTR_NOTNULL, AlteredTableInfo::constraints, NewConstraint::contype, CreateExecutorState(), DEBUG1, ExprContext::ecxt_scantuple, elog, ereport, errcode(), errmsg(), errmsg_internal(), ERROR, errtable(), errtablecol(), errtableconstraint(), EState::es_instrument, EState::es_query_cxt, ExecCheck(), ExecClearTuple(), ExecDropSingleTupleTableSlot(), ExecEvalExpr(), ExecInitExpr(), ExecPrepareExpr(), ExecRelGenVirtualNotNull(), ExecStoreAllNullTuple(), ExecStoreVirtualTuple(), expand_generated_columns_in_expr(), NewColumnValue::expr, NewColumnValue::exprstate, foreach_int, ForwardScanDirection, FreeBulkInsertState(), FreeExecutorState(), GetBulkInsertState(), GetCurrentCommandId(), GetLatestSnapshot(), GetPerTupleExprContext, GetPerTupleMemoryContext, TupleConstr::has_generated_virtual, TupleConstr::has_not_null, i, InitResultRelInfo(), InvalidAttrNumber, NewColumnValue::is_generated, lappend_int(), lfirst, lfirst_int, makeNode, MakeSingleTupleTableSlot(), MemoryContextSwitchTo(), NewConstraint::name, NameStr, TupleDescData::natts, AlteredTableInfo::newvals, NIL, NoLock, OidIsValid, AlteredTableInfo::oldDesc, AlteredTableInfo::partition_constraint, NewConstraint::qual, NewConstraint::qualstate, RegisterSnapshot(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, AlteredTableInfo::relid, ResetExprContext, AlteredTableInfo::rewrite, slot_attisnull(), slot_getallattrs(), table_beginscan(), table_close(), table_endscan(), table_finish_bulk_insert(), TABLE_INSERT_SKIP_FSM, table_open(), table_scan_getnextslot(), table_slot_callbacks(), table_tuple_insert(), TransferPredicateLocksToHeapRelation(), TupleTableSlot::tts_isnull, TupleTableSlot::tts_nvalid, TupleTableSlot::tts_tableOid, TupleTableSlot::tts_values, TupleDescAttr(), TupleDescCompactAttr(), UnregisterSnapshot(), AlteredTableInfo::validate_default, and AlteredTableInfo::verify_new_notnull.

Referenced by ATRewriteTables().

◆ ATRewriteTables()

static void ATRewriteTables ( AlterTableStmt parsetree,
List **  wqueue,
LOCKMODE  lockmode,
AlterTableUtilityContext context 
)
static

Definition at line 5828 of file tablecmds.c.

5830{
5831 ListCell *ltab;
5832
5833 /* Go through each table that needs to be checked or rewritten */
5834 foreach(ltab, *wqueue)
5835 {
5836 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5837
5838 /* Relations without storage may be ignored here */
5839 if (!RELKIND_HAS_STORAGE(tab->relkind))
5840 continue;
5841
5842 /*
5843 * If we change column data types, the operation has to be propagated
5844 * to tables that use this table's rowtype as a column type.
5845 * tab->newvals will also be non-NULL in the case where we're adding a
5846 * column with a default. We choose to forbid that case as well,
5847 * since composite types might eventually support defaults.
5848 *
5849 * (Eventually we'll probably need to check for composite type
5850 * dependencies even when we're just scanning the table without a
5851 * rewrite, but at the moment a composite type does not enforce any
5852 * constraints, so it's not necessary/appropriate to enforce them just
5853 * during ALTER.)
5854 */
5855 if (tab->newvals != NIL || tab->rewrite > 0)
5856 {
5857 Relation rel;
5858
5859 rel = table_open(tab->relid, NoLock);
5860 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5861 table_close(rel, NoLock);
5862 }
5863
5864 /*
5865 * We only need to rewrite the table if at least one column needs to
5866 * be recomputed, or we are changing its persistence or access method.
5867 *
5868 * There are two reasons for requiring a rewrite when changing
5869 * persistence: on one hand, we need to ensure that the buffers
5870 * belonging to each of the two relations are marked with or without
5871 * BM_PERMANENT properly. On the other hand, since rewriting creates
5872 * and assigns a new relfilenumber, we automatically create or drop an
5873 * init fork for the relation as appropriate.
5874 */
5875 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5876 {
5877 /* Build a temporary relation and copy data */
5878 Relation OldHeap;
5879 Oid OIDNewHeap;
5880 Oid NewAccessMethod;
5881 Oid NewTableSpace;
5882 char persistence;
5883
5884 OldHeap = table_open(tab->relid, NoLock);
5885
5886 /*
5887 * We don't support rewriting of system catalogs; there are too
5888 * many corner cases and too little benefit. In particular this
5889 * is certainly not going to work for mapped catalogs.
5890 */
5891 if (IsSystemRelation(OldHeap))
5892 ereport(ERROR,
5893 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5894 errmsg("cannot rewrite system relation \"%s\"",
5895 RelationGetRelationName(OldHeap))));
5896
5897 if (RelationIsUsedAsCatalogTable(OldHeap))
5898 ereport(ERROR,
5899 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5900 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5901 RelationGetRelationName(OldHeap))));
5902
5903 /*
5904 * Don't allow rewrite on temp tables of other backends ... their
5905 * local buffer manager is not going to cope. (This is redundant
5906 * with the check in CheckAlterTableIsSafe, but for safety we'll
5907 * check here too.)
5908 */
5909 if (RELATION_IS_OTHER_TEMP(OldHeap))
5910 ereport(ERROR,
5911 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5912 errmsg("cannot rewrite temporary tables of other sessions")));
5913
5914 /*
5915 * Select destination tablespace (same as original unless user
5916 * requested a change)
5917 */
5918 if (tab->newTableSpace)
5919 NewTableSpace = tab->newTableSpace;
5920 else
5921 NewTableSpace = OldHeap->rd_rel->reltablespace;
5922
5923 /*
5924 * Select destination access method (same as original unless user
5925 * requested a change)
5926 */
5927 if (tab->chgAccessMethod)
5928 NewAccessMethod = tab->newAccessMethod;
5929 else
5930 NewAccessMethod = OldHeap->rd_rel->relam;
5931
5932 /*
5933 * Select persistence of transient table (same as original unless
5934 * user requested a change)
5935 */
5936 persistence = tab->chgPersistence ?
5937 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5938
5939 table_close(OldHeap, NoLock);
5940
5941 /*
5942 * Fire off an Event Trigger now, before actually rewriting the
5943 * table.
5944 *
5945 * We don't support Event Trigger for nested commands anywhere,
5946 * here included, and parsetree is given NULL when coming from
5947 * AlterTableInternal.
5948 *
5949 * And fire it only once.
5950 */
5951 if (parsetree)
5952 EventTriggerTableRewrite((Node *) parsetree,
5953 tab->relid,
5954 tab->rewrite);
5955
5956 /*
5957 * Create transient table that will receive the modified data.
5958 *
5959 * Ensure it is marked correctly as logged or unlogged. We have
5960 * to do this here so that buffers for the new relfilenumber will
5961 * have the right persistence set, and at the same time ensure
5962 * that the original filenumbers's buffers will get read in with
5963 * the correct setting (i.e. the original one). Otherwise a
5964 * rollback after the rewrite would possibly result with buffers
5965 * for the original filenumbers having the wrong persistence
5966 * setting.
5967 *
5968 * NB: This relies on swap_relation_files() also swapping the
5969 * persistence. That wouldn't work for pg_class, but that can't be
5970 * unlogged anyway.
5971 */
5972 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5973 persistence, lockmode);
5974
5975 /*
5976 * Copy the heap data into the new table with the desired
5977 * modifications, and test the current data within the table
5978 * against new constraints generated by ALTER TABLE commands.
5979 */
5980 ATRewriteTable(tab, OIDNewHeap);
5981
5982 /*
5983 * Swap the physical files of the old and new heaps, then rebuild
5984 * indexes and discard the old heap. We can use RecentXmin for
5985 * the table's new relfrozenxid because we rewrote all the tuples
5986 * in ATRewriteTable, so no older Xid remains in the table. Also,
5987 * we never try to swap toast tables by content, since we have no
5988 * interest in letting this code work on system catalogs.
5989 */
5990 finish_heap_swap(tab->relid, OIDNewHeap,
5991 false, false, true,
5993 RecentXmin,
5995 persistence);
5996
5997 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5998 }
5999 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6000 {
6001 if (tab->chgPersistence)
6003 }
6004 else
6005 {
6006 /*
6007 * If required, test the current data within the table against new
6008 * constraints generated by ALTER TABLE commands, but don't
6009 * rebuild data.
6010 */
6011 if (tab->constraints != NIL || tab->verify_new_notnull ||
6012 tab->partition_constraint != NULL)
6014
6015 /*
6016 * If we had SET TABLESPACE but no reason to reconstruct tuples,
6017 * just do a block-by-block copy.
6018 */
6019 if (tab->newTableSpace)
6020 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6021 }
6022
6023 /*
6024 * Also change persistence of owned sequences, so that it matches the
6025 * table persistence.
6026 */
6027 if (tab->chgPersistence)
6028 {
6029 List *seqlist = getOwnedSequences(tab->relid);
6030 ListCell *lc;
6031
6032 foreach(lc, seqlist)
6033 {
6034 Oid seq_relid = lfirst_oid(lc);
6035
6037 }
6038 }
6039 }
6040
6041 /*
6042 * Foreign key constraints are checked in a final pass, since (a) it's
6043 * generally best to examine each one separately, and (b) it's at least
6044 * theoretically possible that we have changed both relations of the
6045 * foreign key, and we'd better have finished both rewrites before we try
6046 * to read the tables.
6047 */
6048 foreach(ltab, *wqueue)
6049 {
6050 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6051 Relation rel = NULL;
6052 ListCell *lcon;
6053
6054 /* Relations without storage may be ignored here too */
6055 if (!RELKIND_HAS_STORAGE(tab->relkind))
6056 continue;
6057
6058 foreach(lcon, tab->constraints)
6059 {
6060 NewConstraint *con = lfirst(lcon);
6061
6062 if (con->contype == CONSTR_FOREIGN)
6063 {
6064 Constraint *fkconstraint = (Constraint *) con->qual;
6065 Relation refrel;
6066
6067 if (rel == NULL)
6068 {
6069 /* Long since locked, no need for another */
6070 rel = table_open(tab->relid, NoLock);
6071 }
6072
6073 refrel = table_open(con->refrelid, RowShareLock);
6074
6075 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6076 con->refindid,
6077 con->conid,
6078 con->conwithperiod);
6079
6080 /*
6081 * No need to mark the constraint row as validated, we did
6082 * that when we inserted the row earlier.
6083 */
6084
6085 table_close(refrel, NoLock);
6086 }
6087 }
6088
6089 if (rel)
6090 table_close(rel, NoLock);
6091 }
6092
6093 /* Finally, run any afterStmts that were queued up */
6094 foreach(ltab, *wqueue)
6095 {
6096 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6097 ListCell *lc;
6098
6099 foreach(lc, tab->afterStmts)
6100 {
6101 Node *stmt = (Node *) lfirst(lc);
6102
6105 }
6106 }
6107}
void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, bool check_constraints, bool is_internal, TransactionId frozenXid, MultiXactId cutoffMulti, char newrelpersistence)
Definition: cluster.c:1445
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
Definition: cluster.c:705
void SequenceChangePersistence(Oid relid, char newrelpersistence)
Definition: sequence.c:541
void EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
#define RowShareLock
Definition: lockdefs.h:37
MultiXactId ReadNextMultiXactId(void)
Definition: multixact.c:771
List * getOwnedSequences(Oid relid)
Definition: pg_depend.c:936
#define RelationIsUsedAsCatalogTable(relation)
Definition: rel.h:397
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:669
TransactionId RecentXmin
Definition: snapmgr.c:159
static void validateForeignKeyConstraint(char *conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid, bool hasperiod)
Definition: tablecmds.c:13643
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
Definition: tablecmds.c:6116

References AlteredTableInfo::afterStmts, ATExecSetTableSpace(), ATRewriteTable(), AlteredTableInfo::chgAccessMethod, AlteredTableInfo::chgPersistence, CommandCounterIncrement(), NewConstraint::conid, Constraint::conname, CONSTR_FOREIGN, AlteredTableInfo::constraints, NewConstraint::contype, NewConstraint::conwithperiod, ereport, errcode(), errmsg(), ERROR, EventTriggerTableRewrite(), find_composite_type_dependencies(), finish_heap_swap(), getOwnedSequences(), if(), InvalidOid, InvokeObjectPostAlterHook, IsSystemRelation(), lfirst, lfirst_oid, make_new_heap(), AlteredTableInfo::newAccessMethod, AlteredTableInfo::newrelpersistence, AlteredTableInfo::newTableSpace, AlteredTableInfo::newvals, NIL, NoLock, OidIsValid, AlteredTableInfo::partition_constraint, ProcessUtilityForAlterTable(), NewConstraint::qual, RelationData::rd_rel, ReadNextMultiXactId(), RecentXmin, NewConstraint::refindid, NewConstraint::refrelid, RELATION_IS_OTHER_TEMP, RelationGetRelationName, RelationIsUsedAsCatalogTable, AlteredTableInfo::relid, AlteredTableInfo::relkind, AlteredTableInfo::rewrite, RowShareLock, SequenceChangePersistence(), stmt, table_close(), table_open(), validateForeignKeyConstraint(), and AlteredTableInfo::verify_new_notnull.

Referenced by ATController().

◆ ATSimplePermissions()

static void ATSimplePermissions ( AlterTableType  cmdtype,
Relation  rel,
int  allowed_targets 
)
static

Definition at line 6729 of file tablecmds.c.

6730{
6731 int actual_target;
6732
6733 switch (rel->rd_rel->relkind)
6734 {
6735 case RELKIND_RELATION:
6736 actual_target = ATT_TABLE;
6737 break;
6738 case RELKIND_PARTITIONED_TABLE:
6739 actual_target = ATT_PARTITIONED_TABLE;
6740 break;
6741 case RELKIND_VIEW:
6742 actual_target = ATT_VIEW;
6743 break;
6744 case RELKIND_MATVIEW:
6745 actual_target = ATT_MATVIEW;
6746 break;
6747 case RELKIND_INDEX:
6748 actual_target = ATT_INDEX;
6749 break;
6750 case RELKIND_PARTITIONED_INDEX:
6751 actual_target = ATT_PARTITIONED_INDEX;
6752 break;
6753 case RELKIND_COMPOSITE_TYPE:
6754 actual_target = ATT_COMPOSITE_TYPE;
6755 break;
6756 case RELKIND_FOREIGN_TABLE:
6757 actual_target = ATT_FOREIGN_TABLE;
6758 break;
6759 case RELKIND_SEQUENCE:
6760 actual_target = ATT_SEQUENCE;
6761 break;
6762 default:
6763 actual_target = 0;
6764 break;
6765 }
6766
6767 /* Wrong target type? */
6768 if ((actual_target & allowed_targets) == 0)
6769 {
6770 const char *action_str = alter_table_type_to_string(cmdtype);
6771
6772 if (action_str)
6773 ereport(ERROR,
6774 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6775 /* translator: %s is a group of some SQL keywords */
6776 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6777 action_str, RelationGetRelationName(rel)),
6778 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6779 else
6780 /* internal error? */
6781 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6783 }
6784
6785 /* Permissions checks */
6786 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6789
6791 ereport(ERROR,
6792 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6793 errmsg("permission denied: \"%s\" is a system catalog",
6795}
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition: tablecmds.c:6586

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, alter_table_type_to_string(), ATT_COMPOSITE_TYPE, ATT_FOREIGN_TABLE, ATT_INDEX, ATT_MATVIEW, ATT_PARTITIONED_INDEX, ATT_PARTITIONED_TABLE, ATT_SEQUENCE, ATT_TABLE, ATT_VIEW, elog, ereport, errcode(), errdetail_relkind_not_supported(), errmsg(), ERROR, get_relkind_objtype(), GetUserId(), IsSystemRelation(), object_ownercheck(), RelationData::rd_rel, RelationGetRelationName, and RelationGetRelid.

Referenced by ATAddCheckNNConstraint(), ATExecAddColumn(), ATExecAddInherit(), ATExecAttachPartition(), ATExecDropColumn(), ATExecSetNotNull(), ATPrepCmd(), and dropconstraint_internal().

◆ ATSimpleRecursion()

static void ATSimpleRecursion ( List **  wqueue,
Relation  rel,
AlterTableCmd cmd,
bool  recurse,
LOCKMODE  lockmode,
AlterTableUtilityContext context 
)
static

Definition at line 6806 of file tablecmds.c.

6809{
6810 /*
6811 * Propagate to children, if desired and if there are (or might be) any
6812 * children.
6813 */
6814 if (recurse && rel->rd_rel->relhassubclass)
6815 {
6816 Oid relid = RelationGetRelid(rel);
6817 ListCell *child;
6818 List *children;
6819
6820 children = find_all_inheritors(relid, lockmode, NULL);
6821
6822 /*
6823 * find_all_inheritors does the recursive search of the inheritance
6824 * hierarchy, so all we have to do is process all of the relids in the
6825 * list that it returns.
6826 */
6827 foreach(child, children)
6828 {
6829 Oid childrelid = lfirst_oid(child);
6830 Relation childrel;
6831
6832 if (childrelid == relid)
6833 continue;
6834 /* find_all_inheritors already got lock */
6835 childrel = relation_open(childrelid, NoLock);
6836 CheckAlterTableIsSafe(childrel);
6837 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6838 relation_close(childrel, NoLock);
6839 }
6840 }
6841}

References ATPrepCmd(), CheckAlterTableIsSafe(), find_all_inheritors(), lfirst_oid, NoLock, RelationData::rd_rel, relation_close(), relation_open(), and RelationGetRelid.

Referenced by ATPrepCmd().

◆ AttachPartitionEnsureIndexes()

static void AttachPartitionEnsureIndexes ( List **  wqueue,
Relation  rel,
Relation  attachrel 
)
static

Definition at line 20486 of file tablecmds.c.

20487{
20488 List *idxes;
20489 List *attachRelIdxs;
20490 Relation *attachrelIdxRels;
20491 IndexInfo **attachInfos;
20492 ListCell *cell;
20493 MemoryContext cxt;
20494 MemoryContext oldcxt;
20495
20497 "AttachPartitionEnsureIndexes",
20499 oldcxt = MemoryContextSwitchTo(cxt);
20500
20501 idxes = RelationGetIndexList(rel);
20502 attachRelIdxs = RelationGetIndexList(attachrel);
20503 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
20504 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
20505
20506 /* Build arrays of all existing indexes and their IndexInfos */
20507 foreach_oid(cldIdxId, attachRelIdxs)
20508 {
20509 int i = foreach_current_index(cldIdxId);
20510
20511 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20512 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20513 }
20514
20515 /*
20516 * If we're attaching a foreign table, we must fail if any of the indexes
20517 * is a constraint index; otherwise, there's nothing to do here. Do this
20518 * before starting work, to avoid wasting the effort of building a few
20519 * non-unique indexes before coming across a unique one.
20520 */
20521 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20522 {
20523 foreach(cell, idxes)
20524 {
20525 Oid idx = lfirst_oid(cell);
20527
20528 if (idxRel->rd_index->indisunique ||
20529 idxRel->rd_index->indisprimary)
20530 ereport(ERROR,
20531 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20532 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20533 RelationGetRelationName(attachrel),
20535 errdetail("Partitioned table \"%s\" contains unique indexes.",
20538 }
20539
20540 goto out;
20541 }
20542
20543 /*
20544 * For each index on the partitioned table, find a matching one in the
20545 * partition-to-be; if one is not found, create one.
20546 */
20547 foreach(cell, idxes)
20548 {
20549 Oid idx = lfirst_oid(cell);
20551 IndexInfo *info;
20552 AttrMap *attmap;
20553 bool found = false;
20554 Oid constraintOid;
20555
20556 /*
20557 * Ignore indexes in the partitioned table other than partitioned
20558 * indexes.
20559 */
20560 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20561 {
20563 continue;
20564 }
20565
20566 /* construct an indexinfo to compare existing indexes against */
20567 info = BuildIndexInfo(idxRel);
20568 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20569 RelationGetDescr(rel),
20570 false);
20572
20573 /*
20574 * Scan the list of existing indexes in the partition-to-be, and mark
20575 * the first matching, valid, unattached one we find, if any, as
20576 * partition of the parent index. If we find one, we're done.
20577 */
20578 for (int i = 0; i < list_length(attachRelIdxs); i++)
20579 {
20580 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20581 Oid cldConstrOid = InvalidOid;
20582
20583 /* does this index have a parent? if so, can't use it */
20584 if (attachrelIdxRels[i]->rd_rel->relispartition)
20585 continue;
20586
20587 /* If this index is invalid, can't use it */
20588 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20589 continue;
20590
20591 if (CompareIndexInfo(attachInfos[i], info,
20592 attachrelIdxRels[i]->rd_indcollation,
20593 idxRel->rd_indcollation,
20594 attachrelIdxRels[i]->rd_opfamily,
20595 idxRel->rd_opfamily,
20596 attmap))
20597 {
20598 /*
20599 * If this index is being created in the parent because of a
20600 * constraint, then the child needs to have a constraint also,
20601 * so look for one. If there is no such constraint, this
20602 * index is no good, so keep looking.
20603 */
20604 if (OidIsValid(constraintOid))
20605 {
20606 cldConstrOid =
20608 cldIdxId);
20609 /* no dice */
20610 if (!OidIsValid(cldConstrOid))
20611 continue;
20612
20613 /* Ensure they're both the same type of constraint */
20614 if (get_constraint_type(constraintOid) !=
20615 get_constraint_type(cldConstrOid))
20616 continue;
20617 }
20618
20619 /* bingo. */
20620 IndexSetParentIndex(attachrelIdxRels[i], idx);
20621 if (OidIsValid(constraintOid))
20622 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20623 RelationGetRelid(attachrel));
20624 found = true;
20625
20627 break;
20628 }
20629 }
20630
20631 /*
20632 * If no suitable index was found in the partition-to-be, create one
20633 * now. Note that if this is a PK, not-null constraints must already
20634 * exist.
20635 */
20636 if (!found)
20637 {
20638 IndexStmt *stmt;
20639 Oid conOid;
20640
20642 idxRel, attmap,
20643 &conOid);
20645 RelationGetRelid(idxRel),
20646 conOid,
20647 -1,
20648 true, false, false, false, false);
20649 }
20650
20652 }
20653
20654out:
20655 /* Clean up. */
20656 for (int i = 0; i < list_length(attachRelIdxs); i++)
20657 index_close(attachrelIdxRels[i], AccessShareLock);
20658 MemoryContextSwitchTo(oldcxt);
20660}
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:262
char get_constraint_type(Oid conoid)
Definition: lsyscache.c:1235
MemoryContext CurrentMemoryContext
Definition: mcxt.c:159
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:485
#define AllocSetContextCreate
Definition: memutils.h:149
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:180
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403

References AccessShareLock, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, build_attrmap_by_name(), BuildIndexInfo(), CommandCounterIncrement(), CompareIndexInfo(), ConstraintSetParentConstraint(), CurrentMemoryContext, DefineIndex(), ereport, errcode(), errdetail(), errmsg(), ERROR, foreach_current_index, foreach_oid, generateClonedIndexStmt(), get_constraint_type(), get_relation_idx_constraint_oid(), i, idx(), index_close(), index_open(), IndexSetParentIndex(), InvalidOid, lfirst_oid, list_length(), MemoryContextDelete(), MemoryContextSwitchTo(), OidIsValid, palloc(), RelationData::rd_indcollation, RelationData::rd_index, RelationData::rd_opfamily, RelationData::rd_rel, RelationGetDescr, RelationGetIndexList(), RelationGetRelationName, RelationGetRelid, and stmt.

Referenced by ATExecAttachPartition().

◆ AttachPartitionForeignKey()

static void AttachPartitionForeignKey ( List **  wqueue,
Relation  partition,
Oid  partConstrOid,
Oid  parentConstrOid,
Oid  parentInsTrigger,
Oid  parentUpdTrigger,
Relation  trigrel 
)
static

Definition at line 11766 of file tablecmds.c.

11773{
11774 HeapTuple parentConstrTup;
11775 Form_pg_constraint parentConstr;
11776 HeapTuple partcontup;
11777 Form_pg_constraint partConstr;
11778 bool queueValidation;
11779 Oid partConstrFrelid;
11780 Oid partConstrRelid;
11781 bool parentConstrIsEnforced;
11782
11783 /* Fetch the parent constraint tuple */
11784 parentConstrTup = SearchSysCache1(CONSTROID,
11785 ObjectIdGetDatum(parentConstrOid));
11786 if (!HeapTupleIsValid(parentConstrTup))
11787 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11788 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11789 parentConstrIsEnforced = parentConstr->conenforced;
11790
11791 /* Fetch the child constraint tuple */
11792 partcontup = SearchSysCache1(CONSTROID,
11793 ObjectIdGetDatum(partConstrOid));
11794 if (!HeapTupleIsValid(partcontup))
11795 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11796 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11797 partConstrFrelid = partConstr->confrelid;
11798 partConstrRelid = partConstr->conrelid;
11799
11800 /*
11801 * If the referenced table is partitioned, then the partition we're
11802 * attaching now has extra pg_constraint rows and action triggers that are
11803 * no longer needed. Remove those.
11804 */
11805 if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11806 {
11807 Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11808
11809 RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11810 partConstrRelid);
11811
11812 table_close(pg_constraint, RowShareLock);
11813 }
11814
11815 /*
11816 * Will we need to validate this constraint? A valid parent constraint
11817 * implies that all child constraints have been validated, so if this one
11818 * isn't, we must trigger phase 3 validation.
11819 */
11820 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11821
11822 ReleaseSysCache(partcontup);
11823 ReleaseSysCache(parentConstrTup);
11824
11825 /*
11826 * The action triggers in the new partition become redundant -- the parent
11827 * table already has equivalent ones, and those will be able to reach the
11828 * partition. Remove the ones in the partition. We identify them because
11829 * they have our constraint OID, as well as being on the referenced rel.
11830 */
11831 DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11832 partConstrRelid);
11833
11834 ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11835 RelationGetRelid(partition));
11836
11837 /*
11838 * Like the constraint, attach partition's "check" triggers to the
11839 * corresponding parent triggers if the constraint is ENFORCED. NOT
11840 * ENFORCED constraints do not have these triggers.
11841 */
11842 if (parentConstrIsEnforced)
11843 {
11844 Oid insertTriggerOid,
11845 updateTriggerOid;
11846
11848 partConstrOid, partConstrFrelid, partConstrRelid,
11849 &insertTriggerOid, &updateTriggerOid);
11850 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11851 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11852 RelationGetRelid(partition));
11853 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11854 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11855 RelationGetRelid(partition));
11856 }
11857
11858 /*
11859 * We updated this pg_constraint row above to set its parent; validating
11860 * it will cause its convalidated flag to change, so we need CCI here. In
11861 * addition, we need it unconditionally for the rare case where the parent
11862 * table has *two* identical constraints; when reaching this function for
11863 * the second one, we must have made our changes visible, otherwise we
11864 * would try to attach both to this one.
11865 */
11867
11868 /* If validation is needed, put it in the queue now. */
11869 if (queueValidation)
11870 {
11871 Relation conrel;
11872
11873 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11874
11875 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11876 if (!HeapTupleIsValid(partcontup))
11877 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11878
11879 /* Use the same lock as for AT_ValidateConstraint */
11880 QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
11882 ReleaseSysCache(partcontup);
11884 }
11885}
static void RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
Definition: tablecmds.c:11894
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12105
void TriggerSetParentTrigger(Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
Definition: trigger.c:1220

References Assert(), CommandCounterIncrement(), ConstraintSetParentConstraint(), DropForeignKeyConstraintTriggers(), elog, ERROR, get_rel_relkind(), GetForeignKeyCheckTriggers(), GETSTRUCT(), HeapTupleIsValid, ObjectIdGetDatum(), OidIsValid, QueueFKConstraintValidation(), RelationGetRelid, ReleaseSysCache(), RemoveInheritedConstraint(), RowExclusiveLock, RowShareLock, SearchSysCache1(), ShareUpdateExclusiveLock, table_close(), table_open(), and TriggerSetParentTrigger().

Referenced by tryAttachPartitionForeignKey().

◆ ATTypedTableRecursion()

static void ATTypedTableRecursion ( List **  wqueue,
Relation  rel,
AlterTableCmd cmd,
LOCKMODE  lockmode,
AlterTableUtilityContext context 
)
static

Definition at line 6881 of file tablecmds.c.

6883{
6884 ListCell *child;
6885 List *children;
6886
6887 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6888
6889 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6891 cmd->behavior);
6892
6893 foreach(child, children)
6894 {
6895 Oid childrelid = lfirst_oid(child);
6896 Relation childrel;
6897
6898 childrel = relation_open(childrelid, lockmode);
6899 CheckAlterTableIsSafe(childrel);
6900 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6901 relation_close(childrel, NoLock);
6902 }
6903}
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition: tablecmds.c:7084

References Assert(), ATPrepCmd(), AlterTableCmd::behavior, CheckAlterTableIsSafe(), find_typed_table_dependencies(), lfirst_oid, NoLock, RelationData::rd_rel, relation_close(), relation_open(), and RelationGetRelationName.

Referenced by ATPrepAddColumn(), ATPrepAlterColumnType(), and ATPrepDropColumn().

◆ BuildDescForRelation()

TupleDesc BuildDescForRelation ( const List columns)

Definition at line 1370 of file tablecmds.c.

1371{
1372 int natts;
1374 ListCell *l;
1375 TupleDesc desc;
1376 char *attname;
1377 Oid atttypid;
1378 int32 atttypmod;
1379 Oid attcollation;
1380 int attdim;
1381
1382 /*
1383 * allocate a new tuple descriptor
1384 */
1385 natts = list_length(columns);
1386 desc = CreateTemplateTupleDesc(natts);
1387
1388 attnum = 0;
1389
1390 foreach(l, columns)
1391 {
1392 ColumnDef *entry = lfirst(l);
1393 AclResult aclresult;
1395
1396 /*
1397 * for each entry in the list, get the name and type information from
1398 * the list and have TupleDescInitEntry fill in the attribute
1399 * information we need.
1400 */
1401 attnum++;
1402
1403 attname = entry->colname;
1404 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1405
1406 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1407 if (aclresult != ACLCHECK_OK)
1408 aclcheck_error_type(aclresult, atttypid);
1409
1410 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1411 attdim = list_length(entry->typeName->arrayBounds);
1412 if (attdim > PG_INT16_MAX)
1413 ereport(ERROR,
1414 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1415 errmsg("too many array dimensions"));
1416
1417 if (entry->typeName->setof)
1418 ereport(ERROR,
1419 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1420 errmsg("column \"%s\" cannot be declared SETOF",
1421 attname)));
1422
1424 atttypid, atttypmod, attdim);
1425 att = TupleDescAttr(desc, attnum - 1);
1426
1427 /* Override TupleDescInitEntry's settings as requested */
1428 TupleDescInitEntryCollation(desc, attnum, attcollation);
1429
1430 /* Fill in additional stuff not handled by TupleDescInitEntry */
1431 att->attnotnull = entry->is_not_null;
1432 att->attislocal = entry->is_local;
1433 att->attinhcount = entry->inhcount;
1434 att->attidentity = entry->identity;
1435 att->attgenerated = entry->generated;
1436 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1437 if (entry->storage)
1438 att->attstorage = entry->storage;
1439 else if (entry->storage_name)
1440 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1441
1443 }
1444
1445 return desc;
1446}
NameData attname
Definition: pg_attribute.h:41
char * storage_name
Definition: parsenodes.h:745
char storage
Definition: parsenodes.h:744
char * compression
Definition: parsenodes.h:739
bool setof
Definition: parsenodes.h:281
List * arrayBounds
Definition: parsenodes.h:285
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:175
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition: tupdesc.c:117
void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid)
Definition: tupdesc.c:1019
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:835

References ACL_USAGE, aclcheck_error_type(), ACLCHECK_OK, TypeName::arrayBounds, attname, attnum, ColumnDef::colname, ColumnDef::compression, CreateTemplateTupleDesc(), ereport, errcode(), errmsg(), ERROR, ColumnDef::generated, GetAttributeCompression(), GetAttributeStorage(), GetColumnDefCollation(), GetUserId(), ColumnDef::identity, ColumnDef::inhcount, ColumnDef::is_local, ColumnDef::is_not_null, lfirst, list_length(), object_aclcheck(), PG_INT16_MAX, populate_compact_attribute(), TypeName::setof, ColumnDef::storage, ColumnDef::storage_name, TupleDescAttr(), TupleDescInitEntry(), TupleDescInitEntryCollation(), ColumnDef::typeName, and typenameTypeIdAndMod().

Referenced by ATExecAddColumn(), DefineRelation(), and DefineVirtualRelation().

◆ change_owner_fix_column_acls()

static void change_owner_fix_column_acls ( Oid  relationOid,
Oid  oldOwnerId,
Oid  newOwnerId 
)
static

Definition at line 16236 of file tablecmds.c.

16237{
16238 Relation attRelation;
16239 SysScanDesc scan;
16240 ScanKeyData key[1];
16241 HeapTuple attributeTuple;
16242
16243 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16244 ScanKeyInit(&key[0],
16245 Anum_pg_attribute_attrelid,
16246 BTEqualStrategyNumber, F_OIDEQ,
16247 ObjectIdGetDatum(relationOid));
16248 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16249 true, NULL, 1, key);
16250 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16251 {
16252 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16253 Datum repl_val[Natts_pg_attribute];
16254 bool repl_null[Natts_pg_attribute];
16255 bool repl_repl[Natts_pg_attribute];
16256 Acl *newAcl;
16257 Datum aclDatum;
16258 bool isNull;
16259 HeapTuple newtuple;
16260
16261 /* Ignore dropped columns */
16262 if (att->attisdropped)
16263 continue;
16264
16265 aclDatum = heap_getattr(attributeTuple,
16266 Anum_pg_attribute_attacl,
16267 RelationGetDescr(attRelation),
16268 &isNull);
16269 /* Null ACLs do not require changes */
16270 if (isNull)
16271 continue;
16272
16273 memset(repl_null, false, sizeof(repl_null));
16274 memset(repl_repl, false, sizeof(repl_repl));
16275
16276 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16277 oldOwnerId, newOwnerId);
16278 repl_repl[Anum_pg_attribute_attacl - 1] = true;
16279 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16280
16281 newtuple = heap_modify_tuple(attributeTuple,
16282 RelationGetDescr(attRelation),
16283 repl_val, repl_null, repl_repl);
16284
16285 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16286
16287 heap_freetuple(newtuple);
16288 }
16289 systable_endscan(scan);
16290 table_close(attRelation, RowExclusiveLock);
16291}

References aclnewowner(), BTEqualStrategyNumber, CatalogTupleUpdate(), DatumGetAclP, GETSTRUCT(), heap_freetuple(), heap_getattr(), heap_modify_tuple(), HeapTupleIsValid, sort-test::key, ObjectIdGetDatum(), PointerGetDatum(), RelationGetDescr, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecChangeOwner().

◆ change_owner_recurse_to_sequences()

static void change_owner_recurse_to_sequences ( Oid  relationOid,
Oid  newOwnerId,
LOCKMODE  lockmode 
)
static

Definition at line 16301 of file tablecmds.c.

16302{
16303 Relation depRel;
16304 SysScanDesc scan;
16305 ScanKeyData key[2];
16306 HeapTuple tup;
16307
16308 /*
16309 * SERIAL sequences are those having an auto dependency on one of the
16310 * table's columns (we don't care *which* column, exactly).
16311 */
16312 depRel = table_open(DependRelationId, AccessShareLock);
16313
16314 ScanKeyInit(&key[0],
16315 Anum_pg_depend_refclassid,
16316 BTEqualStrategyNumber, F_OIDEQ,
16317 ObjectIdGetDatum(RelationRelationId));
16318 ScanKeyInit(&key[1],
16319 Anum_pg_depend_refobjid,
16320 BTEqualStrategyNumber, F_OIDEQ,
16321 ObjectIdGetDatum(relationOid));
16322 /* we leave refobjsubid unspecified */
16323
16324 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16325 NULL, 2, key);
16326
16327 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16328 {
16329 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16330 Relation seqRel;
16331
16332 /* skip dependencies other than auto dependencies on columns */
16333 if (depForm->refobjsubid == 0 ||
16334 depForm->classid != RelationRelationId ||
16335 depForm->objsubid != 0 ||
16336 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16337 continue;
16338
16339 /* Use relation_open just in case it's an index */
16340 seqRel = relation_open(depForm->objid, lockmode);
16341
16342 /* skip non-sequence relations */
16343 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16344 {
16345 /* No need to keep the lock */
16346 relation_close(seqRel, lockmode);
16347 continue;
16348 }
16349
16350 /* We don't need to close the sequence while we alter it. */
16351 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16352
16353 /* Now we can close it. Keep the lock till end of transaction. */
16354 relation_close(seqRel, NoLock);
16355 }
16356
16357 systable_endscan(scan);
16358
16360}

References AccessShareLock, ATExecChangeOwner(), BTEqualStrategyNumber, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, GETSTRUCT(), HeapTupleIsValid, sort-test::key, NoLock, ObjectIdGetDatum(), relation_close(), relation_open(), RelationGetForm, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), and table_open().

Referenced by ATExecChangeOwner().

◆ check_for_column_name_collision()

static bool check_for_column_name_collision ( Relation  rel,
const char *  colname,
bool  if_not_exists 
)
static

Definition at line 7636 of file tablecmds.c.

7638{
7639 HeapTuple attTuple;
7640 int attnum;
7641
7642 /*
7643 * this test is deliberately not attisdropped-aware, since if one tries to
7644 * add a column matching a dropped column name, it's gonna fail anyway.
7645 */
7646 attTuple = SearchSysCache2(ATTNAME,
7648 PointerGetDatum(colname));
7649 if (!HeapTupleIsValid(attTuple))
7650 return true;
7651
7652 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7653 ReleaseSysCache(attTuple);
7654
7655 /*
7656 * We throw a different error message for conflicts with system column
7657 * names, since they are normally not shown and the user might otherwise
7658 * be confused about the reason for the conflict.
7659 */
7660 if (attnum <= 0)
7661 ereport(ERROR,
7662 (errcode(ERRCODE_DUPLICATE_COLUMN),
7663 errmsg("column name \"%s\" conflicts with a system column name",
7664 colname)));
7665 else
7666 {
7667 if (if_not_exists)
7668 {
7670 (errcode(ERRCODE_DUPLICATE_COLUMN),
7671 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7672 colname, RelationGetRelationName(rel))));
7673 return false;
7674 }
7675
7676 ereport(ERROR,
7677 (errcode(ERRCODE_DUPLICATE_COLUMN),
7678 errmsg("column \"%s\" of relation \"%s\" already exists",
7679 colname, RelationGetRelationName(rel))));
7680 }
7681
7682 return true;
7683}
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:232

References attnum, ereport, errcode(), errmsg(), ERROR, GETSTRUCT(), HeapTupleIsValid, NOTICE, ObjectIdGetDatum(), PointerGetDatum(), RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), and SearchSysCache2().

Referenced by ATExecAddColumn(), and renameatt_internal().

◆ check_of_type()

void check_of_type ( HeapTuple  typetuple)

Definition at line 7133 of file tablecmds.c.

7134{
7135 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7136 bool typeOk = false;
7137
7138 if (typ->typtype == TYPTYPE_COMPOSITE)
7139 {
7140 Relation typeRelation;
7141
7142 Assert(OidIsValid(typ->typrelid));
7143 typeRelation = relation_open(typ->typrelid, AccessShareLock);
7144 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7145
7146 /*
7147 * Close the parent rel, but keep our AccessShareLock on it until xact
7148 * commit. That will prevent someone else from deleting or ALTERing
7149 * the type before the typed table creation/conversion commits.
7150 */
7151 relation_close(typeRelation, NoLock);
7152
7153 if (!typeOk)
7154 ereport(ERROR,
7155 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7156 errmsg("type %s is the row type of another table",
7157 format_type_be(typ->oid)),
7158 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7159 }
7160 else
7161 ereport(ERROR,
7162 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7163 errmsg("type %s is not a composite type",
7164 format_type_be(typ->oid))));
7165}

References AccessShareLock, Assert(), ereport, errcode(), errdetail(), errmsg(), ERROR, format_type_be(), GETSTRUCT(), NoLock, OidIsValid, RelationData::rd_rel, relation_close(), and relation_open().

Referenced by ATExecAddOf(), and transformOfType().

◆ CheckAlterTableIsSafe()

static void CheckAlterTableIsSafe ( Relation  rel)
static

Definition at line 4439 of file tablecmds.c.

4440{
4441 /*
4442 * Don't allow ALTER on temp tables of other backends. Their local buffer
4443 * manager is not going to cope if we need to change the table's contents.
4444 * Even if we don't, there may be optimizations that assume temp tables
4445 * aren't subject to such interference.
4446 */
4447 if (RELATION_IS_OTHER_TEMP(rel))
4448 ereport(ERROR,
4449 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4450 errmsg("cannot alter temporary tables of other sessions")));
4451
4452 /*
4453 * Also check for active uses of the relation in the current transaction,
4454 * including open scans and pending AFTER trigger events.
4455 */
4456 CheckTableNotInUse(rel, "ALTER TABLE");
4457}
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4406

References CheckTableNotInUse(), ereport, errcode(), errmsg(), ERROR, and RELATION_IS_OTHER_TEMP.

Referenced by addFkRecurseReferencing(), AlterTable(), ATAddCheckNNConstraint(), ATCheckPartitionsNotInUse(), ATExecAddColumn(), ATExecDropColumn(), ATPrepAlterColumnType(), ATSimpleRecursion(), ATTypedTableRecursion(), dropconstraint_internal(), and set_attnotnull().

◆ checkFkeyPermissions()

static void checkFkeyPermissions ( Relation  rel,
int16 attnums,
int  natts 
)
static

Definition at line 13614 of file tablecmds.c.

13615{
13616 Oid roleid = GetUserId();
13617 AclResult aclresult;
13618 int i;
13619
13620 /* Okay if we have relation-level REFERENCES permission */
13621 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13623 if (aclresult == ACLCHECK_OK)
13624 return;
13625 /* Else we must have REFERENCES on each column */
13626 for (i = 0; i < natts; i++)
13627 {
13628 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13629 roleid, ACL_REFERENCES);
13630 if (aclresult != ACLCHECK_OK)
13631 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13633 }
13634}
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3853
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4024
#define ACL_REFERENCES
Definition: parsenodes.h:81

References ACL_REFERENCES, aclcheck_error(), ACLCHECK_OK, get_relkind_objtype(), GetUserId(), i, pg_attribute_aclcheck(), pg_class_aclcheck(), RelationData::rd_rel, RelationGetRelationName, and RelationGetRelid.

Referenced by ATAddForeignKeyConstraint().

◆ CheckRelationTableSpaceMove()

bool CheckRelationTableSpaceMove ( Relation  rel,
Oid  newTableSpaceId 
)

Definition at line 3683 of file tablecmds.c.

3684{
3685 Oid oldTableSpaceId;
3686
3687 /*
3688 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3689 * stored as 0.
3690 */
3691 oldTableSpaceId = rel->rd_rel->reltablespace;
3692 if (newTableSpaceId == oldTableSpaceId ||
3693 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3694 return false;
3695
3696 /*
3697 * We cannot support moving mapped relations into different tablespaces.
3698 * (In particular this eliminates all shared catalogs.)
3699 */
3700 if (RelationIsMapped(rel))
3701 ereport(ERROR,
3702 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3703 errmsg("cannot move system relation \"%s\"",
3705
3706 /* Cannot move a non-shared relation into pg_global */
3707 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3708 ereport(ERROR,
3709 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3710 errmsg("only shared relations can be placed in pg_global tablespace")));
3711
3712 /*
3713 * Do not allow moving temp tables of other backends ... their local
3714 * buffer manager is not going to cope.
3715 */
3716 if (RELATION_IS_OTHER_TEMP(rel))
3717 ereport(ERROR,
3718 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3719 errmsg("cannot move temporary tables of other sessions")));
3720
3721 return true;
3722}
#define RelationIsMapped(relation)
Definition: rel.h:565

References ereport, errcode(), errmsg(), ERROR, MyDatabaseTableSpace, RelationData::rd_rel, RELATION_IS_OTHER_TEMP, RelationGetRelationName, and RelationIsMapped.

Referenced by ATExecSetTableSpace(), ATExecSetTableSpaceNoStorage(), reindex_index(), and SetRelationTableSpace().

◆ CheckTableNotInUse()

void CheckTableNotInUse ( Relation  rel,
const char *  stmt 
)

Definition at line 4406 of file tablecmds.c.

4407{
4408 int expected_refcnt;
4409
4410 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4411 if (rel->rd_refcnt != expected_refcnt)
4412 ereport(ERROR,
4413 (errcode(ERRCODE_OBJECT_IN_USE),
4414 /* translator: first %s is a SQL command, eg ALTER TABLE */
4415 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4417
4418 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4419 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4421 ereport(ERROR,
4422 (errcode(ERRCODE_OBJECT_IN_USE),
4423 /* translator: first %s is a SQL command, eg ALTER TABLE */
4424 errmsg("cannot %s \"%s\" because it has pending trigger events",
4426}
int rd_refcnt
Definition: rel.h:59
bool rd_isnailed
Definition: rel.h:62
bool AfterTriggerPendingOnRel(Oid relid)
Definition: trigger.c:6024

References AfterTriggerPendingOnRel(), ereport, errcode(), errmsg(), ERROR, RelationData::rd_isnailed, RelationData::rd_refcnt, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, and stmt.

Referenced by CheckAlterTableIsSafe(), cluster_rel(), DefineIndex(), DefineVirtualRelation(), heap_drop_with_catalog(), index_drop(), MergeAttributes(), RefreshMatViewByOid(), reindex_index(), and truncate_check_activity().

◆ ChooseForeignKeyConstraintNameAddition()

static char * ChooseForeignKeyConstraintNameAddition ( List colnames)
static

Definition at line 9835 of file tablecmds.c.

9836{
9837 char buf[NAMEDATALEN * 2];
9838 int buflen = 0;
9839 ListCell *lc;
9840
9841 buf[0] = '\0';
9842 foreach(lc, colnames)
9843 {
9844 const char *name = strVal(lfirst(lc));
9845
9846 if (buflen > 0)
9847 buf[buflen++] = '_'; /* insert _ between names */
9848
9849 /*
9850 * At this point we have buflen <= NAMEDATALEN. name should be less
9851 * than NAMEDATALEN already, but use strlcpy for paranoia.
9852 */
9853 strlcpy(buf + buflen, name, NAMEDATALEN);
9854 buflen += strlen(buf + buflen);
9855 if (buflen >= NAMEDATALEN)
9856 break;
9857 }
9858 return pstrdup(buf);
9859}
static char * buf
Definition: pg_test_fsync.c:72
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45

References buf, lfirst, name, NAMEDATALEN, pstrdup(), strlcpy(), and strVal.

Referenced by addFkConstraint(), and ATExecAddConstraint().

◆ CloneFkReferenced()

static void CloneFkReferenced ( Relation  parentRel,
Relation  partitionRel 
)
static

Definition at line 11211 of file tablecmds.c.

11212{
11213 Relation pg_constraint;
11214 AttrMap *attmap;
11215 ListCell *cell;
11216 SysScanDesc scan;
11217 ScanKeyData key[2];
11218 HeapTuple tuple;
11219 List *clone = NIL;
11220 Relation trigrel;
11221
11222 /*
11223 * Search for any constraints where this partition's parent is in the
11224 * referenced side. However, we must not clone any constraint whose
11225 * parent constraint is also going to be cloned, to avoid duplicates. So
11226 * do it in two steps: first construct the list of constraints to clone,
11227 * then go over that list cloning those whose parents are not in the list.
11228 * (We must not rely on the parent being seen first, since the catalog
11229 * scan could return children first.)
11230 */
11231 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11232 ScanKeyInit(&key[0],
11233 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11234 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11235 ScanKeyInit(&key[1],
11236 Anum_pg_constraint_contype, BTEqualStrategyNumber,
11237 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11238 /* This is a seqscan, as we don't have a usable index ... */
11239 scan = systable_beginscan(pg_constraint, InvalidOid, true,
11240 NULL, 2, key);
11241 while ((tuple = systable_getnext(scan)) != NULL)
11242 {
11243 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11244
11245 clone = lappend_oid(clone, constrForm->oid);
11246 }
11247 systable_endscan(scan);
11248 table_close(pg_constraint, RowShareLock);
11249
11250 /*
11251 * Triggers of the foreign keys will be manipulated a bunch of times in
11252 * the loop below. To avoid repeatedly opening/closing the trigger
11253 * catalog relation, we open it here and pass it to the subroutines called
11254 * below.
11255 */
11256 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11257
11258 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11259 RelationGetDescr(parentRel),
11260 false);
11261 foreach(cell, clone)
11262 {
11263 Oid constrOid = lfirst_oid(cell);
11264 Form_pg_constraint constrForm;
11265 Relation fkRel;
11266 Oid indexOid;
11267 Oid partIndexId;
11268 int numfks;
11269 AttrNumber conkey[INDEX_MAX_KEYS];
11270 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11271 AttrNumber confkey[INDEX_MAX_KEYS];
11272 Oid conpfeqop[INDEX_MAX_KEYS];
11273 Oid conppeqop[INDEX_MAX_KEYS];
11274 Oid conffeqop[INDEX_MAX_KEYS];
11275 int numfkdelsetcols;
11276 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11277 Constraint *fkconstraint;
11278 ObjectAddress address;
11279 Oid deleteTriggerOid = InvalidOid,
11280 updateTriggerOid = InvalidOid;
11281
11282 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11283 if (!HeapTupleIsValid(tuple))
11284 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11285 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11286
11287 /*
11288 * As explained above: don't try to clone a constraint for which we're
11289 * going to clone the parent.
11290 */
11291 if (list_member_oid(clone, constrForm->conparentid))
11292 {
11293 ReleaseSysCache(tuple);
11294 continue;
11295 }
11296
11297 /*
11298 * Don't clone self-referencing foreign keys, which can be in the
11299 * partitioned table or in the partition-to-be.
11300 */
11301 if (constrForm->conrelid == RelationGetRelid(parentRel) ||
11302 constrForm->conrelid == RelationGetRelid(partitionRel))
11303 {
11304 ReleaseSysCache(tuple);
11305 continue;
11306 }
11307
11308 /* We need the same lock level that CreateTrigger will acquire */
11309 fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11310
11311 indexOid = constrForm->conindid;
11313 &numfks,
11314 conkey,
11315 confkey,
11316 conpfeqop,
11317 conppeqop,
11318 conffeqop,
11319 &numfkdelsetcols,
11320 confdelsetcols);
11321
11322 for (int i = 0; i < numfks; i++)
11323 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11324
11325 fkconstraint = makeNode(Constraint);
11326 fkconstraint->contype = CONSTRAINT_FOREIGN;
11327 fkconstraint->conname = NameStr(constrForm->conname);
11328 fkconstraint->deferrable = constrForm->condeferrable;
11329 fkconstraint->initdeferred = constrForm->condeferred;
11330 fkconstraint->location = -1;
11331 fkconstraint->pktable = NULL;
11332 /* ->fk_attrs determined below */
11333 fkconstraint->pk_attrs = NIL;
11334 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11335 fkconstraint->fk_upd_action = constrForm->confupdtype;
11336 fkconstraint->fk_del_action = constrForm->confdeltype;
11337 fkconstraint->fk_del_set_cols = NIL;
11338 fkconstraint->old_conpfeqop = NIL;
11339 fkconstraint->old_pktable_oid = InvalidOid;
11340 fkconstraint->is_enforced = constrForm->conenforced;
11341 fkconstraint->skip_validation = false;
11342 fkconstraint->initially_valid = constrForm->convalidated;
11343
11344 /* set up colnames that are used to generate the constraint name */
11345 for (int i = 0; i < numfks; i++)
11346 {
11348
11349 att = TupleDescAttr(RelationGetDescr(fkRel),
11350 conkey[i] - 1);
11351 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11352 makeString(NameStr(att->attname)));
11353 }
11354
11355 /*
11356 * Add the new foreign key constraint pointing to the new partition.
11357 * Because this new partition appears in the referenced side of the
11358 * constraint, we don't need to set up for Phase 3 check.
11359 */
11360 partIndexId = index_get_partition(partitionRel, indexOid);
11361 if (!OidIsValid(partIndexId))
11362 elog(ERROR, "index for %u not found in partition %s",
11363 indexOid, RelationGetRelationName(partitionRel));
11364
11365 /*
11366 * Get the "action" triggers belonging to the constraint to pass as
11367 * parent OIDs for similar triggers that will be created on the
11368 * partition in addFkRecurseReferenced().
11369 */
11370 if (constrForm->conenforced)
11371 GetForeignKeyActionTriggers(trigrel, constrOid,
11372 constrForm->confrelid, constrForm->conrelid,
11373 &deleteTriggerOid, &updateTriggerOid);
11374
11375 /* Add this constraint ... */
11377 fkconstraint->conname, fkconstraint, fkRel,
11378 partitionRel, partIndexId, constrOid,
11379 numfks, mapped_confkey,
11380 conkey, conpfeqop, conppeqop, conffeqop,
11381 numfkdelsetcols, confdelsetcols, false,
11382 constrForm->conperiod);
11383 /* ... and recurse */
11384 addFkRecurseReferenced(fkconstraint,
11385 fkRel,
11386 partitionRel,
11387 partIndexId,
11388 address.objectId,
11389 numfks,
11390 mapped_confkey,
11391 conkey,
11392 conpfeqop,
11393 conppeqop,
11394 conffeqop,
11395 numfkdelsetcols,
11396 confdelsetcols,
11397 true,
11398 deleteTriggerOid,
11399 updateTriggerOid,
11400 constrForm->conperiod);
11401
11402 table_close(fkRel, NoLock);
11403 ReleaseSysCache(tuple);
11404 }
11405
11406 table_close(trigrel, RowExclusiveLock);
11407}
void DeconstructFkConstraintRow(HeapTuple tuple, int *numfks, AttrNumber *conkey, AttrNumber *confkey, Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs, int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols)
static Datum CharGetDatum(char X)
Definition: postgres.h:127
ParseLoc location
Definition: parsenodes.h:2866
static void GetForeignKeyActionTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12044

References addFkConstraint(), addFkRecurseReferenced(), addFkReferencedSide, AttrMap::attnums, BTEqualStrategyNumber, build_attrmap_by_name(), CharGetDatum(), Constraint::conname, Constraint::contype, DeconstructFkConstraintRow(), Constraint::deferrable, elog, ERROR, Constraint::fk_attrs, Constraint::fk_del_action, Constraint::fk_del_set_cols, Constraint::fk_matchtype, Constraint::fk_upd_action, GetForeignKeyActionTriggers(), GETSTRUCT(), HeapTupleIsValid, i, index_get_partition(), INDEX_MAX_KEYS, Constraint::initdeferred, Constraint::initially_valid, InvalidOid, Constraint::is_enforced, sort-test::key, lappend(), lappend_oid(), lfirst_oid, list_member_oid(), Constraint::location, makeNode, makeString(), NameStr, NIL, NoLock, ObjectAddress::objectId, ObjectIdGetDatum(), OidIsValid, Constraint::old_conpfeqop, Constraint::old_pktable_oid, Constraint::pk_attrs, Constraint::pktable, RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, RowShareLock, ScanKeyInit(), SearchSysCache1(), ShareRowExclusiveLock, Constraint::skip_validation, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), and TupleDescAttr().

Referenced by CloneForeignKeyConstraints().

◆ CloneFkReferencing()

static void CloneFkReferencing ( List **  wqueue,
Relation  parentRel,
Relation  partRel 
)
static

Definition at line 11423 of file tablecmds.c.

11424{
11425 AttrMap *attmap;
11426 List *partFKs;
11427 List *clone = NIL;
11428 ListCell *cell;
11429 Relation trigrel;
11430
11431 /* obtain a list of constraints that we need to clone */
11432 foreach(cell, RelationGetFKeyList(parentRel))
11433 {
11434 ForeignKeyCacheInfo *fk = lfirst(cell);
11435
11436 /*
11437 * Refuse to attach a table as partition that this partitioned table
11438 * already has a foreign key to. This isn't useful schema, which is
11439 * proven by the fact that there have been no user complaints that
11440 * it's already impossible to achieve this in the opposite direction,
11441 * i.e., creating a foreign key that references a partition. This
11442 * restriction allows us to dodge some complexities around
11443 * pg_constraint and pg_trigger row creations that would be needed
11444 * during ATTACH/DETACH for this kind of relationship.
11445 */
11446 if (fk->confrelid == RelationGetRelid(partRel))
11447 ereport(ERROR,
11448 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11449 errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11450 RelationGetRelationName(partRel),
11452
11453 clone = lappend_oid(clone, fk->conoid);
11454 }
11455
11456 /*
11457 * Silently do nothing if there's nothing to do. In particular, this
11458 * avoids throwing a spurious error for foreign tables.
11459 */
11460 if (clone == NIL)
11461 return;
11462
11463 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11464 ereport(ERROR,
11465 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11466 errmsg("foreign key constraints are not supported on foreign tables")));
11467
11468 /*
11469 * Triggers of the foreign keys will be manipulated a bunch of times in
11470 * the loop below. To avoid repeatedly opening/closing the trigger
11471 * catalog relation, we open it here and pass it to the subroutines called
11472 * below.
11473 */
11474 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11475
11476 /*
11477 * The constraint key may differ, if the columns in the partition are
11478 * different. This map is used to convert them.
11479 */
11480 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11481 RelationGetDescr(parentRel),
11482 false);
11483
11484 partFKs = copyObject(RelationGetFKeyList(partRel));
11485
11486 foreach(cell, clone)
11487 {
11488 Oid parentConstrOid = lfirst_oid(cell);
11489 Form_pg_constraint constrForm;
11490 Relation pkrel;
11491 HeapTuple tuple;
11492 int numfks;
11493 AttrNumber conkey[INDEX_MAX_KEYS];
11494 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11495 AttrNumber confkey[INDEX_MAX_KEYS];
11496 Oid conpfeqop[INDEX_MAX_KEYS];
11497 Oid conppeqop[INDEX_MAX_KEYS];
11498 Oid conffeqop[INDEX_MAX_KEYS];
11499 int numfkdelsetcols;
11500 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11501 Constraint *fkconstraint;
11502 bool attached;
11503 Oid indexOid;
11504 ObjectAddress address;
11505 ListCell *lc;
11506 Oid insertTriggerOid = InvalidOid,
11507 updateTriggerOid = InvalidOid;
11508 bool with_period;
11509
11510 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11511 if (!HeapTupleIsValid(tuple))
11512 elog(ERROR, "cache lookup failed for constraint %u",
11513 parentConstrOid);
11514 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11515
11516 /* Don't clone constraints whose parents are being cloned */
11517 if (list_member_oid(clone, constrForm->conparentid))
11518 {
11519 ReleaseSysCache(tuple);
11520 continue;
11521 }
11522
11523 /*
11524 * Need to prevent concurrent deletions. If pkrel is a partitioned
11525 * relation, that means to lock all partitions.
11526 */
11527 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11528 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11530 ShareRowExclusiveLock, NULL);
11531
11532 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11533 conpfeqop, conppeqop, conffeqop,
11534 &numfkdelsetcols, confdelsetcols);
11535 for (int i = 0; i < numfks; i++)
11536 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11537
11538 /*
11539 * Get the "check" triggers belonging to the constraint, if it is
11540 * ENFORCED, to pass as parent OIDs for similar triggers that will be
11541 * created on the partition in addFkRecurseReferencing(). They are
11542 * also passed to tryAttachPartitionForeignKey() below to simply
11543 * assign as parents to the partition's existing "check" triggers,
11544 * that is, if the corresponding constraints is deemed attachable to
11545 * the parent constraint.
11546 */
11547 if (constrForm->conenforced)
11548 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11549 constrForm->confrelid, constrForm->conrelid,
11550 &insertTriggerOid, &updateTriggerOid);
11551
11552 /*
11553 * Before creating a new constraint, see whether any existing FKs are
11554 * fit for the purpose. If one is, attach the parent constraint to
11555 * it, and don't clone anything. This way we avoid the expensive
11556 * verification step and don't end up with a duplicate FK, and we
11557 * don't need to recurse to partitions for this constraint.
11558 */
11559 attached = false;
11560 foreach(lc, partFKs)
11561 {
11563
11565 fk,
11566 partRel,
11567 parentConstrOid,
11568 numfks,
11569 mapped_conkey,
11570 confkey,
11571 conpfeqop,
11572 insertTriggerOid,
11573 updateTriggerOid,
11574 trigrel))
11575 {
11576 attached = true;
11577 table_close(pkrel, NoLock);
11578 break;
11579 }
11580 }
11581 if (attached)
11582 {
11583 ReleaseSysCache(tuple);
11584 continue;
11585 }
11586
11587 /* No dice. Set up to create our own constraint */
11588 fkconstraint = makeNode(Constraint);
11589 fkconstraint->contype = CONSTRAINT_FOREIGN;
11590 /* ->conname determined below */
11591 fkconstraint->deferrable = constrForm->condeferrable;
11592 fkconstraint->initdeferred = constrForm->condeferred;
11593 fkconstraint->location = -1;
11594 fkconstraint->pktable = NULL;
11595 /* ->fk_attrs determined below */
11596 fkconstraint->pk_attrs = NIL;
11597 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11598 fkconstraint->fk_upd_action = constrForm->confupdtype;
11599 fkconstraint->fk_del_action = constrForm->confdeltype;
11600 fkconstraint->fk_del_set_cols = NIL;
11601 fkconstraint->old_conpfeqop = NIL;
11602 fkconstraint->old_pktable_oid = InvalidOid;
11603 fkconstraint->is_enforced = constrForm->conenforced;
11604 fkconstraint->skip_validation = false;
11605 fkconstraint->initially_valid = constrForm->convalidated;
11606 for (int i = 0; i < numfks; i++)
11607 {
11609
11610 att = TupleDescAttr(RelationGetDescr(partRel),
11611 mapped_conkey[i] - 1);
11612 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11613 makeString(NameStr(att->attname)));
11614 }
11615
11616 indexOid = constrForm->conindid;
11617 with_period = constrForm->conperiod;
11618
11619 /* Create the pg_constraint entry at this level */
11621 NameStr(constrForm->conname), fkconstraint,
11622 partRel, pkrel, indexOid, parentConstrOid,
11623 numfks, confkey,
11624 mapped_conkey, conpfeqop,
11625 conppeqop, conffeqop,
11626 numfkdelsetcols, confdelsetcols,
11627 false, with_period);
11628
11629 /* Done with the cloned constraint's tuple */
11630 ReleaseSysCache(tuple);
11631
11632 /* Create the check triggers, and recurse to partitions, if any */
11634 fkconstraint,
11635 partRel,
11636 pkrel,
11637 indexOid,
11638 address.objectId,
11639 numfks,
11640 confkey,
11641 mapped_conkey,
11642 conpfeqop,
11643 conppeqop,
11644 conffeqop,
11645 numfkdelsetcols,
11646 confdelsetcols,
11647 false, /* no old check exists */
11649 insertTriggerOid,
11650 updateTriggerOid,
11651 with_period);
11652 table_close(pkrel, NoLock);
11653 }
11654
11655 table_close(trigrel, RowExclusiveLock);
11656}

References AccessExclusiveLock, addFkConstraint(), addFkRecurseReferencing(), addFkReferencingSide, AttrMap::attnums, build_attrmap_by_name(), ForeignKeyCacheInfo::confrelid, ForeignKeyCacheInfo::conoid, Constraint::contype, copyObject, DeconstructFkConstraintRow(), Constraint::deferrable, elog, ereport, errcode(), errmsg(), ERROR, find_all_inheritors(), Constraint::fk_attrs, Constraint::fk_del_action, Constraint::fk_del_set_cols, Constraint::fk_matchtype, Constraint::fk_upd_action, get_constraint_name(), GetForeignKeyCheckTriggers(), GETSTRUCT(), HeapTupleIsValid, i, if(), INDEX_MAX_KEYS, Constraint::initdeferred, Constraint::initially_valid, InvalidOid, Constraint::is_enforced, lappend(), lappend_oid(), lfirst, lfirst_node, lfirst_oid, list_member_oid(), Constraint::location, makeNode, makeString(), NameStr, NIL, NoLock, ObjectAddress::objectId, ObjectIdGetDatum(), Constraint::old_conpfeqop, Constraint::old_pktable_oid, Constraint::pk_attrs, Constraint::pktable, RelationData::rd_rel, RelationGetDescr, RelationGetFKeyList(), RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), ShareRowExclusiveLock, Constraint::skip_validation, table_close(), table_open(), tryAttachPartitionForeignKey(), and TupleDescAttr().

Referenced by CloneForeignKeyConstraints().

◆ CloneForeignKeyConstraints()

static void CloneForeignKeyConstraints ( List **  wqueue,
Relation  parentRel,
Relation  partitionRel 
)
static

Definition at line 11180 of file tablecmds.c.

11182{
11183 /* This only works for declarative partitioning */
11184 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11185
11186 /*
11187 * Clone constraints for which the parent is on the referenced side.
11188 */
11189 CloneFkReferenced(parentRel, partitionRel);
11190
11191 /*
11192 * Now clone constraints where the parent is on the referencing side.
11193 */
11194 CloneFkReferencing(wqueue, parentRel, partitionRel);
11195}
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11423
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11211

References Assert(), CloneFkReferenced(), CloneFkReferencing(), and RelationData::rd_rel.

Referenced by ATExecAttachPartition(), and DefineRelation().

◆ CloneRowTriggersToPartition()

static void CloneRowTriggersToPartition ( Relation  parent,
Relation  partition 
)
static

Definition at line 20668 of file tablecmds.c.

20669{
20670 Relation pg_trigger;
20672 SysScanDesc scan;
20673 HeapTuple tuple;
20674 MemoryContext perTupCxt;
20675
20676 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20677 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20678 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20679 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20680 true, NULL, 1, &key);
20681
20683 "clone trig", ALLOCSET_SMALL_SIZES);
20684
20685 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20686 {
20687 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20688 CreateTrigStmt *trigStmt;
20689 Node *qual = NULL;
20690 Datum value;
20691 bool isnull;
20692 List *cols = NIL;
20693 List *trigargs = NIL;
20694 MemoryContext oldcxt;
20695
20696 /*
20697 * Ignore statement-level triggers; those are not cloned.
20698 */
20699 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20700 continue;
20701
20702 /*
20703 * Don't clone internal triggers, because the constraint cloning code
20704 * will.
20705 */
20706 if (trigForm->tgisinternal)
20707 continue;
20708
20709 /*
20710 * Complain if we find an unexpected trigger type.
20711 */
20712 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20713 !TRIGGER_FOR_AFTER(trigForm->tgtype))
20714 elog(ERROR, "unexpected trigger \"%s\" found",
20715 NameStr(trigForm->tgname));
20716
20717 /* Use short-lived context for CREATE TRIGGER */
20718 oldcxt = MemoryContextSwitchTo(perTupCxt);
20719
20720 /*
20721 * If there is a WHEN clause, generate a 'cooked' version of it that's
20722 * appropriate for the partition.
20723 */
20724 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20725 RelationGetDescr(pg_trigger), &isnull);
20726 if (!isnull)
20727 {
20729 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20730 partition, parent);
20731 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20732 partition, parent);
20733 }
20734
20735 /*
20736 * If there is a column list, transform it to a list of column names.
20737 * Note we don't need to map this list in any way ...
20738 */
20739 if (trigForm->tgattr.dim1 > 0)
20740 {
20741 int i;
20742
20743 for (i = 0; i < trigForm->tgattr.dim1; i++)
20744 {
20746
20747 col = TupleDescAttr(parent->rd_att,
20748 trigForm->tgattr.values[i] - 1);
20749 cols = lappend(cols,
20750 makeString(pstrdup(NameStr(col->attname))));
20751 }
20752 }
20753
20754 /* Reconstruct trigger arguments list. */
20755 if (trigForm->tgnargs > 0)
20756 {
20757 char *p;
20758
20759 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20760 RelationGetDescr(pg_trigger), &isnull);
20761 if (isnull)
20762 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20763 NameStr(trigForm->tgname), RelationGetRelationName(partition));
20764
20765 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20766
20767 for (int i = 0; i < trigForm->tgnargs; i++)
20768 {
20769 trigargs = lappend(trigargs, makeString(pstrdup(p)));
20770 p += strlen(p) + 1;
20771 }
20772 }
20773
20774 trigStmt = makeNode(CreateTrigStmt);
20775 trigStmt->replace = false;
20776 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20777 trigStmt->trigname = NameStr(trigForm->tgname);
20778 trigStmt->relation = NULL;
20779 trigStmt->funcname = NULL; /* passed separately */
20780 trigStmt->args = trigargs;
20781 trigStmt->row = true;
20782 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20783 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20784 trigStmt->columns = cols;
20785 trigStmt->whenClause = NULL; /* passed separately */
20786 trigStmt->transitionRels = NIL; /* not supported at present */
20787 trigStmt->deferrable = trigForm->tgdeferrable;
20788 trigStmt->initdeferred = trigForm->tginitdeferred;
20789 trigStmt->constrrel = NULL; /* passed separately */
20790
20791 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20792 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20793 trigForm->tgfoid, trigForm->oid, qual,
20794 false, true, trigForm->tgenabled);
20795
20796 MemoryContextSwitchTo(oldcxt);
20797 MemoryContextReset(perTupCxt);
20798 }
20799
20800 MemoryContextDelete(perTupCxt);
20801
20802 systable_endscan(scan);
20803 table_close(pg_trigger, RowExclusiveLock);
20804}
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define DatumGetByteaPP(X)
Definition: fmgr.h:291
static struct @165 value
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:414
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:190
#define PRS2_OLD_VARNO
Definition: primnodes.h:250
#define PRS2_NEW_VARNO
Definition: primnodes.h:251
void * stringToNode(const char *str)
Definition: read.c:90
Node * whenClause
Definition: parsenodes.h:3110
List * transitionRels
Definition: parsenodes.h:3112
RangeVar * constrrel
Definition: parsenodes.h:3116
RangeVar * relation
Definition: parsenodes.h:3101
ObjectAddress CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
Definition: trigger.c:177
#define VARDATA_ANY(PTR)
Definition: varatt.h:324

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, CreateTrigStmt::args, BTEqualStrategyNumber, CreateTrigStmt::columns, CreateTrigStmt::constrrel, CreateTriggerFiringOn(), CurrentMemoryContext, DatumGetByteaPP, CreateTrigStmt::deferrable, elog, ERROR, CreateTrigStmt::events, CreateTrigStmt::funcname, GETSTRUCT(), heap_getattr(), HeapTupleIsValid, i, CreateTrigStmt::initdeferred, InvalidOid, CreateTrigStmt::isconstraint, sort-test::key, lappend(), makeNode, makeString(), map_partition_varattnos(), MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), NameStr, NIL, ObjectIdGetDatum(), OidIsValid, PRS2_NEW_VARNO, PRS2_OLD_VARNO, pstrdup(), RelationData::rd_att, CreateTrigStmt::relation, RelationGetDescr, RelationGetRelationName, RelationGetRelid, CreateTrigStmt::replace, CreateTrigStmt::row, RowExclusiveLock, ScanKeyInit(), stringToNode(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TextDatumGetCString, CreateTrigStmt::timing, CreateTrigStmt::transitionRels, CreateTrigStmt::trigname, TupleDescAttr(), value, VARDATA_ANY, and CreateTrigStmt::whenClause.

Referenced by ATExecAttachPartition(), and DefineRelation().

◆ ComputePartitionAttrs()

static void ComputePartitionAttrs ( ParseState pstate,
Relation  rel,
List partParams,
AttrNumber partattrs,
List **  partexprs,
Oid partopclass,
Oid partcollation,
PartitionStrategy  strategy 
)
static

Definition at line 19708 of file tablecmds.c.

19711{
19712 int attn;
19713 ListCell *lc;
19714 Oid am_oid;
19715
19716 attn = 0;
19717 foreach(lc, partParams)
19718 {
19720 Oid atttype;
19721 Oid attcollation;
19722
19723 if (pelem->name != NULL)
19724 {
19725 /* Simple attribute reference */
19726 HeapTuple atttuple;
19727 Form_pg_attribute attform;
19728
19730 pelem->name);
19731 if (!HeapTupleIsValid(atttuple))
19732 ereport(ERROR,
19733 (errcode(ERRCODE_UNDEFINED_COLUMN),
19734 errmsg("column \"%s\" named in partition key does not exist",
19735 pelem->name),
19736 parser_errposition(pstate, pelem->location)));
19737 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19738
19739 if (attform->attnum <= 0)
19740 ereport(ERROR,
19741 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19742 errmsg("cannot use system column \"%s\" in partition key",
19743 pelem->name),
19744 parser_errposition(pstate, pelem->location)));
19745
19746 /*
19747 * Stored generated columns cannot work: They are computed after
19748 * BEFORE triggers, but partition routing is done before all
19749 * triggers. Maybe virtual generated columns could be made to
19750 * work, but then they would need to be handled as an expression
19751 * below.
19752 */
19753 if (attform->attgenerated)
19754 ereport(ERROR,
19755 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19756 errmsg("cannot use generated column in partition key"),
19757 errdetail("Column \"%s\" is a generated column.",
19758 pelem->name),
19759 parser_errposition(pstate, pelem->location)));
19760
19761 partattrs[attn] = attform->attnum;
19762 atttype = attform->atttypid;
19763 attcollation = attform->attcollation;
19764 ReleaseSysCache(atttuple);
19765 }
19766 else
19767 {
19768 /* Expression */
19769 Node *expr = pelem->expr;
19770 char partattname[16];
19771
19772 Assert(expr != NULL);
19773 atttype = exprType(expr);
19774 attcollation = exprCollation(expr);
19775
19776 /*
19777 * The expression must be of a storable type (e.g., not RECORD).
19778 * The test is the same as for whether a table column is of a safe
19779 * type (which is why we needn't check for the non-expression
19780 * case).
19781 */
19782 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19783 CheckAttributeType(partattname,
19784 atttype, attcollation,
19786
19787 /*
19788 * Strip any top-level COLLATE clause. This ensures that we treat
19789 * "x COLLATE y" and "(x COLLATE y)" alike.
19790 */
19791 while (IsA(expr, CollateExpr))
19792 expr = (Node *) ((CollateExpr *) expr)->arg;
19793
19794 if (IsA(expr, Var) &&
19795 ((Var *) expr)->varattno > 0)
19796 {
19797 /*
19798 * User wrote "(column)" or "(column COLLATE something)".
19799 * Treat it like simple attribute anyway.
19800 */
19801 partattrs[attn] = ((Var *) expr)->varattno;
19802 }
19803 else
19804 {
19805 Bitmapset *expr_attrs = NULL;
19806 int i;
19807
19808 partattrs[attn] = 0; /* marks the column as expression */
19809 *partexprs = lappend(*partexprs, expr);
19810
19811 /*
19812 * transformPartitionSpec() should have already rejected
19813 * subqueries, aggregates, window functions, and SRFs, based
19814 * on the EXPR_KIND_ for partition expressions.
19815 */
19816
19817 /*
19818 * Cannot allow system column references, since that would
19819 * make partition routing impossible: their values won't be
19820 * known yet when we need to do that.
19821 */
19822 pull_varattnos(expr, 1, &expr_attrs);
19823 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
19824 {
19826 expr_attrs))
19827 ereport(ERROR,
19828 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19829 errmsg("partition key expressions cannot contain system column references")));
19830 }
19831
19832 /*
19833 * Stored generated columns cannot work: They are computed
19834 * after BEFORE triggers, but partition routing is done before
19835 * all triggers. Virtual generated columns could probably
19836 * work, but it would require more work elsewhere (for example
19837 * SET EXPRESSION would need to check whether the column is
19838 * used in partition keys). Seems safer to prohibit for now.
19839 */
19840 i = -1;
19841 while ((i = bms_next_member(expr_attrs, i)) >= 0)
19842 {
19844
19845 if (attno > 0 &&
19846 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19847 ereport(ERROR,
19848 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19849 errmsg("cannot use generated column in partition key"),
19850 errdetail("Column \"%s\" is a generated column.",
19851 get_attname(RelationGetRelid(rel), attno, false)),
19852 parser_errposition(pstate, pelem->location)));
19853 }
19854
19855 /*
19856 * Preprocess the expression before checking for mutability.
19857 * This is essential for the reasons described in
19858 * contain_mutable_functions_after_planning. However, we call
19859 * expression_planner for ourselves rather than using that
19860 * function, because if constant-folding reduces the
19861 * expression to a constant, we'd like to know that so we can
19862 * complain below.
19863 *
19864 * Like contain_mutable_functions_after_planning, assume that
19865 * expression_planner won't scribble on its input, so this
19866 * won't affect the partexprs entry we saved above.
19867 */
19868 expr = (Node *) expression_planner((Expr *) expr);
19869
19870 /*
19871 * Partition expressions cannot contain mutable functions,
19872 * because a given row must always map to the same partition
19873 * as long as there is no change in the partition boundary
19874 * structure.
19875 */
19876 if (contain_mutable_functions(expr))
19877 ereport(ERROR,
19878 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19879 errmsg("functions in partition key expression must be marked IMMUTABLE")));
19880
19881 /*
19882 * While it is not exactly *wrong* for a partition expression
19883 * to be a constant, it seems better to reject such keys.
19884 */
19885 if (IsA(expr, Const))
19886 ereport(ERROR,
19887 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19888 errmsg("cannot use constant expression as partition key")));
19889 }
19890 }
19891
19892 /*
19893 * Apply collation override if any
19894 */
19895 if (pelem->collation)
19896 attcollation = get_collation_oid(pelem->collation, false);
19897
19898 /*
19899 * Check we have a collation iff it's a collatable type. The only
19900 * expected failures here are (1) COLLATE applied to a noncollatable
19901 * type, or (2) partition expression had an unresolved collation. But
19902 * we might as well code this to be a complete consistency check.
19903 */
19904 if (type_is_collatable(atttype))
19905 {
19906 if (!OidIsValid(attcollation))
19907 ereport(ERROR,
19908 (errcode(ERRCODE_INDETERMINATE_COLLATION),
19909 errmsg("could not determine which collation to use for partition expression"),
19910 errhint("Use the COLLATE clause to set the collation explicitly.")));
19911 }
19912 else
19913 {
19914 if (OidIsValid(attcollation))
19915 ereport(ERROR,
19916 (errcode(ERRCODE_DATATYPE_MISMATCH),
19917 errmsg("collations are not supported by type %s",
19918 format_type_be(atttype))));
19919 }
19920
19921 partcollation[attn] = attcollation;
19922
19923 /*
19924 * Identify the appropriate operator class. For list and range
19925 * partitioning, we use a btree operator class; hash partitioning uses
19926 * a hash operator class.
19927 */
19928 if (strategy == PARTITION_STRATEGY_HASH)
19929 am_oid = HASH_AM_OID;
19930 else
19931 am_oid = BTREE_AM_OID;
19932
19933 if (!pelem->opclass)
19934 {
19935 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
19936
19937 if (!OidIsValid(partopclass[attn]))
19938 {
19939 if (strategy == PARTITION_STRATEGY_HASH)
19940 ereport(ERROR,
19941 (errcode(ERRCODE_UNDEFINED_OBJECT),
19942 errmsg("data type %s has no default operator class for access method \"%s\"",
19943 format_type_be(atttype), "hash"),
19944 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
19945 else
19946 ereport(ERROR,
19947 (errcode(ERRCODE_UNDEFINED_OBJECT),
19948 errmsg("data type %s has no default operator class for access method \"%s\"",
19949 format_type_be(atttype), "btree"),
19950 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19951 }
19952 }
19953 else
19954 partopclass[attn] = ResolveOpClass(pelem->opclass,
19955 atttype,
19956 am_oid == HASH_AM_OID ? "hash" : "btree",
19957 am_oid);
19958
19959 attn++;
19960 }
19961}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
bool contain_mutable_functions(Node *clause)
Definition: clauses.c:371
#define CHKATYPE_IS_PARTKEY
Definition: heap.h:25
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2345
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition: indexcmds.c:2260
bool type_is_collatable(Oid typid)
Definition: lsyscache.c:3221
Oid get_collation_oid(List *collname, bool missing_ok)
Definition: namespace.c:3971
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:821
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:885
#define snprintf
Definition: port.h:239
List * collation
Definition: parsenodes.h:876
ParseLoc location
Definition: parsenodes.h:878
List * opclass
Definition: parsenodes.h:877
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:296

References arg, Assert(), bms_is_member(), bms_next_member(), CheckAttributeType(), CHKATYPE_IS_PARTKEY, PartitionElem::collation, contain_mutable_functions(), ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, PartitionElem::expr, exprCollation(), expression_planner(), exprType(), FirstLowInvalidHeapAttributeNumber, format_type_be(), get_attname(), get_collation_oid(), GetDefaultOpClass(), GETSTRUCT(), HeapTupleIsValid, i, IsA, lappend(), lfirst_node, PartitionElem::location, PartitionElem::name, NIL, OidIsValid, PartitionElem::opclass, parser_errposition(), PARTITION_STRATEGY_HASH, pull_varattnos(), RelationGetDescr, RelationGetRelid, ReleaseSysCache(), ResolveOpClass(), SearchSysCacheAttName(), snprintf, TupleDescAttr(), and type_is_collatable().

Referenced by DefineRelation().

◆ ConstraintImpliedByRelConstraint()

bool ConstraintImpliedByRelConstraint ( Relation  scanrel,
List testConstraint,
List provenConstraint 
)
static

Definition at line 20027 of file tablecmds.c.

20028{
20029 List *existConstraint = list_copy(provenConstraint);
20030 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20031 int num_check,
20032 i;
20033
20034 num_check = (constr != NULL) ? constr->num_check : 0;
20035 for (i = 0; i < num_check; i++)
20036 {
20037 Node *cexpr;
20038
20039 /*
20040 * If this constraint hasn't been fully validated yet, we must ignore
20041 * it here.
20042 */
20043 if (!constr->check[i].ccvalid)
20044 continue;
20045
20046 /*
20047 * NOT ENFORCED constraints are always marked as invalid, which should
20048 * have been ignored.
20049 */
20050 Assert(constr->check[i].ccenforced);
20051
20052 cexpr = stringToNode(constr->check[i].ccbin);
20053
20054 /*
20055 * Run each expression through const-simplification and
20056 * canonicalization. It is necessary, because we will be comparing it
20057 * to similarly-processed partition constraint expressions, and may
20058 * fail to detect valid matches without this.
20059 */
20060 cexpr = eval_const_expressions(NULL, cexpr);
20061 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20062
20063 existConstraint = list_concat(existConstraint,
20064 make_ands_implicit((Expr *) cexpr));
20065 }
20066
20067 /*
20068 * Try to make the proof. Since we are comparing CHECK constraints, we
20069 * need to use weak implication, i.e., we assume existConstraint is
20070 * not-false and try to prove the same for testConstraint.
20071 *
20072 * Note that predicate_implied_by assumes its first argument is known
20073 * immutable. That should always be true for both NOT NULL and partition
20074 * constraints, so we don't test it here.
20075 */
20076 return predicate_implied_by(testConstraint, existConstraint, true);
20077}
for(;;)
List * list_copy(const List *oldlist)
Definition: list.c:1573
List * make_ands_implicit(Expr *clause)
Definition: makefuncs.c:810
bool predicate_implied_by(List *predicate_list, List *clause_list, bool weak)
Definition: predtest.c:152
Expr * canonicalize_qual(Expr *qual, bool is_check)
Definition: prepqual.c:293
bool ccenforced
Definition: tupdesc.h:32
bool ccvalid
Definition: tupdesc.h:33
char * ccbin
Definition: tupdesc.h:31
ConstrCheck * check
Definition: tupdesc.h:41

References Assert(), canonicalize_qual(), ConstrCheck::ccbin, ConstrCheck::ccenforced, ConstrCheck::ccvalid, TupleConstr::check, eval_const_expressions(), for(), i, list_concat(), list_copy(), make_ands_implicit(), TupleConstr::num_check, predicate_implied_by(), RelationGetDescr, and stringToNode().

Referenced by NotNullImpliedByRelConstraints(), and PartConstraintImpliedByRelConstraint().

◆ constraints_equivalent()

static bool constraints_equivalent ( HeapTuple  a,
HeapTuple  b,
TupleDesc  tupleDesc 
)
static

Definition at line 17394 of file tablecmds.c.

17395{
17398
17399 if (acon->condeferrable != bcon->condeferrable ||
17400 acon->condeferred != bcon->condeferred ||
17401 strcmp(decompile_conbin(a, tupleDesc),
17402 decompile_conbin(b, tupleDesc)) != 0)
17403 return false;
17404 else
17405 return true;
17406}
int b
Definition: isn.c:74
int a
Definition: isn.c:73
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:17366

References a, b, decompile_conbin(), and GETSTRUCT().

Referenced by MergeConstraintsIntoExisting().

◆ CreateFKCheckTrigger()

static Oid CreateFKCheckTrigger ( Oid  myRelOid,
Oid  refRelOid,
Constraint fkconstraint,
Oid  constraintOid,
Oid  indexOid,
Oid  parentTrigOid,
bool  on_insert 
)
static

Definition at line 13743 of file tablecmds.c.

13746{
13747 ObjectAddress trigAddress;
13748 CreateTrigStmt *fk_trigger;
13749
13750 /*
13751 * Note: for a self-referential FK (referencing and referenced tables are
13752 * the same), it is important that the ON UPDATE action fires before the
13753 * CHECK action, since both triggers will fire on the same row during an
13754 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13755 * state of the row. Triggers fire in name order, so we ensure this by
13756 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13757 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13758 */
13759 fk_trigger = makeNode(CreateTrigStmt);
13760 fk_trigger->replace = false;
13761 fk_trigger->isconstraint = true;
13762 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13763 fk_trigger->relation = NULL;
13764
13765 /* Either ON INSERT or ON UPDATE */
13766 if (on_insert)
13767 {
13768 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13769 fk_trigger->events = TRIGGER_TYPE_INSERT;
13770 }
13771 else
13772 {
13773 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13774 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13775 }
13776
13777 fk_trigger->args = NIL;
13778 fk_trigger->row = true;
13779 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13780 fk_trigger->columns = NIL;
13781 fk_trigger->whenClause = NULL;
13782 fk_trigger->transitionRels = NIL;
13783 fk_trigger->deferrable = fkconstraint->deferrable;
13784 fk_trigger->initdeferred = fkconstraint->initdeferred;
13785 fk_trigger->constrrel = NULL;
13786
13787 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13788 constraintOid, indexOid, InvalidOid,
13789 parentTrigOid, NULL, true, false);
13790
13791 /* Make changes-so-far visible */
13793
13794 return trigAddress.objectId;
13795}
List * SystemFuncName(char *name)
ObjectAddress CreateTrigger(CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition)
Definition: trigger.c:160

References CreateTrigStmt::args, CreateTrigStmt::columns, CommandCounterIncrement(), CreateTrigStmt::constrrel, CreateTrigger(), Constraint::deferrable, CreateTrigStmt::deferrable, CreateTrigStmt::events, CreateTrigStmt::funcname, Constraint::initdeferred, CreateTrigStmt::initdeferred, InvalidOid, CreateTrigStmt::isconstraint, makeNode, NIL, ObjectAddress::objectId, CreateTrigStmt::relation, CreateTrigStmt::replace, CreateTrigStmt::row, SystemFuncName(), CreateTrigStmt::timing, CreateTrigStmt::transitionRels, CreateTrigStmt::trigname, and CreateTrigStmt::whenClause.

Referenced by createForeignKeyCheckTriggers().

◆ createForeignKeyActionTriggers()

static void createForeignKeyActionTriggers ( Oid  myRelOid,
Oid  refRelOid,
Constraint fkconstraint,
Oid  constraintOid,
Oid  indexOid,
Oid  parentDelTrigger,
Oid  parentUpdTrigger,
Oid deleteTrigOid,
Oid updateTrigOid 
)
static

Definition at line 13806 of file tablecmds.c.

13810{
13811 CreateTrigStmt *fk_trigger;
13812 ObjectAddress trigAddress;
13813
13814 /*
13815 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13816 * DELETE action on the referenced table.
13817 */
13818 fk_trigger = makeNode(CreateTrigStmt);
13819 fk_trigger->replace = false;
13820 fk_trigger->isconstraint = true;
13821 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13822 fk_trigger->relation = NULL;
13823 fk_trigger->args = NIL;
13824 fk_trigger->row = true;
13825 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13826 fk_trigger->events = TRIGGER_TYPE_DELETE;
13827 fk_trigger->columns = NIL;
13828 fk_trigger->whenClause = NULL;
13829 fk_trigger->transitionRels = NIL;
13830 fk_trigger->constrrel = NULL;
13831
13832 switch (fkconstraint->fk_del_action)
13833 {
13835 fk_trigger->deferrable = fkconstraint->deferrable;
13836 fk_trigger->initdeferred = fkconstraint->initdeferred;
13837 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13838 break;
13840 fk_trigger->deferrable = false;
13841 fk_trigger->initdeferred = false;
13842 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13843 break;
13845 fk_trigger->deferrable = false;
13846 fk_trigger->initdeferred = false;
13847 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13848 break;
13850 fk_trigger->deferrable = false;
13851 fk_trigger->initdeferred = false;
13852 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13853 break;
13855 fk_trigger->deferrable = false;
13856 fk_trigger->initdeferred = false;
13857 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13858 break;
13859 default:
13860 elog(ERROR, "unrecognized FK action type: %d",
13861 (int) fkconstraint->fk_del_action);
13862 break;
13863 }
13864
13865 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13866 constraintOid, indexOid, InvalidOid,
13867 parentDelTrigger, NULL, true, false);
13868 if (deleteTrigOid)
13869 *deleteTrigOid = trigAddress.objectId;
13870
13871 /* Make changes-so-far visible */
13873
13874 /*
13875 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13876 * UPDATE action on the referenced table.
13877 */
13878 fk_trigger = makeNode(CreateTrigStmt);
13879 fk_trigger->replace = false;
13880 fk_trigger->isconstraint = true;
13881 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13882 fk_trigger->relation = NULL;
13883 fk_trigger->args = NIL;
13884 fk_trigger->row = true;
13885 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13886 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13887 fk_trigger->columns = NIL;
13888 fk_trigger->whenClause = NULL;
13889 fk_trigger->transitionRels = NIL;
13890 fk_trigger->constrrel = NULL;
13891
13892 switch (fkconstraint->fk_upd_action)
13893 {
13895 fk_trigger->deferrable = fkconstraint->deferrable;
13896 fk_trigger->initdeferred = fkconstraint->initdeferred;
13897 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13898 break;
13900 fk_trigger->deferrable = false;
13901 fk_trigger->initdeferred = false;
13902 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13903 break;
13905 fk_trigger->deferrable = false;
13906 fk_trigger->initdeferred = false;
13907 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13908 break;
13910 fk_trigger->deferrable = false;
13911 fk_trigger->initdeferred = false;
13912 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13913 break;
13915 fk_trigger->deferrable = false;
13916 fk_trigger->initdeferred = false;
13917 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13918 break;
13919 default:
13920 elog(ERROR, "unrecognized FK action type: %d",
13921 (int) fkconstraint->fk_upd_action);
13922 break;
13923 }
13924
13925 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13926 constraintOid, indexOid, InvalidOid,
13927 parentUpdTrigger, NULL, true, false);
13928 if (updateTrigOid)
13929 *updateTrigOid = trigAddress.objectId;
13930}
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2808

References CreateTrigStmt::args, CreateTrigStmt::columns, CommandCounterIncrement(), CreateTrigStmt::constrrel, CreateTrigger(), Constraint::deferrable, CreateTrigStmt::deferrable, elog, ERROR, CreateTrigStmt::events, Constraint::fk_del_action, Constraint::fk_upd_action, FKCONSTR_ACTION_CASCADE, FKCONSTR_ACTION_NOACTION, FKCONSTR_ACTION_RESTRICT, FKCONSTR_ACTION_SETDEFAULT, FKCONSTR_ACTION_SETNULL, CreateTrigStmt::funcname, Constraint::initdeferred, CreateTrigStmt::initdeferred, InvalidOid, CreateTrigStmt::isconstraint, makeNode, NIL, ObjectAddress::objectId, CreateTrigStmt::relation, CreateTrigStmt::replace, CreateTrigStmt::row, SystemFuncName(), CreateTrigStmt::timing, CreateTrigStmt::transitionRels, CreateTrigStmt::trigname, and CreateTrigStmt::whenClause.

Referenced by addFkRecurseReferenced(), and ATExecAlterConstrEnforceability().

◆ createForeignKeyCheckTriggers()

static void createForeignKeyCheckTriggers ( Oid  myRelOid,
Oid  refRelOid,
Constraint fkconstraint,
Oid  constraintOid,
Oid  indexOid,
Oid  parentInsTrigger,
Oid  parentUpdTrigger,
Oid insertTrigOid,
Oid updateTrigOid 
)
static

Definition at line 13941 of file tablecmds.c.

13946{
13947 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13948 constraintOid, indexOid,
13949 parentInsTrigger, true);
13950 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13951 constraintOid, indexOid,
13952 parentUpdTrigger, false);
13953}
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:13743

References CreateFKCheckTrigger().

Referenced by addFkRecurseReferencing(), and ATExecAlterConstrEnforceability().

◆ CreateInheritance()

static void CreateInheritance ( Relation  child_rel,
Relation  parent_rel,
bool  ispartition 
)
static

Definition at line 17297 of file tablecmds.c.

17298{
17299 Relation catalogRelation;
17300 SysScanDesc scan;
17302 HeapTuple inheritsTuple;
17303 int32 inhseqno;
17304
17305 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17306 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17307
17308 /*
17309 * Check for duplicates in the list of parents, and determine the highest
17310 * inhseqno already present; we'll use the next one for the new parent.
17311 * Also, if proposed child is a partition, it cannot already be
17312 * inheriting.
17313 *
17314 * Note: we do not reject the case where the child already inherits from
17315 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17316 */
17318 Anum_pg_inherits_inhrelid,
17319 BTEqualStrategyNumber, F_OIDEQ,
17321 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17322 true, NULL, 1, &key);
17323
17324 /* inhseqno sequences start at 1 */
17325 inhseqno = 0;
17326 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17327 {
17328 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17329
17330 if (inh->inhparent == RelationGetRelid(parent_rel))
17331 ereport(ERROR,
17332 (errcode(ERRCODE_DUPLICATE_TABLE),
17333 errmsg("relation \"%s\" would be inherited from more than once",
17334 RelationGetRelationName(parent_rel))));
17335
17336 if (inh->inhseqno > inhseqno)
17337 inhseqno = inh->inhseqno;
17338 }
17339 systable_endscan(scan);
17340
17341 /* Match up the columns and bump attinhcount as needed */
17342 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17343
17344 /* Match up the constraints and bump coninhcount as needed */
17345 MergeConstraintsIntoExisting(child_rel, parent_rel);
17346
17347 /*
17348 * OK, it looks valid. Make the catalog entries that show inheritance.
17349 */
17351 RelationGetRelid(parent_rel),
17352 inhseqno + 1,
17353 catalogRelation,
17354 parent_rel->rd_rel->relkind ==
17355 RELKIND_PARTITIONED_TABLE);
17356
17357 /* Now we're done with pg_inherits */
17358 table_close(catalogRelation, RowExclusiveLock);
17359}
FormData_pg_inherits * Form_pg_inherits
Definition: pg_inherits.h:45
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17423
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3555
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17561

References BTEqualStrategyNumber, ereport, errcode(), errmsg(), ERROR, GETSTRUCT(), HeapTupleIsValid, sort-test::key, MergeAttributesIntoExisting(), MergeConstraintsIntoExisting(), ObjectIdGetDatum(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), StoreCatalogInheritance1(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecAddInherit(), and ATExecAttachPartition().

◆ decompile_conbin()

static char * decompile_conbin ( HeapTuple  contup,
TupleDesc  tupdesc 
)
static

Definition at line 17366 of file tablecmds.c.

17367{
17369 bool isnull;
17370 Datum attr;
17371 Datum expr;
17372
17373 con = (Form_pg_constraint) GETSTRUCT(contup);
17374 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17375 if (isnull)
17376 elog(ERROR, "null conbin for constraint %u", con->oid);
17377
17378 expr = DirectFunctionCall2(pg_get_expr, attr,
17379 ObjectIdGetDatum(con->conrelid));
17380 return TextDatumGetCString(expr);
17381}
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:684
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition: ruleutils.c:2675

References DirectFunctionCall2, elog, ERROR, GETSTRUCT(), heap_getattr(), ObjectIdGetDatum(), pg_get_expr(), and TextDatumGetCString.

Referenced by constraints_equivalent().

◆ DefineRelation()

ObjectAddress DefineRelation ( CreateStmt stmt,
char  relkind,
Oid  ownerId,
ObjectAddress typaddress,
const char *  queryString 
)

Definition at line 764 of file tablecmds.c.

766{
767 char relname[NAMEDATALEN];
768 Oid namespaceId;
769 Oid relationId;
770 Oid tablespaceId;
771 Relation rel;
773 List *inheritOids;
774 List *old_constraints;
775 List *old_notnulls;
776 List *rawDefaults;
777 List *cookedDefaults;
778 List *nncols;
779 Datum reloptions;
780 ListCell *listptr;
782 bool partitioned;
783 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
784 Oid ofTypeId;
785 ObjectAddress address;
786 LOCKMODE parentLockmode;
787 Oid accessMethodId = InvalidOid;
788
789 /*
790 * Truncate relname to appropriate length (probably a waste of time, as
791 * parser should have done this already).
792 */
793 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
794
795 /*
796 * Check consistency of arguments
797 */
798 if (stmt->oncommit != ONCOMMIT_NOOP
799 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
801 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
802 errmsg("ON COMMIT can only be used on temporary tables")));
803
804 if (stmt->partspec != NULL)
805 {
806 if (relkind != RELKIND_RELATION)
807 elog(ERROR, "unexpected relkind: %d", (int) relkind);
808
809 relkind = RELKIND_PARTITIONED_TABLE;
810 partitioned = true;
811 }
812 else
813 partitioned = false;
814
815 if (relkind == RELKIND_PARTITIONED_TABLE &&
816 stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
818 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
819 errmsg("partitioned tables cannot be unlogged")));
820
821 /*
822 * Look up the namespace in which we are supposed to create the relation,
823 * check we have permission to create there, lock it against concurrent
824 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
825 * namespace is selected.
826 */
827 namespaceId =
829
830 /*
831 * Security check: disallow creating temp tables from security-restricted
832 * code. This is needed because calling code might not expect untrusted
833 * tables to appear in pg_temp at the front of its search path.
834 */
835 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
838 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
839 errmsg("cannot create temporary table within security-restricted operation")));
840
841 /*
842 * Determine the lockmode to use when scanning parents. A self-exclusive
843 * lock is needed here.
844 *
845 * For regular inheritance, if two backends attempt to add children to the
846 * same parent simultaneously, and that parent has no pre-existing
847 * children, then both will attempt to update the parent's relhassubclass
848 * field, leading to a "tuple concurrently updated" error. Also, this
849 * interlocks against a concurrent ANALYZE on the parent table, which
850 * might otherwise be attempting to clear the parent's relhassubclass
851 * field, if its previous children were recently dropped.
852 *
853 * If the child table is a partition, then we instead grab an exclusive
854 * lock on the parent because its partition descriptor will be changed by
855 * addition of the new partition.
856 */
857 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
859
860 /* Determine the list of OIDs of the parents. */
861 inheritOids = NIL;
862 foreach(listptr, stmt->inhRelations)
863 {
864 RangeVar *rv = (RangeVar *) lfirst(listptr);
865 Oid parentOid;
866
867 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
868
869 /*
870 * Reject duplications in the list of parents.
871 */
872 if (list_member_oid(inheritOids, parentOid))
874 (errcode(ERRCODE_DUPLICATE_TABLE),
875 errmsg("relation \"%s\" would be inherited from more than once",
876 get_rel_name(parentOid))));
877
878 inheritOids = lappend_oid(inheritOids, parentOid);
879 }
880
881 /*
882 * Select tablespace to use: an explicitly indicated one, or (in the case
883 * of a partitioned table) the parent's, if it has one.
884 */
885 if (stmt->tablespacename)
886 {
887 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
888
889 if (partitioned && tablespaceId == MyDatabaseTableSpace)
891 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
892 errmsg("cannot specify default tablespace for partitioned relations")));
893 }
894 else if (stmt->partbound)
895 {
896 Assert(list_length(inheritOids) == 1);
897 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
898 }
899 else
900 tablespaceId = InvalidOid;
901
902 /* still nothing? use the default */
903 if (!OidIsValid(tablespaceId))
904 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
905 partitioned);
906
907 /* Check permissions except when using database's default */
908 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
909 {
910 AclResult aclresult;
911
912 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
913 ACL_CREATE);
914 if (aclresult != ACLCHECK_OK)
916 get_tablespace_name(tablespaceId));
917 }
918
919 /* In all cases disallow placing user relations in pg_global */
920 if (tablespaceId == GLOBALTABLESPACE_OID)
922 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
923 errmsg("only shared relations can be placed in pg_global tablespace")));
924
925 /* Identify user ID that will own the table */
926 if (!OidIsValid(ownerId))
927 ownerId = GetUserId();
928
929 /*
930 * Parse and validate reloptions, if any.
931 */
932 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
933 true, false);
934
935 switch (relkind)
936 {
937 case RELKIND_VIEW:
938 (void) view_reloptions(reloptions, true);
939 break;
940 case RELKIND_PARTITIONED_TABLE:
941 (void) partitioned_table_reloptions(reloptions, true);
942 break;
943 default:
944 (void) heap_reloptions(relkind, reloptions, true);
945 }
946
947 if (stmt->ofTypename)
948 {
949 AclResult aclresult;
950
951 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
952
953 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
954 if (aclresult != ACLCHECK_OK)
955 aclcheck_error_type(aclresult, ofTypeId);
956 }
957 else
958 ofTypeId = InvalidOid;
959
960 /*
961 * Look up inheritance ancestors and generate relation schema, including
962 * inherited attributes. (Note that stmt->tableElts is destructively
963 * modified by MergeAttributes.)
964 */
965 stmt->tableElts =
966 MergeAttributes(stmt->tableElts, inheritOids,
967 stmt->relation->relpersistence,
968 stmt->partbound != NULL,
969 &old_constraints, &old_notnulls);
970
971 /*
972 * Create a tuple descriptor from the relation schema. Note that this
973 * deals with column names, types, and in-descriptor NOT NULL flags, but
974 * not default values, NOT NULL or CHECK constraints; we handle those
975 * below.
976 */
978
979 /*
980 * Find columns with default values and prepare for insertion of the
981 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
982 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
983 * while raw defaults go into a list of RawColumnDefault structs that will
984 * be processed by AddRelationNewConstraints. (We can't deal with raw
985 * expressions until we can do transformExpr.)
986 */
987 rawDefaults = NIL;
988 cookedDefaults = NIL;
989 attnum = 0;
990
991 foreach(listptr, stmt->tableElts)
992 {
993 ColumnDef *colDef = lfirst(listptr);
994
995 attnum++;
996 if (colDef->raw_default != NULL)
997 {
998 RawColumnDefault *rawEnt;
999
1000 Assert(colDef->cooked_default == NULL);
1001
1002 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
1003 rawEnt->attnum = attnum;
1004 rawEnt->raw_default = colDef->raw_default;
1005 rawEnt->generated = colDef->generated;
1006 rawDefaults = lappend(rawDefaults, rawEnt);
1007 }
1008 else if (colDef->cooked_default != NULL)
1009 {
1010 CookedConstraint *cooked;
1011
1012 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
1013 cooked->contype = CONSTR_DEFAULT;
1014 cooked->conoid = InvalidOid; /* until created */
1015 cooked->name = NULL;
1016 cooked->attnum = attnum;
1017 cooked->expr = colDef->cooked_default;
1018 cooked->is_enforced = true;
1019 cooked->skip_validation = false;
1020 cooked->is_local = true; /* not used for defaults */
1021 cooked->inhcount = 0; /* ditto */
1022 cooked->is_no_inherit = false;
1023 cookedDefaults = lappend(cookedDefaults, cooked);
1024 }
1025 }
1026
1027 /*
1028 * For relations with table AM and partitioned tables, select access
1029 * method to use: an explicitly indicated one, or (in the case of a
1030 * partitioned table) the parent's, if it has one.
1031 */
1032 if (stmt->accessMethod != NULL)
1033 {
1034 Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1035 accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1036 }
1037 else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1038 {
1039 if (stmt->partbound)
1040 {
1041 Assert(list_length(inheritOids) == 1);
1042 accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1043 }
1044
1045 if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1046 accessMethodId = get_table_am_oid(default_table_access_method, false);
1047 }
1048
1049 /*
1050 * Create the relation. Inherited defaults and CHECK constraints are
1051 * passed in for immediate handling --- since they don't need parsing,
1052 * they can be stored immediately.
1053 */
1054 relationId = heap_create_with_catalog(relname,
1055 namespaceId,
1056 tablespaceId,
1057 InvalidOid,
1058 InvalidOid,
1059 ofTypeId,
1060 ownerId,
1061 accessMethodId,
1062 descriptor,
1063 list_concat(cookedDefaults,
1064 old_constraints),
1065 relkind,
1066 stmt->relation->relpersistence,
1067 false,
1068 false,
1069 stmt->oncommit,
1070 reloptions,
1071 true,
1073 false,
1074 InvalidOid,
1075 typaddress);
1076
1077 /*
1078 * We must bump the command counter to make the newly-created relation
1079 * tuple visible for opening.
1080 */
1082
1083 /*
1084 * Open the new relation and acquire exclusive lock on it. This isn't
1085 * really necessary for locking out other backends (since they can't see
1086 * the new rel anyway until we commit), but it keeps the lock manager from
1087 * complaining about deadlock risks.
1088 */
1089 rel = relation_open(relationId, AccessExclusiveLock);
1090
1091 /*
1092 * Now add any newly specified column default and generation expressions
1093 * to the new relation. These are passed to us in the form of raw
1094 * parsetrees; we need to transform them to executable expression trees
1095 * before they can be added. The most convenient way to do that is to
1096 * apply the parser's transformExpr routine, but transformExpr doesn't
1097 * work unless we have a pre-existing relation. So, the transformation has
1098 * to be postponed to this final step of CREATE TABLE.
1099 *
1100 * This needs to be before processing the partitioning clauses because
1101 * those could refer to generated columns.
1102 */
1103 if (rawDefaults)
1104 AddRelationNewConstraints(rel, rawDefaults, NIL,
1105 true, true, false, queryString);
1106
1107 /*
1108 * Make column generation expressions visible for use by partitioning.
1109 */
1111
1112 /* Process and store partition bound, if any. */
1113 if (stmt->partbound)
1114 {
1115 PartitionBoundSpec *bound;
1116 ParseState *pstate;
1117 Oid parentId = linitial_oid(inheritOids),
1118 defaultPartOid;
1119 Relation parent,
1120 defaultRel = NULL;
1121 ParseNamespaceItem *nsitem;
1122
1123 /* Already have strong enough lock on the parent */
1124 parent = table_open(parentId, NoLock);
1125
1126 /*
1127 * We are going to try to validate the partition bound specification
1128 * against the partition key of parentRel, so it better have one.
1129 */
1130 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1131 ereport(ERROR,
1132 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1133 errmsg("\"%s\" is not partitioned",
1134 RelationGetRelationName(parent))));
1135
1136 /*
1137 * The partition constraint of the default partition depends on the
1138 * partition bounds of every other partition. It is possible that
1139 * another backend might be about to execute a query on the default
1140 * partition table, and that the query relies on previously cached
1141 * default partition constraints. We must therefore take a table lock
1142 * strong enough to prevent all queries on the default partition from
1143 * proceeding until we commit and send out a shared-cache-inval notice
1144 * that will make them update their index lists.
1145 *
1146 * Order of locking: The relation being added won't be visible to
1147 * other backends until it is committed, hence here in
1148 * DefineRelation() the order of locking the default partition and the
1149 * relation being added does not matter. But at all other places we
1150 * need to lock the default relation before we lock the relation being
1151 * added or removed i.e. we should take the lock in same order at all
1152 * the places such that lock parent, lock default partition and then
1153 * lock the partition so as to avoid a deadlock.
1154 */
1155 defaultPartOid =
1157 true));
1158 if (OidIsValid(defaultPartOid))
1159 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1160
1161 /* Transform the bound values */
1162 pstate = make_parsestate(NULL);
1163 pstate->p_sourcetext = queryString;
1164
1165 /*
1166 * Add an nsitem containing this relation, so that transformExpr
1167 * called on partition bound expressions is able to report errors
1168 * using a proper context.
1169 */
1170 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1171 NULL, false, false);
1172 addNSItemToQuery(pstate, nsitem, false, true, true);
1173
1174 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1175
1176 /*
1177 * Check first that the new partition's bound is valid and does not
1178 * overlap with any of existing partitions of the parent.
1179 */
1180 check_new_partition_bound(relname, parent, bound, pstate);
1181
1182 /*
1183 * If the default partition exists, its partition constraints will
1184 * change after the addition of this new partition such that it won't
1185 * allow any row that qualifies for this new partition. So, check that
1186 * the existing data in the default partition satisfies the constraint
1187 * as it will exist after adding this partition.
1188 */
1189 if (OidIsValid(defaultPartOid))
1190 {
1191 check_default_partition_contents(parent, defaultRel, bound);
1192 /* Keep the lock until commit. */
1193 table_close(defaultRel, NoLock);
1194 }
1195
1196 /* Update the pg_class entry. */
1197 StorePartitionBound(rel, parent, bound);
1198
1199 table_close(parent, NoLock);
1200 }
1201
1202 /* Store inheritance information for new rel. */
1203 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1204
1205 /*
1206 * Process the partitioning specification (if any) and store the partition
1207 * key information into the catalog.
1208 */
1209 if (partitioned)
1210 {
1211 ParseState *pstate;
1212 int partnatts;
1213 AttrNumber partattrs[PARTITION_MAX_KEYS];
1214 Oid partopclass[PARTITION_MAX_KEYS];
1215 Oid partcollation[PARTITION_MAX_KEYS];
1216 List *partexprs = NIL;
1217
1218 pstate = make_parsestate(NULL);
1219 pstate->p_sourcetext = queryString;
1220
1221 partnatts = list_length(stmt->partspec->partParams);
1222
1223 /* Protect fixed-size arrays here and in executor */
1224 if (partnatts > PARTITION_MAX_KEYS)
1225 ereport(ERROR,
1226 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1227 errmsg("cannot partition using more than %d columns",
1229
1230 /*
1231 * We need to transform the raw parsetrees corresponding to partition
1232 * expressions into executable expression trees. Like column defaults
1233 * and CHECK constraints, we could not have done the transformation
1234 * earlier.
1235 */
1236 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1237
1238 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1239 partattrs, &partexprs, partopclass,
1240 partcollation, stmt->partspec->strategy);
1241
1242 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1243 partexprs,
1244 partopclass, partcollation);
1245
1246 /* make it all visible */
1248 }
1249
1250 /*
1251 * If we're creating a partition, create now all the indexes, triggers,
1252 * FKs defined in the parent.
1253 *
1254 * We can't do it earlier, because DefineIndex wants to know the partition
1255 * key which we just stored.
1256 */
1257 if (stmt->partbound)
1258 {
1259 Oid parentId = linitial_oid(inheritOids);
1260 Relation parent;
1261 List *idxlist;
1262 ListCell *cell;
1263
1264 /* Already have strong enough lock on the parent */
1265 parent = table_open(parentId, NoLock);
1266 idxlist = RelationGetIndexList(parent);
1267
1268 /*
1269 * For each index in the parent table, create one in the partition
1270 */
1271 foreach(cell, idxlist)
1272 {
1274 AttrMap *attmap;
1275 IndexStmt *idxstmt;
1276 Oid constraintOid;
1277
1278 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1279 {
1280 if (idxRel->rd_index->indisunique)
1281 ereport(ERROR,
1282 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1283 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1284 RelationGetRelationName(parent)),
1285 errdetail("Table \"%s\" contains indexes that are unique.",
1286 RelationGetRelationName(parent))));
1287 else
1288 {
1290 continue;
1291 }
1292 }
1293
1295 RelationGetDescr(parent),
1296 false);
1297 idxstmt =
1298 generateClonedIndexStmt(NULL, idxRel,
1299 attmap, &constraintOid);
1301 idxstmt,
1302 InvalidOid,
1303 RelationGetRelid(idxRel),
1304 constraintOid,
1305 -1,
1306 false, false, false, false, false);
1307
1309 }
1310
1311 list_free(idxlist);
1312
1313 /*
1314 * If there are any row-level triggers, clone them to the new
1315 * partition.
1316 */
1317 if (parent->trigdesc != NULL)
1318 CloneRowTriggersToPartition(parent, rel);
1319
1320 /*
1321 * And foreign keys too. Note that because we're freshly creating the
1322 * table, there is no need to verify these new constraints.
1323 */
1324 CloneForeignKeyConstraints(NULL, parent, rel);
1325
1326 table_close(parent, NoLock);
1327 }
1328
1329 /*
1330 * Now add any newly specified CHECK constraints to the new relation. Same
1331 * as for defaults above, but these need to come after partitioning is set
1332 * up.
1333 */
1334 if (stmt->constraints)
1335 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1336 true, true, false, queryString);
1337
1338 /*
1339 * Finally, merge the not-null constraints that are declared directly with
1340 * those that come from parent relations (making sure to count inheritance
1341 * appropriately for each), create them, and set the attnotnull flag on
1342 * columns that don't yet have it.
1343 */
1344 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1345 old_notnulls);
1346 foreach_int(attrnum, nncols)
1347 set_attnotnull(NULL, rel, attrnum, true, false);
1348
1349 ObjectAddressSet(address, RelationRelationId, relationId);
1350
1351 /*
1352 * Clean up. We keep lock on new relation (although it shouldn't be
1353 * visible to anyone else anyway, until commit).
1354 */
1355 relation_close(rel, NoLock);
1356
1357 return address;
1358}
Oid GetDefaultTablespace(char relpersistence, bool partitioned)
Definition: tablespace.c:1143
void StorePartitionKey(Relation rel, char strategy, int16 partnatts, AttrNumber *partattrs, List *partexprs, Oid *partopclass, Oid *partcollation)
Definition: heap.c:3800
Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid reltypeid, Oid reloftypeid, Oid ownerid, Oid accessmtd, TupleDesc tupdesc, List *cooked_constraints, char relkind, char relpersistence, bool shared_relation, bool mapped_relation, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, bool allow_system_table_mods, bool is_internal, Oid relrewrite, ObjectAddress *typaddress)
Definition: heap.c:1112
List * AddRelationNotNullConstraints(Relation rel, List *constraints, List *old_notnulls)
Definition: heap.c:2884
Oid get_rel_relam(Oid relid)
Definition: lsyscache.c:2240
Oid get_rel_tablespace(Oid relid)
Definition: lsyscache.c:2194
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:690
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, int lockmode, Alias *alias, bool inh, bool inFromCl)
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
Definition: parse_type.c:291
PartitionBoundSpec * transformPartitionBound(ParseState *pstate, Relation parent, PartitionBoundSpec *spec)
@ CONSTR_DEFAULT
Definition: parsenodes.h:2791
void check_default_partition_contents(Relation parent, Relation default_rel, PartitionBoundSpec *new_spec)
Definition: partbounds.c:3251
NameData relname
Definition: pg_class.h:38
#define PARTITION_MAX_KEYS
#define linitial_oid(l)
Definition: pg_list.h:180
@ ONCOMMIT_NOOP
Definition: primnodes.h:58
bool is_enforced
Definition: heap.h:43
bool is_no_inherit
Definition: heap.h:47
int16 inhcount
Definition: heap.h:46
bool is_local
Definition: heap.h:45
static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, PartitionStrategy strategy)
Definition: tablecmds.c:19708
static void StoreCatalogInheritance(Oid relationId, List *supers, bool child_is_partition)
Definition: tablecmds.c:3511
static PartitionSpec * transformPartitionSpec(Relation rel, PartitionSpec *partspec)
Definition: tablecmds.c:19650
static List * MergeAttributes(List *columns, const List *supers, char relpersistence, bool is_partition, List **supconstr, List **supnotnulls)
Definition: tablecmds.c:2536

References AccessExclusiveLock, AccessShareLock, ACL_CREATE, ACL_USAGE, aclcheck_error(), aclcheck_error_type(), ACLCHECK_OK, addNSItemToQuery(), addRangeTableEntryForRelation(), AddRelationNewConstraints(), AddRelationNotNullConstraints(), allowSystemTableMods, Assert(), RawColumnDefault::attnum, CookedConstraint::attnum, attnum, build_attrmap_by_name(), BuildDescForRelation(), check_default_partition_contents(), check_new_partition_bound(), CloneForeignKeyConstraints(), CloneRowTriggersToPartition(), CommandCounterIncrement(), ComputePartitionAttrs(), CookedConstraint::conoid, CONSTR_DEFAULT, CookedConstraint::contype, ColumnDef::cooked_default, default_table_access_method, DefineIndex(), elog, ereport, errcode(), errdetail(), errmsg(), ERROR, CookedConstraint::expr, foreach_int, generateClonedIndexStmt(), RawColumnDefault::generated, ColumnDef::generated, get_default_oid_from_partdesc(), get_rel_name(), get_rel_relam(), get_rel_tablespace(), get_table_am_oid(), get_tablespace_name(), get_tablespace_oid(), GetDefaultTablespace(), GetUserId(), heap_create_with_catalog(), HEAP_RELOPT_NAMESPACES, heap_reloptions(), index_close(), index_open(), CookedConstraint::inhcount, InSecurityRestrictedOperation(), InvalidOid, CookedConstraint::is_enforced, CookedConstraint::is_local, CookedConstraint::is_no_inherit, lappend(), lappend_oid(), lfirst, lfirst_oid, linitial_oid, list_concat(), list_free(), list_length(), list_member_oid(), make_parsestate(), MergeAttributes(), MyDatabaseTableSpace, CookedConstraint::name, NAMEDATALEN, NIL, NoLock, object_aclcheck(), OBJECT_TABLESPACE, ObjectAddressSet, OidIsValid, ONCOMMIT_NOOP, ParseState::p_sourcetext, palloc(), PARTITION_MAX_KEYS, partitioned_table_reloptions(), RangeVarGetAndCheckCreationNamespace(), RangeVarGetRelid, RawColumnDefault::raw_default, ColumnDef::raw_default, RelationData::rd_index, RelationData::rd_rel, relation_close(), relation_open(), RelationGetDescr, RelationGetIndexList(), RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, relname, set_attnotnull(), ShareUpdateExclusiveLock, CookedConstraint::skip_validation, stmt, StoreCatalogInheritance(), StorePartitionBound(), StorePartitionKey(), strlcpy(), table_close(), table_open(), transformPartitionBound(), transformPartitionSpec(), transformRelOptions(), RelationData::trigdesc, typenameTypeId(), and view_reloptions().

Referenced by create_ctas_internal(), DefineCompositeType(), DefineSequence(), DefineVirtualRelation(), and ProcessUtilitySlow().

◆ DetachAddConstraintIfNeeded()

static void DetachAddConstraintIfNeeded ( List **  wqueue,
Relation  partRel 
)
static

Definition at line 21365 of file tablecmds.c.

21366{
21367 List *constraintExpr;
21368
21369 constraintExpr = RelationGetPartitionQual(partRel);
21370 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
21371
21372 /*
21373 * Avoid adding a new constraint if the needed constraint is implied by an
21374 * existing constraint
21375 */
21376 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
21377 {
21378 AlteredTableInfo *tab;
21379 Constraint *n;
21380
21381 tab = ATGetQueueEntry(wqueue, partRel);
21382
21383 /* Add constraint on partition, equivalent to the partition constraint */
21384 n = makeNode(Constraint);
21385 n->contype = CONSTR_CHECK;
21386 n->conname = NULL;
21387 n->location = -1;
21388 n->is_no_inherit = false;
21389 n->raw_expr = NULL;
21390 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
21391 n->is_enforced = true;
21392 n->initially_valid = true;
21393 n->skip_validation = true;
21394 /* It's a re-add, since it nominally already exists */
21395 ATAddCheckNNConstraint(wqueue, tab, partRel, n,
21396 true, false, true, ShareUpdateExclusiveLock);
21397 }
21398}
char * nodeToString(const void *obj)
Definition: outfuncs.c:797
char * cooked_expr
Definition: parsenodes.h:2832
Node * raw_expr
Definition: parsenodes.h:2830
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:19972

References ATAddCheckNNConstraint(), ATGetQueueEntry(), Constraint::conname, CONSTR_CHECK, Constraint::contype, Constraint::cooked_expr, eval_const_expressions(), Constraint::initially_valid, Constraint::is_enforced, Constraint::is_no_inherit, Constraint::location, make_ands_explicit(), makeNode, nodeToString(), PartConstraintImpliedByRelConstraint(), Constraint::raw_expr, RelationGetPartitionQual(), ShareUpdateExclusiveLock, and Constraint::skip_validation.

Referenced by ATExecDetachPartition().

◆ DetachPartitionFinalize()

static void DetachPartitionFinalize ( Relation  rel,
Relation  partRel,
bool  concurrent,
Oid  defaultPartOid 
)
static

Definition at line 20996 of file tablecmds.c.

20998{
20999 Relation classRel;
21000 List *fks;
21001 ListCell *cell;
21002 List *indexes;
21003 Datum new_val[Natts_pg_class];
21004 bool new_null[Natts_pg_class],
21005 new_repl[Natts_pg_class];
21006 HeapTuple tuple,
21007 newtuple;
21008 Relation trigrel = NULL;
21009 List *fkoids = NIL;
21010
21011 if (concurrent)
21012 {
21013 /*
21014 * We can remove the pg_inherits row now. (In the non-concurrent case,
21015 * this was already done).
21016 */
21017 RemoveInheritance(partRel, rel, true);
21018 }
21019
21020 /* Drop any triggers that were cloned on creation/attach. */
21022
21023 /*
21024 * Detach any foreign keys that are inherited. This includes creating
21025 * additional action triggers.
21026 */
21027 fks = copyObject(RelationGetFKeyList(partRel));
21028 if (fks != NIL)
21029 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21030
21031 /*
21032 * It's possible that the partition being detached has a foreign key that
21033 * references a partitioned table. When that happens, there are multiple
21034 * pg_constraint rows for the partition: one points to the partitioned
21035 * table itself, while the others point to each of its partitions. Only
21036 * the topmost one is to be considered here; the child constraints must be
21037 * left alone, because conceptually those aren't coming from our parent
21038 * partitioned table, but from this partition itself.
21039 *
21040 * We implement this by collecting all the constraint OIDs in a first scan
21041 * of the FK array, and skipping in the loop below those constraints whose
21042 * parents are listed here.
21043 */
21045 fkoids = lappend_oid(fkoids, fk->conoid);
21046
21047 foreach(cell, fks)
21048 {
21049 ForeignKeyCacheInfo *fk = lfirst(cell);
21050 HeapTuple contup;
21051 Form_pg_constraint conform;
21052
21053 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21054 if (!HeapTupleIsValid(contup))
21055 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21056 conform = (Form_pg_constraint) GETSTRUCT(contup);
21057
21058 /*
21059 * Consider only inherited foreign keys, and only if their parents
21060 * aren't in the list.
21061 */
21062 if (conform->contype != CONSTRAINT_FOREIGN ||
21063 !OidIsValid(conform->conparentid) ||
21064 list_member_oid(fkoids, conform->conparentid))
21065 {
21066 ReleaseSysCache(contup);
21067 continue;
21068 }
21069
21070 /*
21071 * The constraint on this table must be marked no longer a child of
21072 * the parent's constraint, as do its check triggers.
21073 */
21075
21076 /*
21077 * Also, look up the partition's "check" triggers corresponding to the
21078 * ENFORCED constraint being detached and detach them from the parent
21079 * triggers. NOT ENFORCED constraints do not have these triggers;
21080 * therefore, this step is not needed.
21081 */
21082 if (fk->conenforced)
21083 {
21084 Oid insertTriggerOid,
21085 updateTriggerOid;
21086
21088 fk->conoid, fk->confrelid, fk->conrelid,
21089 &insertTriggerOid, &updateTriggerOid);
21090 Assert(OidIsValid(insertTriggerOid));
21091 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21092 RelationGetRelid(partRel));
21093 Assert(OidIsValid(updateTriggerOid));
21094 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21095 RelationGetRelid(partRel));
21096 }
21097
21098 /*
21099 * Lastly, create the action triggers on the referenced table, using
21100 * addFkRecurseReferenced, which requires some elaborate setup (so put
21101 * it in a separate block). While at it, if the table is partitioned,
21102 * that function will recurse to create the pg_constraint rows and
21103 * action triggers for each partition.
21104 *
21105 * Note there's no need to do addFkConstraint() here, because the
21106 * pg_constraint row already exists.
21107 */
21108 {
21109 Constraint *fkconstraint;
21110 int numfks;
21111 AttrNumber conkey[INDEX_MAX_KEYS];
21112 AttrNumber confkey[INDEX_MAX_KEYS];
21113 Oid conpfeqop[INDEX_MAX_KEYS];
21114 Oid conppeqop[INDEX_MAX_KEYS];
21115 Oid conffeqop[INDEX_MAX_KEYS];
21116 int numfkdelsetcols;
21117 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21118 Relation refdRel;
21119
21121 &numfks,
21122 conkey,
21123 confkey,
21124 conpfeqop,
21125 conppeqop,
21126 conffeqop,
21127 &numfkdelsetcols,
21128 confdelsetcols);
21129
21130 /* Create a synthetic node we'll use throughout */
21131 fkconstraint = makeNode(Constraint);
21132 fkconstraint->contype = CONSTRAINT_FOREIGN;
21133 fkconstraint->conname = pstrdup(NameStr(conform->conname));
21134 fkconstraint->deferrable = conform->condeferrable;
21135 fkconstraint->initdeferred = conform->condeferred;
21136 fkconstraint->is_enforced = conform->conenforced;
21137 fkconstraint->skip_validation = true;
21138 fkconstraint->initially_valid = conform->convalidated;
21139 /* a few irrelevant fields omitted here */
21140 fkconstraint->pktable = NULL;
21141 fkconstraint->fk_attrs = NIL;
21142 fkconstraint->pk_attrs = NIL;
21143 fkconstraint->fk_matchtype = conform->confmatchtype;
21144 fkconstraint->fk_upd_action = conform->confupdtype;
21145 fkconstraint->fk_del_action = conform->confdeltype;
21146 fkconstraint->fk_del_set_cols = NIL;
21147 fkconstraint->old_conpfeqop = NIL;
21148 fkconstraint->old_pktable_oid = InvalidOid;
21149 fkconstraint->location = -1;
21150
21151 /* set up colnames, used to generate the constraint name */
21152 for (int i = 0; i < numfks; i++)
21153 {
21155
21156 att = TupleDescAttr(RelationGetDescr(partRel),
21157 conkey[i] - 1);
21158
21159 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21160 makeString(NameStr(att->attname)));
21161 }
21162
21164
21165 addFkRecurseReferenced(fkconstraint, partRel,
21166 refdRel,
21167 conform->conindid,
21168 fk->conoid,
21169 numfks,
21170 confkey,
21171 conkey,
21172 conpfeqop,
21173 conppeqop,
21174 conffeqop,
21175 numfkdelsetcols,
21176 confdelsetcols,
21177 true,
21179 conform->conperiod);
21180 table_close(refdRel, NoLock); /* keep lock till end of xact */
21181 }
21182
21183 ReleaseSysCache(contup);
21184 }
21185 list_free_deep(fks);
21186 if (trigrel)
21187 table_close(trigrel, RowExclusiveLock);
21188
21189 /*
21190 * Any sub-constraints that are in the referenced-side of a larger
21191 * constraint have to be removed. This partition is no longer part of the
21192 * key space of the constraint.
21193 */
21194 foreach(cell, GetParentedForeignKeyRefs(partRel))
21195 {
21196 Oid constrOid = lfirst_oid(cell);
21197 ObjectAddress constraint;
21198
21200 deleteDependencyRecordsForClass(ConstraintRelationId,
21201 constrOid,
21202 ConstraintRelationId,
21205
21206 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21207 performDeletion(&constraint, DROP_RESTRICT, 0);
21208 }
21209
21210 /* Now we can detach indexes */
21211 indexes = RelationGetIndexList(partRel);
21212 foreach(cell, indexes)
21213 {
21214 Oid idxid = lfirst_oid(cell);
21215 Oid parentidx;
21216 Relation idx;
21217 Oid constrOid;
21218 Oid parentConstrOid;
21219
21220 if (!has_superclass(idxid))
21221 continue;
21222
21223 parentidx = get_partition_parent(idxid, false);
21224 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21225
21228
21229 /*
21230 * If there's a constraint associated with the index, detach it too.
21231 * Careful: it is possible for a constraint index in a partition to be
21232 * the child of a non-constraint index, so verify whether the parent
21233 * index does actually have a constraint.
21234 */
21236 idxid);
21238 parentidx);
21239 if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21241
21243 }
21244
21245 /* Update pg_class tuple */
21246 classRel = table_open(RelationRelationId, RowExclusiveLock);
21247 tuple = SearchSysCacheCopy1(RELOID,
21249 if (!HeapTupleIsValid(tuple))
21250 elog(ERROR, "cache lookup failed for relation %u",
21251 RelationGetRelid(partRel));
21252 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21253
21254 /* Clear relpartbound and reset relispartition */
21255 memset(new_val, 0, sizeof(new_val));
21256 memset(new_null, false, sizeof(new_null));
21257 memset(new_repl, false, sizeof(new_repl));
21258 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21259 new_null[Anum_pg_class_relpartbound - 1] = true;
21260 new_repl[Anum_pg_class_relpartbound - 1] = true;
21261 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21262 new_val, new_null, new_repl);
21263
21264 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21265 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21266 heap_freetuple(newtuple);
21267 table_close(classRel, RowExclusiveLock);
21268
21269 /*
21270 * Drop identity property from all identity columns of partition.
21271 */
21272 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21273 {
21274 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21275
21276 if (!attr->attisdropped && attr->attidentity)
21277 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21278 AccessExclusiveLock, true, true);
21279 }
21280
21281 if (OidIsValid(defaultPartOid))
21282 {
21283 /*
21284 * If the relation being detached is the default partition itself,
21285 * remove it from the parent's pg_partitioned_table entry.
21286 *
21287 * If not, we must invalidate default partition's relcache entry, as
21288 * in StorePartitionBound: its partition constraint depends on every
21289 * other partition's partition constraint.
21290 */
21291 if (RelationGetRelid(partRel) == defaultPartOid)
21293 else
21294 CacheInvalidateRelcacheByRelid(defaultPartOid);
21295 }
21296
21297 /*
21298 * Invalidate the parent's relcache so that the partition is no longer
21299 * included in its partition descriptor.
21300 */
21302
21303 /*
21304 * If the partition we just detached is partitioned itself, invalidate
21305 * relcache for all descendent partitions too to ensure that their
21306 * rd_partcheck expression trees are rebuilt; must lock partitions before
21307 * doing so, using the same lockmode as what partRel has been locked with
21308 * by the caller.
21309 */
21310 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21311 {
21312 List *children;
21313
21314 children = find_all_inheritors(RelationGetRelid(partRel),
21315 AccessExclusiveLock, NULL);
21316 foreach(cell, children)
21317 {
21319 }
21320 }
21321}
void list_free_deep(List *list)
Definition: list.c:1560
void update_default_partition_oid(Oid parentId, Oid defaultPartId)
Definition: partition.c:340
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:377
#define RelationGetNumberOfAttributes(relation)
Definition: rel.h:522
bool conenforced
Definition: rel.h:288
static void DropClonedTriggersFromPartition(Oid partitionId)
Definition: tablecmds.c:21407

References AccessExclusiveLock, addFkRecurseReferenced(), Assert(), ATExecDropIdentity(), CacheInvalidateRelcache(), CacheInvalidateRelcacheByRelid(), CatalogTupleUpdate(), CommandCounterIncrement(), ForeignKeyCacheInfo::conenforced, ForeignKeyCacheInfo::confrelid, Constraint::conname, ForeignKeyCacheInfo::conoid, ForeignKeyCacheInfo::conrelid, ConstraintSetParentConstraint(), Constraint::contype, copyObject, DeconstructFkConstraintRow(), Constraint::deferrable, deleteDependencyRecordsForClass(), DEPENDENCY_INTERNAL, DROP_RESTRICT, DropClonedTriggersFromPartition(), elog, ERROR, find_all_inheritors(), Constraint::fk_attrs, Constraint::fk_del_action, Constraint::fk_del_set_cols, Constraint::fk_matchtype, Constraint::fk_upd_action, foreach_node, get_partition_parent(), get_relation_idx_constraint_oid(), GetForeignKeyCheckTriggers(), GetParentedForeignKeyRefs(), GETSTRUCT(), has_superclass(), heap_freetuple(), heap_modify_tuple(), HeapTupleIsValid, i, idx(), index_close(), INDEX_MAX_KEYS, index_open(), IndexGetRelation(), IndexSetParentIndex(), Constraint::initdeferred, Constraint::initially_valid, InvalidOid, Constraint::is_enforced, lappend(), lappend_oid(), lfirst, lfirst_oid, list_free_deep(), list_member_oid(), Constraint::location, makeNode, makeString(), NameStr, NIL, NoLock, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, Constraint::old_conpfeqop, Constraint::old_pktable_oid, performDeletion(), Constraint::pk_attrs, Constraint::pktable, pstrdup(), RelationData::rd_att, RelationData::rd_rel, RelationGetDescr, RelationGetFKeyList(), RelationGetIndexList(), RelationGetNumberOfAttributes, RelationGetRelid, ReleaseSysCache(), RemoveInheritance(), RowExclusiveLock, SearchSysCache1(), SearchSysCacheCopy1, ShareRowExclusiveLock, Constraint::skip_validation, HeapTupleData::t_self, table_close(), table_open(), TriggerSetParentTrigger(), TupleDescAttr(), and update_default_partition_oid().

Referenced by ATExecDetachPartition(), and ATExecDetachPartitionFinalize().

◆ drop_parent_dependency()

static void drop_parent_dependency ( Oid  relid,
Oid  refclassid,
Oid  refobjid,
DependencyType  deptype 
)
static

Definition at line 18087 of file tablecmds.c.

18089{
18090 Relation catalogRelation;
18091 SysScanDesc scan;
18092 ScanKeyData key[3];
18093 HeapTuple depTuple;
18094
18095 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18096
18097 ScanKeyInit(&key[0],
18098 Anum_pg_depend_classid,
18099 BTEqualStrategyNumber, F_OIDEQ,
18100 ObjectIdGetDatum(RelationRelationId));
18101 ScanKeyInit(&key[1],
18102 Anum_pg_depend_objid,
18103 BTEqualStrategyNumber, F_OIDEQ,
18104 ObjectIdGetDatum(relid));
18105 ScanKeyInit(&key[2],
18106 Anum_pg_depend_objsubid,
18107 BTEqualStrategyNumber, F_INT4EQ,
18108 Int32GetDatum(0));
18109
18110 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18111 NULL, 3, key);
18112
18113 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18114 {
18115 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18116
18117 if (dep->refclassid == refclassid &&
18118 dep->refobjid == refobjid &&
18119 dep->refobjsubid == 0 &&
18120 dep->deptype == deptype)
18121 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18122 }
18123
18124 systable_endscan(scan);
18125 table_close(catalogRelation, RowExclusiveLock);
18126}

References BTEqualStrategyNumber, CatalogTupleDelete(), GETSTRUCT(), HeapTupleIsValid, Int32GetDatum(), sort-test::key, ObjectIdGetDatum(), RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecAddOf(), ATExecDropOf(), and RemoveInheritance().

◆ DropClonedTriggersFromPartition()

static void DropClonedTriggersFromPartition ( Oid  partitionId)
static

Definition at line 21407 of file tablecmds.c.

21408{
21409 ScanKeyData skey;
21410 SysScanDesc scan;
21411 HeapTuple trigtup;
21412 Relation tgrel;
21413 ObjectAddresses *objects;
21414
21415 objects = new_object_addresses();
21416
21417 /*
21418 * Scan pg_trigger to search for all triggers on this rel.
21419 */
21420 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21421 F_OIDEQ, ObjectIdGetDatum(partitionId));
21422 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21423 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21424 true, NULL, 1, &skey);
21425 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21426 {
21427 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21428 ObjectAddress trig;
21429
21430 /* Ignore triggers that weren't cloned */
21431 if (!OidIsValid(pg_trigger->tgparentid))
21432 continue;
21433
21434 /*
21435 * Ignore internal triggers that are implementation objects of foreign
21436 * keys, because these will be detached when the foreign keys
21437 * themselves are.
21438 */
21439 if (OidIsValid(pg_trigger->tgconstrrelid))
21440 continue;
21441
21442 /*
21443 * This is ugly, but necessary: remove the dependency markings on the
21444 * trigger so that it can be removed.
21445 */
21446 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21447 TriggerRelationId,
21449 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21450 RelationRelationId,
21452
21453 /* remember this trigger to remove it below */
21454 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21455 add_exact_object_address(&trig, objects);
21456 }
21457
21458 /* make the dependency removal visible to the deletion below */
21461
21462 /* done */
21463 free_object_addresses(objects);
21464 systable_endscan(scan);
21466}

References add_exact_object_address(), BTEqualStrategyNumber, CommandCounterIncrement(), deleteDependencyRecordsForClass(), DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, DROP_RESTRICT, free_object_addresses(), GETSTRUCT(), HeapTupleIsValid, new_object_addresses(), ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, PERFORM_DELETION_INTERNAL, performMultipleDeletions(), RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by DetachPartitionFinalize().

◆ dropconstraint_internal()

static ObjectAddress dropconstraint_internal ( Relation  rel,
HeapTuple  constraintTup,
DropBehavior  behavior,
bool  recurse,
bool  recursing,
bool  missing_ok,
LOCKMODE  lockmode 
)
static

Definition at line 14026 of file tablecmds.c.

14029{
14030 Relation conrel;
14032 ObjectAddress conobj;
14033 List *children;
14034 bool is_no_inherit_constraint = false;
14035 char *constrName;
14036 char *colname = NULL;
14037
14038 /* Guard against stack overflow due to overly deep inheritance tree. */
14040
14041 /* At top level, permission check was done in ATPrepCmd, else do it */
14042 if (recursing)
14045
14046 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14047
14048 con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14049 constrName = NameStr(con->conname);
14050
14051 /* Don't allow drop of inherited constraints */
14052 if (con->coninhcount > 0 && !recursing)
14053 ereport(ERROR,
14054 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14055 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14056 constrName, RelationGetRelationName(rel))));
14057
14058 /*
14059 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14060 *
14061 * While doing that, we're in a good position to disallow dropping a not-
14062 * null constraint underneath a primary key, a replica identity index, or
14063 * a generated identity column.
14064 */
14065 if (con->contype == CONSTRAINT_NOTNULL)
14066 {
14067 Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14068 AttrNumber attnum = extractNotNullColumn(constraintTup);
14069 Bitmapset *pkattrs;
14070 Bitmapset *irattrs;
14071 HeapTuple atttup;
14072 Form_pg_attribute attForm;
14073
14074 /* save column name for recursion step */
14075 colname = get_attname(RelationGetRelid(rel), attnum, false);
14076
14077 /*
14078 * Disallow if it's in the primary key. For partitioned tables we
14079 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14080 * return NULL if the primary key is invalid; but we still need to
14081 * protect not-null constraints under such a constraint, so check the
14082 * slow way.
14083 */
14085
14086 if (pkattrs == NULL &&
14087 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14088 {
14089 Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14090
14091 if (OidIsValid(pkindex))
14092 {
14093 Relation pk = relation_open(pkindex, AccessShareLock);
14094
14095 pkattrs = NULL;
14096 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14097 pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14098
14100 }
14101 }
14102
14103 if (pkattrs &&
14105 ereport(ERROR,
14106 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14107 errmsg("column \"%s\" is in a primary key",
14108 get_attname(RelationGetRelid(rel), attnum, false)));
14109
14110 /* Disallow if it's in the replica identity */
14113 ereport(ERROR,
14114 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14115 errmsg("column \"%s\" is in index used as replica identity",
14116 get_attname(RelationGetRelid(rel), attnum, false)));
14117
14118 /* Disallow if it's a GENERATED AS IDENTITY column */
14120 if (!HeapTupleIsValid(atttup))
14121 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14122 attnum, RelationGetRelid(rel));
14123 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14124 if (attForm->attidentity != '\0')
14125 ereport(ERROR,
14126 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14127 errmsg("column \"%s\" of relation \"%s\" is an identity column",
14129 false),
14131
14132 /* All good -- reset attnotnull if needed */
14133 if (attForm->attnotnull)
14134 {
14135 attForm->attnotnull = false;
14136 CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14137 }
14138
14140 }
14141
14142 is_no_inherit_constraint = con->connoinherit;
14143
14144 /*
14145 * If it's a foreign-key constraint, we'd better lock the referenced table
14146 * and check that that's not in use, just as we've already done for the
14147 * constrained table (else we might, eg, be dropping a trigger that has
14148 * unfired events). But we can/must skip that in the self-referential
14149 * case.
14150 */
14151 if (con->contype == CONSTRAINT_FOREIGN &&
14152 con->confrelid != RelationGetRelid(rel))
14153 {
14154 Relation frel;
14155
14156 /* Must match lock taken by RemoveTriggerById: */
14157 frel = table_open(con->confrelid, AccessExclusiveLock);
14159 table_close(frel, NoLock);
14160 }
14161
14162 /*
14163 * Perform the actual constraint deletion
14164 */
14165 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14166 performDeletion(&conobj, behavior, 0);
14167
14168 /*
14169 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14170 * are dropped via the dependency mechanism, so we're done here.
14171 */
14172 if (con->contype != CONSTRAINT_CHECK &&
14173 con->contype != CONSTRAINT_NOTNULL &&
14174 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14175 {
14177 return conobj;
14178 }
14179
14180 /*
14181 * Propagate to children as appropriate. Unlike most other ALTER
14182 * routines, we have to do this one level of recursion at a time; we can't
14183 * use find_all_inheritors to do it in one pass.
14184 */
14185 if (!is_no_inherit_constraint)
14186 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14187 else
14188 children = NIL;
14189
14190 foreach_oid(childrelid, children)
14191 {
14192 Relation childrel;
14193 HeapTuple tuple;
14194 Form_pg_constraint childcon;
14195
14196 /* find_inheritance_children already got lock */
14197 childrel = table_open(childrelid, NoLock);
14198 CheckAlterTableIsSafe(childrel);
14199
14200 /*
14201 * We search for not-null constraints by column name, and others by
14202 * constraint name.
14203 */
14204 if (con->contype == CONSTRAINT_NOTNULL)
14205 {
14206 tuple = findNotNullConstraint(childrelid, colname);
14207 if (!HeapTupleIsValid(tuple))
14208 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14209 colname, RelationGetRelid(childrel));
14210 }
14211 else
14212 {
14213 SysScanDesc scan;
14214 ScanKeyData skey[3];
14215
14216 ScanKeyInit(&skey[0],
14217 Anum_pg_constraint_conrelid,
14218 BTEqualStrategyNumber, F_OIDEQ,
14219 ObjectIdGetDatum(childrelid));
14220 ScanKeyInit(&skey[1],
14221 Anum_pg_constraint_contypid,
14222 BTEqualStrategyNumber, F_OIDEQ,
14224 ScanKeyInit(&skey[2],
14225 Anum_pg_constraint_conname,
14226 BTEqualStrategyNumber, F_NAMEEQ,
14227 CStringGetDatum(constrName));
14228 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14229 true, NULL, 3, skey);
14230 /* There can only be one, so no need to loop */
14231 tuple = systable_getnext(scan);
14232 if (!HeapTupleIsValid(tuple))
14233 ereport(ERROR,
14234 (errcode(ERRCODE_UNDEFINED_OBJECT),
14235 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14236 constrName,
14237 RelationGetRelationName(childrel))));
14238 tuple = heap_copytuple(tuple);
14239 systable_endscan(scan);
14240 }
14241
14242 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14243
14244 /* Right now only CHECK and not-null constraints can be inherited */
14245 if (childcon->contype != CONSTRAINT_CHECK &&
14246 childcon->contype != CONSTRAINT_NOTNULL)
14247 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14248
14249 if (childcon->coninhcount <= 0) /* shouldn't happen */
14250 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14251 childrelid, NameStr(childcon->conname));
14252
14253 if (recurse)
14254 {
14255 /*
14256 * If the child constraint has other definition sources, just
14257 * decrement its inheritance count; if not, recurse to delete it.
14258 */
14259 if (childcon->coninhcount == 1 && !childcon->conislocal)
14260 {
14261 /* Time to delete this child constraint, too */
14262 dropconstraint_internal(childrel, tuple, behavior,
14263 recurse, true, missing_ok,
14264 lockmode);
14265 }
14266 else
14267 {
14268 /* Child constraint must survive my deletion */
14269 childcon->coninhcount--;
14270 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14271
14272 /* Make update visible */
14274 }
14275 }
14276 else
14277 {
14278 /*
14279 * If we were told to drop ONLY in this table (no recursion) and
14280 * there are no further parents for this constraint, we need to
14281 * mark the inheritors' constraints as locally defined rather than
14282 * inherited.
14283 */
14284 childcon->coninhcount--;
14285 if (childcon->coninhcount == 0)
14286 childcon->conislocal = true;
14287
14288 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14289
14290 /* Make update visible */
14292 }
14293
14294 heap_freetuple(tuple);
14295
14296 table_close(childrel, NoLock);
14297 }
14298
14300
14301 return conobj;
14302}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok)
Definition: relcache.c:5044
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition: relcache.c:5300
@ INDEX_ATTR_BITMAP_PRIMARY_KEY
Definition: relcache.h:70
@ INDEX_ATTR_BITMAP_IDENTITY_KEY
Definition: relcache.h:71
HeapTuple SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
Definition: syscache.c:566

References AccessExclusiveLock, AccessShareLock, AT_DropConstraint, ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, attnum, bms_add_member(), bms_is_member(), BTEqualStrategyNumber, CatalogTupleUpdate(), check_stack_depth(), CheckAlterTableIsSafe(), CommandCounterIncrement(), CStringGetDatum(), dropconstraint_internal(), elog, ereport, errcode(), errmsg(), ERROR, extractNotNullColumn(), find_inheritance_children(), findNotNullConstraint(), FirstLowInvalidHeapAttributeNumber, foreach_oid, get_attname(), GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, i, INDEX_ATTR_BITMAP_IDENTITY_KEY, INDEX_ATTR_BITMAP_PRIMARY_KEY, InvalidOid, NameStr, NIL, NoLock, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, performDeletion(), RelationData::rd_index, RelationData::rd_rel, relation_close(), relation_open(), RelationGetIndexAttrBitmap(), RelationGetPrimaryKeyIndex(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopyAttNum(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecDropConstraint(), ATExecDropNotNull(), and dropconstraint_internal().

◆ DropErrorMsgNonExistent()

static void DropErrorMsgNonExistent ( RangeVar rel,
char  rightkind,
bool  missing_ok 
)
static

Definition at line 1453 of file tablecmds.c.

1454{
1455 const struct dropmsgstrings *rentry;
1456
1457 if (rel->schemaname != NULL &&
1459 {
1460 if (!missing_ok)
1461 {
1462 ereport(ERROR,
1463 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1464 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1465 }
1466 else
1467 {
1469 (errmsg("schema \"%s\" does not exist, skipping",
1470 rel->schemaname)));
1471 }
1472 return;
1473 }
1474
1475 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1476 {
1477 if (rentry->kind == rightkind)
1478 {
1479 if (!missing_ok)
1480 {
1481 ereport(ERROR,
1482 (errcode(rentry->nonexistent_code),
1483 errmsg(rentry->nonexistent_msg, rel->relname)));
1484 }
1485 else
1486 {
1487 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1488 break;
1489 }
1490 }
1491 }
1492
1493 Assert(rentry->kind != '\0'); /* Should be impossible */
1494}
Oid LookupNamespaceNoError(const char *nspname)
Definition: namespace.c:3355
char * schemaname
Definition: primnodes.h:80
const char * skipping_msg
Definition: tablecmds.c:250
int nonexistent_code
Definition: tablecmds.c:248
const char * nonexistent_msg
Definition: tablecmds.c:249
static const struct dropmsgstrings dropmsgstringarray[]
Definition: tablecmds.c:255

References Assert(), dropmsgstringarray, ereport, errcode(), errmsg(), ERROR, dropmsgstrings::kind, LookupNamespaceNoError(), dropmsgstrings::nonexistent_code, dropmsgstrings::nonexistent_msg, NOTICE, OidIsValid, RangeVar::relname, RangeVar::schemaname, and dropmsgstrings::skipping_msg.

Referenced by RemoveRelations().

◆ DropErrorMsgWrongType()

static void DropErrorMsgWrongType ( const char *  relname,
char  wrongkind,
char  rightkind 
)
static

Definition at line 1501 of file tablecmds.c.

1502{
1503 const struct dropmsgstrings *rentry;
1504 const struct dropmsgstrings *wentry;
1505
1506 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1507 if (rentry->kind == rightkind)
1508 break;
1509 Assert(rentry->kind != '\0');
1510
1511 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1512 if (wentry->kind == wrongkind)
1513 break;
1514 /* wrongkind could be something we don't have in our table... */
1515
1516 ereport(ERROR,
1517 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1518 errmsg(rentry->nota_msg, relname),
1519 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1520}
const char * drophint_msg
Definition: tablecmds.c:252
const char * nota_msg
Definition: tablecmds.c:251

References _, Assert(), dropmsgstrings::drophint_msg, dropmsgstringarray, ereport, errcode(), errhint(), errmsg(), ERROR, dropmsgstrings::kind, dropmsgstrings::nota_msg, and relname.

Referenced by RangeVarCallbackForDropRelation().

◆ DropForeignKeyConstraintTriggers()

static void DropForeignKeyConstraintTriggers ( Relation  trigrel,
Oid  conoid,
Oid  confrelid,
Oid  conrelid 
)
static

Definition at line 11976 of file tablecmds.c.

11978{
11980 SysScanDesc scan;
11981 HeapTuple trigtup;
11982
11984 Anum_pg_trigger_tgconstraint,
11985 BTEqualStrategyNumber, F_OIDEQ,
11986 ObjectIdGetDatum(conoid));
11987 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11988 NULL, 1, &key);
11989 while ((trigtup = systable_getnext(scan)) != NULL)
11990 {
11991 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11992 ObjectAddress trigger;
11993
11994 /* Invalid if trigger is not for a referential integrity constraint */
11995 if (!OidIsValid(trgform->tgconstrrelid))
11996 continue;
11997 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
11998 continue;
11999 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12000 continue;
12001
12002 /* We should be droping trigger related to foreign key constraint */
12003 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12004 trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12005 trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12006 trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12007 trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12008 trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12009 trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12010 trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12011 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12012 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12013 trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12014 trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12015
12016 /*
12017 * The constraint is originally set up to contain this trigger as an
12018 * implementation object, so there's a dependency record that links
12019 * the two; however, since the trigger is no longer needed, we remove
12020 * the dependency link in order to be able to drop the trigger while
12021 * keeping the constraint intact.
12022 */
12023 deleteDependencyRecordsFor(TriggerRelationId,
12024 trgform->oid,
12025 false);
12026 /* make dependency deletion visible to performDeletion */
12028 ObjectAddressSet(trigger, TriggerRelationId,
12029 trgform->oid);
12030 performDeletion(&trigger, DROP_RESTRICT, 0);
12031 /* make trigger drop visible, in case the loop iterates */
12033 }
12034
12035 systable_endscan(scan);
12036}

References Assert(), BTEqualStrategyNumber, CommandCounterIncrement(), deleteDependencyRecordsFor(), DROP_RESTRICT, GETSTRUCT(), sort-test::key, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, performDeletion(), ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by ATExecAlterConstrEnforceability(), and AttachPartitionForeignKey().

◆ ExecuteTruncate()

void ExecuteTruncate ( TruncateStmt stmt)

Definition at line 1851 of file tablecmds.c.

1852{
1853 List *rels = NIL;
1854 List *relids = NIL;
1855 List *relids_logged = NIL;
1856 ListCell *cell;
1857
1858 /*
1859 * Open, exclusive-lock, and check all the explicitly-specified relations
1860 */
1861 foreach(cell, stmt->relations)
1862 {
1863 RangeVar *rv = lfirst(cell);
1864 Relation rel;
1865 bool recurse = rv->inh;
1866 Oid myrelid;
1867 LOCKMODE lockmode = AccessExclusiveLock;
1868
1869 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1871 NULL);
1872
1873 /* don't throw error for "TRUNCATE foo, foo" */
1874 if (list_member_oid(relids, myrelid))
1875 continue;
1876
1877 /* open the relation, we already hold a lock on it */
1878 rel = table_open(myrelid, NoLock);
1879
1880 /*
1881 * RangeVarGetRelidExtended() has done most checks with its callback,
1882 * but other checks with the now-opened Relation remain.
1883 */
1885
1886 rels = lappend(rels, rel);
1887 relids = lappend_oid(relids, myrelid);
1888
1889 /* Log this relation only if needed for logical decoding */
1891 relids_logged = lappend_oid(relids_logged, myrelid);
1892
1893 if (recurse)
1894 {
1895 ListCell *child;
1896 List *children;
1897
1898 children = find_all_inheritors(myrelid, lockmode, NULL);
1899
1900 foreach(child, children)
1901 {
1902 Oid childrelid = lfirst_oid(child);
1903
1904 if (list_member_oid(relids, childrelid))
1905 continue;
1906
1907 /* find_all_inheritors already got lock */
1908 rel = table_open(childrelid, NoLock);
1909
1910 /*
1911 * It is possible that the parent table has children that are
1912 * temp tables of other backends. We cannot safely access
1913 * such tables (because of buffering issues), and the best
1914 * thing to do is to silently ignore them. Note that this
1915 * check is the same as one of the checks done in
1916 * truncate_check_activity() called below, still it is kept
1917 * here for simplicity.
1918 */
1919 if (RELATION_IS_OTHER_TEMP(rel))
1920 {
1921 table_close(rel, lockmode);
1922 continue;
1923 }
1924
1925 /*
1926 * Inherited TRUNCATE commands perform access permission
1927 * checks on the parent table only. So we skip checking the
1928 * children's permissions and don't call
1929 * truncate_check_perms() here.
1930 */
1933
1934 rels = lappend(rels, rel);
1935 relids = lappend_oid(relids, childrelid);
1936
1937 /* Log this relation only if needed for logical decoding */
1939 relids_logged = lappend_oid(relids_logged, childrelid);
1940 }
1941 }
1942 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1943 ereport(ERROR,
1944 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1945 errmsg("cannot truncate only a partitioned table"),
1946 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1947 }
1948
1949 ExecuteTruncateGuts(rels, relids, relids_logged,
1950 stmt->behavior, stmt->restart_seqs, false);
1951
1952 /* And close the rels */
1953 foreach(cell, rels)
1954 {
1955 Relation rel = (Relation) lfirst(cell);
1956
1957 table_close(rel, NoLock);
1958 }
1959}
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:712
struct RelationData * Relation
Definition: relcache.h:27
static void truncate_check_activity(Relation rel)
Definition: tablecmds.c:2428
static void truncate_check_rel(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2362
static void RangeVarCallbackForTruncate(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19453
void ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
Definition: tablecmds.c:1975

References AccessExclusiveLock, ereport, errcode(), errhint(), errmsg(), ERROR, ExecuteTruncateGuts(), find_all_inheritors(), RangeVar::inh, lappend(), lappend_oid(), lfirst, lfirst_oid, list_member_oid(), NIL, NoLock, RangeVarCallbackForTruncate(), RangeVarGetRelidExtended(), RelationData::rd_rel, RELATION_IS_OTHER_TEMP, RelationGetRelid, RelationIsLogicallyLogged, stmt, table_close(), table_open(), truncate_check_activity(), and truncate_check_rel().

Referenced by standard_ProcessUtility().

◆ ExecuteTruncateGuts()

void ExecuteTruncateGuts ( List explicit_rels,
List relids,
List relids_logged,
DropBehavior  behavior,
bool  restart_seqs,
bool  run_as_table_owner 
)

Definition at line 1975 of file tablecmds.c.

1980{
1981 List *rels;
1982 List *seq_relids = NIL;
1983 HTAB *ft_htab = NULL;
1984 EState *estate;
1985 ResultRelInfo *resultRelInfos;
1986 ResultRelInfo *resultRelInfo;
1987 SubTransactionId mySubid;
1988 ListCell *cell;
1989 Oid *logrelids;
1990
1991 /*
1992 * Check the explicitly-specified relations.
1993 *
1994 * In CASCADE mode, suck in all referencing relations as well. This
1995 * requires multiple iterations to find indirectly-dependent relations. At
1996 * each phase, we need to exclusive-lock new rels before looking for their
1997 * dependencies, else we might miss something. Also, we check each rel as
1998 * soon as we open it, to avoid a faux pas such as holding lock for a long
1999 * time on a rel we have no permissions for.
2000 */
2001 rels = list_copy(explicit_rels);
2002 if (behavior == DROP_CASCADE)
2003 {
2004 for (;;)
2005 {
2006 List *newrelids;
2007
2008 newrelids = heap_truncate_find_FKs(relids);
2009 if (newrelids == NIL)
2010 break; /* nothing else to add */
2011
2012 foreach(cell, newrelids)
2013 {
2014 Oid relid = lfirst_oid(cell);
2015 Relation rel;
2016
2017 rel = table_open(relid, AccessExclusiveLock);
2019 (errmsg("truncate cascades to table \"%s\"",
2021 truncate_check_rel(relid, rel->rd_rel);
2022 truncate_check_perms(relid, rel->rd_rel);
2024 rels = lappend(rels, rel);
2025 relids = lappend_oid(relids, relid);
2026
2027 /* Log this relation only if needed for logical decoding */
2029 relids_logged = lappend_oid(relids_logged, relid);
2030 }
2031 }
2032 }
2033
2034 /*
2035 * Check foreign key references. In CASCADE mode, this should be
2036 * unnecessary since we just pulled in all the references; but as a
2037 * cross-check, do it anyway if in an Assert-enabled build.
2038 */
2039#ifdef USE_ASSERT_CHECKING
2040 heap_truncate_check_FKs(rels, false);
2041#else
2042 if (behavior == DROP_RESTRICT)
2043 heap_truncate_check_FKs(rels, false);
2044#endif
2045
2046 /*
2047 * If we are asked to restart sequences, find all the sequences, lock them
2048 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2049 * We want to do this early since it's pointless to do all the truncation
2050 * work only to fail on sequence permissions.
2051 */
2052 if (restart_seqs)
2053 {
2054 foreach(cell, rels)
2055 {
2056 Relation rel = (Relation) lfirst(cell);
2057 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2058 ListCell *seqcell;
2059
2060 foreach(seqcell, seqlist)
2061 {
2062 Oid seq_relid = lfirst_oid(seqcell);
2063 Relation seq_rel;
2064
2065 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2066
2067 /* This check must match AlterSequence! */
2068 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2070 RelationGetRelationName(seq_rel));
2071
2072 seq_relids = lappend_oid(seq_relids, seq_relid);
2073
2074 relation_close(seq_rel, NoLock);
2075 }
2076 }
2077 }
2078
2079 /* Prepare to catch AFTER triggers. */
2081
2082 /*
2083 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2084 * each relation. We don't need to call ExecOpenIndices, though.
2085 *
2086 * We put the ResultRelInfos in the es_opened_result_relations list, even
2087 * though we don't have a range table and don't populate the
2088 * es_result_relations array. That's a bit bogus, but it's enough to make
2089 * ExecGetTriggerResultRel() find them.
2090 */
2091 estate = CreateExecutorState();
2092 resultRelInfos = (ResultRelInfo *)
2093 palloc(list_length(rels) * sizeof(ResultRelInfo));
2094 resultRelInfo = resultRelInfos;
2095 foreach(cell, rels)
2096 {
2097 Relation rel = (Relation) lfirst(cell);
2098
2099 InitResultRelInfo(resultRelInfo,
2100 rel,
2101 0, /* dummy rangetable index */
2102 NULL,
2103 0);
2105 lappend(estate->es_opened_result_relations, resultRelInfo);
2106 resultRelInfo++;
2107 }
2108
2109 /*
2110 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2111 * truncating (this is because one of them might throw an error). Also, if
2112 * we were to allow them to prevent statement execution, that would need
2113 * to be handled here.
2114 */
2115 resultRelInfo = resultRelInfos;
2116 foreach(cell, rels)
2117 {
2118 UserContext ucxt;
2119
2120 if (run_as_table_owner)
2121 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2122 &ucxt);
2123 ExecBSTruncateTriggers(estate, resultRelInfo);
2124 if (run_as_table_owner)
2125 RestoreUserContext(&ucxt);
2126 resultRelInfo++;
2127 }
2128
2129 /*
2130 * OK, truncate each table.
2131 */
2132 mySubid = GetCurrentSubTransactionId();
2133
2134 foreach(cell, rels)
2135 {
2136 Relation rel = (Relation) lfirst(cell);
2137
2138 /* Skip partitioned tables as there is nothing to do */
2139 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2140 continue;
2141
2142 /*
2143 * Build the lists of foreign tables belonging to each foreign server
2144 * and pass each list to the foreign data wrapper's callback function,
2145 * so that each server can truncate its all foreign tables in bulk.
2146 * Each list is saved as a single entry in a hash table that uses the
2147 * server OID as lookup key.
2148 */
2149 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2150 {
2152 bool found;
2153 ForeignTruncateInfo *ft_info;
2154
2155 /* First time through, initialize hashtable for foreign tables */
2156 if (!ft_htab)
2157 {
2158 HASHCTL hctl;
2159
2160 memset(&hctl, 0, sizeof(HASHCTL));
2161 hctl.keysize = sizeof(Oid);
2162 hctl.entrysize = sizeof(ForeignTruncateInfo);
2164
2165 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2166 32, /* start small and extend */
2167 &hctl,
2169 }
2170
2171 /* Find or create cached entry for the foreign table */
2172 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2173 if (!found)
2174 ft_info->rels = NIL;
2175
2176 /*
2177 * Save the foreign table in the entry of the server that the
2178 * foreign table belongs to.
2179 */
2180 ft_info->rels = lappend(ft_info->rels, rel);
2181 continue;
2182 }
2183
2184 /*
2185 * Normally, we need a transaction-safe truncation here. However, if
2186 * the table was either created in the current (sub)transaction or has
2187 * a new relfilenumber in the current (sub)transaction, then we can
2188 * just truncate it in-place, because a rollback would cause the whole
2189 * table or the current physical file to be thrown away anyway.
2190 */
2191 if (rel->rd_createSubid == mySubid ||
2192 rel->rd_newRelfilelocatorSubid == mySubid)
2193 {
2194 /* Immediate, non-rollbackable truncation is OK */
2196 }
2197 else
2198 {
2199 Oid heap_relid;
2200 Oid toast_relid;
2201 ReindexParams reindex_params = {0};
2202
2203 /*
2204 * This effectively deletes all rows in the table, and may be done
2205 * in a serializable transaction. In that case we must record a
2206 * rw-conflict in to this transaction from each transaction
2207 * holding a predicate lock on the table.
2208 */
2210
2211 /*
2212 * Need the full transaction-safe pushups.
2213 *
2214 * Create a new empty storage file for the relation, and assign it
2215 * as the relfilenumber value. The old storage file is scheduled
2216 * for deletion at commit.
2217 */
2218 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2219
2220 heap_relid = RelationGetRelid(rel);
2221
2222 /*
2223 * The same for the toast table, if any.
2224 */
2225 toast_relid = rel->rd_rel->reltoastrelid;
2226 if (OidIsValid(toast_relid))
2227 {
2228 Relation toastrel = relation_open(toast_relid,
2230
2232 toastrel->rd_rel->relpersistence);
2233 table_close(toastrel, NoLock);
2234 }
2235
2236 /*
2237 * Reconstruct the indexes to match, and we're done.
2238 */
2240 &reindex_params);
2241 }
2242
2244 }
2245
2246 /* Now go through the hash table, and truncate foreign tables */
2247 if (ft_htab)
2248 {
2249 ForeignTruncateInfo *ft_info;
2250 HASH_SEQ_STATUS seq;
2251
2252 hash_seq_init(&seq, ft_htab);
2253
2254 PG_TRY();
2255 {
2256 while ((ft_info = hash_seq_search(&seq)) != NULL)
2257 {
2258 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2259
2260 /* truncate_check_rel() has checked that already */
2261 Assert(routine->ExecForeignTruncate != NULL);
2262
2263 routine->ExecForeignTruncate(ft_info->rels,
2264 behavior,
2265 restart_seqs);
2266 }
2267 }
2268 PG_FINALLY();
2269 {
2270 hash_destroy(ft_htab);
2271 }
2272 PG_END_TRY();
2273 }
2274
2275 /*
2276 * Restart owned sequences if we were asked to.
2277 */
2278 foreach(cell, seq_relids)
2279 {
2280 Oid seq_relid = lfirst_oid(cell);
2281
2282 ResetSequence(seq_relid);
2283 }
2284
2285 /*
2286 * Write a WAL record to allow this set of actions to be logically
2287 * decoded.
2288 *
2289 * Assemble an array of relids so we can write a single WAL record for the
2290 * whole action.
2291 */
2292 if (relids_logged != NIL)
2293 {
2294 xl_heap_truncate xlrec;
2295 int i = 0;
2296
2297 /* should only get here if wal_level >= logical */
2299
2300 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2301 foreach(cell, relids_logged)
2302 logrelids[i++] = lfirst_oid(cell);
2303
2304 xlrec.dbId = MyDatabaseId;
2305 xlrec.nrelids = list_length(relids_logged);
2306 xlrec.flags = 0;
2307 if (behavior == DROP_CASCADE)
2308 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2309 if (restart_seqs)
2311
2314 XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2315
2317
2318 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2319 }
2320
2321 /*
2322 * Process all AFTER STATEMENT TRUNCATE triggers.
2323 */
2324 resultRelInfo = resultRelInfos;
2325 foreach(cell, rels)
2326 {
2327 UserContext ucxt;
2328
2329 if (run_as_table_owner)
2330 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2331 &ucxt);
2332 ExecASTruncateTriggers(estate, resultRelInfo);
2333 if (run_as_table_owner)
2334 RestoreUserContext(&ucxt);
2335 resultRelInfo++;
2336 }
2337
2338 /* Handle queued AFTER triggers */
2339 AfterTriggerEndQuery(estate);
2340
2341 /* We can clean up the EState now */
2342 FreeExecutorState(estate);
2343
2344 /*
2345 * Close any rels opened by CASCADE (can't do this while EState still
2346 * holds refs)
2347 */
2348 rels = list_difference_ptr(rels, explicit_rels);
2349 foreach(cell, rels)
2350 {
2351 Relation rel = (Relation) lfirst(cell);
2352
2353 table_close(rel, NoLock);
2354 }
2355}
uint32 SubTransactionId
Definition: c.h:627
void ResetSequence(Oid seq_relid)
Definition: sequence.c:262
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
void hash_destroy(HTAB *hashp)
Definition: dynahash.c:865
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1420
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1385
#define PG_TRY(...)
Definition: elog.h:372
#define PG_END_TRY(...)
Definition: elog.h:397
#define PG_FINALLY(...)
Definition: elog.h:389
struct ResultRelInfo ResultRelInfo
FdwRoutine * GetFdwRoutineByServerId(Oid serverid)
Definition: foreign.c:377
Oid GetForeignServerIdByRelId(Oid relid)
Definition: foreign.c:355
List * heap_truncate_find_FKs(List *relationIds)
Definition: heap.c:3673
void heap_truncate_check_FKs(List *relations, bool tempTables)
Definition: heap.c:3578
void heap_truncate_one_rel(Relation rel)
Definition: heap.c:3534
#define XLOG_HEAP_TRUNCATE
Definition: heapam_xlog.h:36
#define XLH_TRUNCATE_RESTART_SEQS
Definition: heapam_xlog.h:127
#define SizeOfHeapTruncate
Definition: heapam_xlog.h:142
#define XLH_TRUNCATE_CASCADE
Definition: heapam_xlog.h:126
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
Definition: index.c:3948
#define REINDEX_REL_PROCESS_TOAST
Definition: index.h:159
List * list_difference_ptr(const List *list1, const List *list2)
Definition: list.c:1263
@ DROP_CASCADE
Definition: parsenodes.h:2391
@ OBJECT_SEQUENCE
Definition: parsenodes.h:2354
void pgstat_count_truncate(Relation rel)
void CheckTableForSerializableConflictIn(Relation relation)
Definition: predicate.c:4419
void RelationSetNewRelfilenumber(Relation relation, char persistence)
Definition: relcache.c:3773
List * es_opened_result_relations
Definition: execnodes.h:686
ExecForeignTruncate_function ExecForeignTruncate
Definition: fdwapi.h:263
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86
Definition: dynahash.c:220
SubTransactionId rd_newRelfilelocatorSubid
Definition: rel.h:104
Relation ri_RelationDesc
Definition: execnodes.h:475
struct ForeignTruncateInfo ForeignTruncateInfo
static void truncate_check_perms(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2410
void ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:3231
void ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:3278
void AfterTriggerEndQuery(EState *estate)
Definition: trigger.c:5088
void AfterTriggerBeginQuery(void)
Definition: trigger.c:5053
void SwitchToUntrustedUser(Oid userid, UserContext *context)
Definition: usercontext.c:33
void RestoreUserContext(UserContext *context)
Definition: usercontext.c:87
SubTransactionId GetCurrentSubTransactionId(void)
Definition: xact.c:791
#define XLogLogicalInfoActive()
Definition: xlog.h:126
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:154
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:474
void XLogRegisterData(const void *data, uint32 len)
Definition: xloginsert.c:364
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:456
void XLogBeginInsert(void)
Definition: xloginsert.c:149

References AccessExclusiveLock, aclcheck_error(), ACLCHECK_NOT_OWNER, AfterTriggerBeginQuery(), AfterTriggerEndQuery(), Assert(), CheckTableForSerializableConflictIn(), CreateExecutorState(), CurrentMemoryContext, xl_heap_truncate::dbId, DROP_CASCADE, DROP_RESTRICT, HASHCTL::entrysize, ereport, errmsg(), EState::es_opened_result_relations, ExecASTruncateTriggers(), ExecBSTruncateTriggers(), FdwRoutine::ExecForeignTruncate, xl_heap_truncate::flags, FreeExecutorState(), GetCurrentSubTransactionId(), GetFdwRoutineByServerId(), GetForeignServerIdByRelId(), getOwnedSequences(), GetUserId(), HASH_BLOBS, HASH_CONTEXT, hash_create(), hash_destroy(), HASH_ELEM, HASH_ENTER, hash_search(), hash_seq_init(), hash_seq_search(), HASHCTL::hcxt, heap_truncate_check_FKs(), heap_truncate_find_FKs(), heap_truncate_one_rel(), i, InitResultRelInfo(), HASHCTL::keysize, lappend(), lappend_oid(), lfirst, lfirst_oid, list_copy(), list_difference_ptr(), list_length(), MyDatabaseId, NIL, NoLock, NOTICE, xl_heap_truncate::nrelids, object_ownercheck(), OBJECT_SEQUENCE, OidIsValid, palloc(), PG_END_TRY, PG_FINALLY, PG_TRY, pgstat_count_truncate(), RelationData::rd_createSubid, RelationData::rd_newRelfilelocatorSubid, RelationData::rd_rel, REINDEX_REL_PROCESS_TOAST, reindex_relation(), relation_close(), relation_open(), RelationGetRelationName, RelationGetRelid, RelationIsLogicallyLogged, RelationSetNewRelfilenumber(), ForeignTruncateInfo::rels, ResetSequence(), RestoreUserContext(), ResultRelInfo::ri_RelationDesc, ForeignTruncateInfo::serverid, SizeOfHeapTruncate, SwitchToUntrustedUser(), table_close(), table_open(), truncate_check_activity(), truncate_check_perms(), truncate_check_rel(), XLH_TRUNCATE_CASCADE, XLH_TRUNCATE_RESTART_SEQS, XLOG_HEAP_TRUNCATE, XLOG_INCLUDE_ORIGIN, XLogBeginInsert(), XLogInsert(), XLogLogicalInfoActive, XLogRegisterData(), and XLogSetRecordFlags().

Referenced by apply_handle_truncate(), and ExecuteTruncate().

◆ find_composite_type_dependencies()

void find_composite_type_dependencies ( Oid  typeOid,
Relation  origRelation,
const char *  origTypeName 
)

Definition at line 6926 of file tablecmds.c.

6928{
6929 Relation depRel;
6930 ScanKeyData key[2];
6931 SysScanDesc depScan;
6932 HeapTuple depTup;
6933
6934 /* since this function recurses, it could be driven to stack overflow */
6936
6937 /*
6938 * We scan pg_depend to find those things that depend on the given type.
6939 * (We assume we can ignore refobjsubid for a type.)
6940 */
6941 depRel = table_open(DependRelationId, AccessShareLock);
6942
6943 ScanKeyInit(&key[0],
6944 Anum_pg_depend_refclassid,
6945 BTEqualStrategyNumber, F_OIDEQ,
6946 ObjectIdGetDatum(TypeRelationId));
6947 ScanKeyInit(&key[1],
6948 Anum_pg_depend_refobjid,
6949 BTEqualStrategyNumber, F_OIDEQ,
6950 ObjectIdGetDatum(typeOid));
6951
6952 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6953 NULL, 2, key);
6954
6955 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6956 {
6957 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6958 Relation rel;
6959 TupleDesc tupleDesc;
6961
6962 /* Check for directly dependent types */
6963 if (pg_depend->classid == TypeRelationId)
6964 {
6965 /*
6966 * This must be an array, domain, or range containing the given
6967 * type, so recursively check for uses of this type. Note that
6968 * any error message will mention the original type not the
6969 * container; this is intentional.
6970 */
6971 find_composite_type_dependencies(pg_depend->objid,
6972 origRelation, origTypeName);
6973 continue;
6974 }
6975
6976 /* Else, ignore dependees that aren't relations */
6977 if (pg_depend->classid != RelationRelationId)
6978 continue;
6979
6980 rel = relation_open(pg_depend->objid, AccessShareLock);
6981 tupleDesc = RelationGetDescr(rel);
6982
6983 /*
6984 * If objsubid identifies a specific column, refer to that in error
6985 * messages. Otherwise, search to see if there's a user column of the
6986 * type. (We assume system columns are never of interesting types.)
6987 * The search is needed because an index containing an expression
6988 * column of the target type will just be recorded as a whole-relation
6989 * dependency. If we do not find a column of the type, the dependency
6990 * must indicate that the type is transiently referenced in an index
6991 * expression but not stored on disk, which we assume is OK, just as
6992 * we do for references in views. (It could also be that the target
6993 * type is embedded in some container type that is stored in an index
6994 * column, but the previous recursion should catch such cases.)
6995 */
6996 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6997 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6998 else
6999 {
7000 att = NULL;
7001 for (int attno = 1; attno <= tupleDesc->natts; attno++)
7002 {
7003 att = TupleDescAttr(tupleDesc, attno - 1);
7004 if (att->atttypid == typeOid && !att->attisdropped)
7005 break;
7006 att = NULL;
7007 }
7008 if (att == NULL)
7009 {
7010 /* No such column, so assume OK */
7012 continue;
7013 }
7014 }
7015
7016 /*
7017 * We definitely should reject if the relation has storage. If it's
7018 * partitioned, then perhaps we don't have to reject: if there are
7019 * partitions then we'll fail when we find one, else there is no
7020 * stored data to worry about. However, it's possible that the type
7021 * change would affect conclusions about whether the type is sortable
7022 * or hashable and thus (if it's a partitioning column) break the
7023 * partitioning rule. For now, reject for partitioned rels too.
7024 */
7025 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7026 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7027 {
7028 if (origTypeName)
7029 ereport(ERROR,
7030 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7031 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7032 origTypeName,
7034 NameStr(att->attname))));
7035 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7036 ereport(ERROR,
7037 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7038 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7039 RelationGetRelationName(origRelation),
7041 NameStr(att->attname))));
7042 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7043 ereport(ERROR,
7044 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7045 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7046 RelationGetRelationName(origRelation),
7048 NameStr(att->attname))));
7049 else
7050 ereport(ERROR,
7051 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7052 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7053 RelationGetRelationName(origRelation),
7055 NameStr(att->attname))));
7056 }
7057 else if (OidIsValid(rel->rd_rel->reltype))
7058 {
7059 /*
7060 * A view or composite type itself isn't a problem, but we must
7061 * recursively check for indirect dependencies via its rowtype.
7062 */
7064 origRelation, origTypeName);
7065 }
7066
7068 }
7069
7070 systable_endscan(depScan);
7071
7073}

References AccessShareLock, BTEqualStrategyNumber, check_stack_depth(), ereport, errcode(), errmsg(), ERROR, find_composite_type_dependencies(), GETSTRUCT(), HeapTupleIsValid, sort-test::key, NameStr, TupleDescData::natts, ObjectIdGetDatum(), OidIsValid, RelationData::rd_rel, relation_close(), relation_open(), RelationGetDescr, RelationGetRelationName, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_open(), and TupleDescAttr().

Referenced by ATPrepAlterColumnType(), ATRewriteTables(), find_composite_type_dependencies(), and get_rels_with_domain().

◆ find_typed_table_dependencies()

static List * find_typed_table_dependencies ( Oid  typeOid,
const char *  typeName,
DropBehavior  behavior 
)
static

Definition at line 7084 of file tablecmds.c.

7085{
7086 Relation classRel;
7087 ScanKeyData key[1];
7088 TableScanDesc scan;
7089 HeapTuple tuple;
7090 List *result = NIL;
7091
7092 classRel = table_open(RelationRelationId, AccessShareLock);
7093
7094 ScanKeyInit(&key[0],
7095 Anum_pg_class_reloftype,
7096 BTEqualStrategyNumber, F_OIDEQ,
7097 ObjectIdGetDatum(typeOid));
7098
7099 scan = table_beginscan_catalog(classRel, 1, key);
7100
7101 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7102 {
7103 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7104
7105 if (behavior == DROP_RESTRICT)
7106 ereport(ERROR,
7107 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7108 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7109 typeName),
7110 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7111 else
7112 result = lappend_oid(result, classform->oid);
7113 }
7114
7115 table_endscan(scan);
7116 table_close(classRel, AccessShareLock);
7117
7118 return result;
7119}

References AccessShareLock, BTEqualStrategyNumber, DROP_RESTRICT, ereport, errcode(), errhint(), errmsg(), ERROR, ForwardScanDirection, GETSTRUCT(), heap_getnext(), sort-test::key, lappend_oid(), NIL, ObjectIdGetDatum(), ScanKeyInit(), table_beginscan_catalog(), table_close(), table_endscan(), and table_open().

Referenced by ATTypedTableRecursion(), and renameatt_internal().

◆ findAttrByName()

static int findAttrByName ( const char *  attributeName,
const List columns 
)
static

Definition at line 3600 of file tablecmds.c.

3601{
3602 ListCell *lc;
3603 int i = 1;
3604
3605 foreach(lc, columns)
3606 {
3607 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3608 return i;
3609
3610 i++;
3611 }
3612 return 0;
3613}

References i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

static CoercionPathType findFkeyCast ( Oid  targetTypeId,
Oid  sourceTypeId,
Oid funcid 
)
static

Definition at line 13585 of file tablecmds.c.

13586{
13587 CoercionPathType ret;
13588
13589 if (targetTypeId == sourceTypeId)
13590 {
13592 *funcid = InvalidOid;
13593 }
13594 else
13595 {
13596 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13597 COERCION_IMPLICIT, funcid);
13598 if (ret == COERCION_PATH_NONE)
13599 /* A previously-relied-upon cast is now gone. */
13600 elog(ERROR, "could not find cast from %u to %u",
13601 sourceTypeId, targetTypeId);
13602 }
13603
13604 return ret;
13605}
CoercionPathType find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, Oid *funcid)
@ COERCION_PATH_NONE
Definition: parse_coerce.h:26
@ COERCION_PATH_RELABELTYPE
Definition: parse_coerce.h:28

References COERCION_IMPLICIT, COERCION_PATH_NONE, COERCION_PATH_RELABELTYPE, elog, ERROR, find_coercion_pathway(), and InvalidOid.

Referenced by ATAddForeignKeyConstraint().

◆ GetAttributeCompression()

static char GetAttributeCompression ( Oid  atttypid,
const char *  compression 
)
static

Definition at line 21945 of file tablecmds.c.

21946{
21947 char cmethod;
21948
21949 if (compression == NULL || strcmp(compression, "default") == 0)
21951
21952 /*
21953 * To specify a nondefault method, the column data type must be toastable.
21954 * Note this says nothing about whether the column's attstorage setting
21955 * permits compression; we intentionally allow attstorage and
21956 * attcompression to be independent. But with a non-toastable type,
21957 * attstorage could not be set to a value that would permit compression.
21958 *
21959 * We don't actually need to enforce this, since nothing bad would happen
21960 * if attcompression were non-default; it would never be consulted. But
21961 * it seems more user-friendly to complain about a certainly-useless
21962 * attempt to set the property.
21963 */
21964 if (!TypeIsToastable(atttypid))
21965 ereport(ERROR,
21966 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21967 errmsg("column data type %s does not support compression",
21968 format_type_be(atttypid))));
21969
21970 cmethod = CompressionNameToMethod(compression);
21971 if (!CompressionMethodIsValid(cmethod))
21972 ereport(ERROR,
21973 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21974 errmsg("invalid compression method \"%s\"", compression)));
21975
21976 return cmethod;
21977}
#define TypeIsToastable(typid)
Definition: lsyscache.h:218
char CompressionNameToMethod(const char *compression)
#define CompressionMethodIsValid(cm)

References CompressionMethodIsValid, CompressionNameToMethod(), ereport, errcode(), errmsg(), ERROR, format_type_be(), InvalidCompressionMethod, and TypeIsToastable.

Referenced by ATExecSetCompression(), and BuildDescForRelation().

◆ GetAttributeStorage()

static char GetAttributeStorage ( Oid  atttypid,
const char *  storagemode 
)
static

Definition at line 21983 of file tablecmds.c.

21984{
21985 char cstorage = 0;
21986
21987 if (pg_strcasecmp(storagemode, "plain") == 0)
21988 cstorage = TYPSTORAGE_PLAIN;
21989 else if (pg_strcasecmp(storagemode, "external") == 0)
21990 cstorage = TYPSTORAGE_EXTERNAL;
21991 else if (pg_strcasecmp(storagemode, "extended") == 0)
21992 cstorage = TYPSTORAGE_EXTENDED;
21993 else if (pg_strcasecmp(storagemode, "main") == 0)
21994 cstorage = TYPSTORAGE_MAIN;
21995 else if (pg_strcasecmp(storagemode, "default") == 0)
21996 cstorage = get_typstorage(atttypid);
21997 else
21998 ereport(ERROR,
21999 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22000 errmsg("invalid storage type \"%s\"",
22001 storagemode)));
22002
22003 /*
22004 * safety check: do not allow toasted storage modes unless column datatype
22005 * is TOAST-aware.
22006 */
22007 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22008 ereport(ERROR,
22009 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22010 errmsg("column data type %s can only have storage PLAIN",
22011 format_type_be(atttypid))));
22012
22013 return cstorage;
22014}
char get_typstorage(Oid typid)
Definition: lsyscache.c:2559
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36

References ereport, errcode(), errmsg(), ERROR, format_type_be(), get_typstorage(), pg_strcasecmp(), and TypeIsToastable.

Referenced by ATExecSetStorage(), and BuildDescForRelation().

◆ GetForeignKeyActionTriggers()

static void GetForeignKeyActionTriggers ( Relation  trigrel,
Oid  conoid,
Oid  confrelid,
Oid  conrelid,
Oid deleteTriggerOid,
Oid updateTriggerOid 
)
static

Definition at line 12044 of file tablecmds.c.

12048{
12050 SysScanDesc scan;
12051 HeapTuple trigtup;
12052
12053 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12055 Anum_pg_trigger_tgconstraint,
12056 BTEqualStrategyNumber, F_OIDEQ,
12057 ObjectIdGetDatum(conoid));
12058
12059 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12060 NULL, 1, &key);
12061 while ((trigtup = systable_getnext(scan)) != NULL)
12062 {
12063 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12064
12065 if (trgform->tgconstrrelid != conrelid)
12066 continue;
12067 if (trgform->tgrelid != confrelid)
12068 continue;
12069 /* Only ever look at "action" triggers on the PK side. */
12070 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12071 continue;
12072 if (TRIGGER_FOR_DELETE(trgform->tgtype))
12073 {
12074 Assert(*deleteTriggerOid == InvalidOid);
12075 *deleteTriggerOid = trgform->oid;
12076 }
12077 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12078 {
12079 Assert(*updateTriggerOid == InvalidOid);
12080 *updateTriggerOid = trgform->oid;
12081 }
12082#ifndef USE_ASSERT_CHECKING
12083 /* In an assert-enabled build, continue looking to find duplicates */
12084 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12085 break;
12086#endif
12087 }
12088
12089 if (!OidIsValid(*deleteTriggerOid))
12090 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12091 conoid);
12092 if (!OidIsValid(*updateTriggerOid))
12093 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12094 conoid);
12095
12096 systable_endscan(scan);
12097}
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3210
#define RI_TRIGGER_PK
Definition: trigger.h:283

References Assert(), BTEqualStrategyNumber, elog, ERROR, GETSTRUCT(), InvalidOid, sort-test::key, ObjectIdGetDatum(), OidIsValid, RI_FKey_trigger_type(), RI_TRIGGER_PK, ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by CloneFkReferenced().

◆ GetForeignKeyCheckTriggers()

static void GetForeignKeyCheckTriggers ( Relation  trigrel,
Oid  conoid,
Oid  confrelid,
Oid  conrelid,
Oid insertTriggerOid,
Oid updateTriggerOid 
)
static

Definition at line 12105 of file tablecmds.c.

12109{
12111 SysScanDesc scan;
12112 HeapTuple trigtup;
12113
12114 *insertTriggerOid = *updateTriggerOid = InvalidOid;
12116 Anum_pg_trigger_tgconstraint,
12117 BTEqualStrategyNumber, F_OIDEQ,
12118 ObjectIdGetDatum(conoid));
12119
12120 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12121 NULL, 1, &key);
12122 while ((trigtup = systable_getnext(scan)) != NULL)
12123 {
12124 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12125
12126 if (trgform->tgconstrrelid != confrelid)
12127 continue;
12128 if (trgform->tgrelid != conrelid)
12129 continue;
12130 /* Only ever look at "check" triggers on the FK side. */
12131 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12132 continue;
12133 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12134 {
12135 Assert(*insertTriggerOid == InvalidOid);
12136 *insertTriggerOid = trgform->oid;
12137 }
12138 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12139 {
12140 Assert(*updateTriggerOid == InvalidOid);
12141 *updateTriggerOid = trgform->oid;
12142 }
12143#ifndef USE_ASSERT_CHECKING
12144 /* In an assert-enabled build, continue looking to find duplicates. */
12145 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12146 break;
12147#endif
12148 }
12149
12150 if (!OidIsValid(*insertTriggerOid))
12151 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12152 conoid);
12153 if (!OidIsValid(*updateTriggerOid))
12154 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12155 conoid);
12156
12157 systable_endscan(scan);
12158}
#define RI_TRIGGER_FK
Definition: trigger.h:284

References Assert(), BTEqualStrategyNumber, elog, ERROR, GETSTRUCT(), InvalidOid, sort-test::key, ObjectIdGetDatum(), OidIsValid, RI_FKey_trigger_type(), RI_TRIGGER_FK, ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by AttachPartitionForeignKey(), CloneFkReferencing(), and DetachPartitionFinalize().

◆ GetParentedForeignKeyRefs()

static List * GetParentedForeignKeyRefs ( Relation  partition)
static

Definition at line 21843 of file tablecmds.c.

21844{
21845 Relation pg_constraint;
21846 HeapTuple tuple;
21847 SysScanDesc scan;
21848 ScanKeyData key[2];
21849 List *constraints = NIL;
21850
21851 /*
21852 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21853 * scan.
21854 */
21855 if (RelationGetIndexList(partition) == NIL ||
21858 return NIL;
21859
21860 /* Search for constraints referencing this table */
21861 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21862 ScanKeyInit(&key[0],
21863 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21864 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21865 ScanKeyInit(&key[1],
21866 Anum_pg_constraint_contype, BTEqualStrategyNumber,
21867 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21868
21869 /* XXX This is a seqscan, as we don't have a usable index */
21870 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21871 while ((tuple = systable_getnext(scan)) != NULL)
21872 {
21873 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21874
21875 /*
21876 * We only need to process constraints that are part of larger ones.
21877 */
21878 if (!OidIsValid(constrForm->conparentid))
21879 continue;
21880
21881 constraints = lappend_oid(constraints, constrForm->oid);
21882 }
21883
21884 systable_endscan(scan);
21885 table_close(pg_constraint, AccessShareLock);
21886
21887 return constraints;
21888}
#define bms_is_empty(a)
Definition: bitmapset.h:118
@ INDEX_ATTR_BITMAP_KEY
Definition: relcache.h:69

References AccessShareLock, bms_is_empty, BTEqualStrategyNumber, CharGetDatum(), GETSTRUCT(), INDEX_ATTR_BITMAP_KEY, InvalidOid, sort-test::key, lappend_oid(), NIL, ObjectIdGetDatum(), OidIsValid, RelationGetIndexAttrBitmap(), RelationGetIndexList(), RelationGetRelid, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATDetachCheckNoForeignKeyRefs(), and DetachPartitionFinalize().

◆ index_copy_data()

static void index_copy_data ( Relation  rel,
RelFileLocator  newrlocator 
)
static

Definition at line 17070 of file tablecmds.c.

17071{
17072 SMgrRelation dstrel;
17073
17074 /*
17075 * Since we copy the file directly without looking at the shared buffers,
17076 * we'd better first flush out any pages of the source relation that are
17077 * in shared buffers. We assume no new changes will be made while we are
17078 * holding exclusive lock on the rel.
17079 */
17081
17082 /*
17083 * Create and copy all forks of the relation, and schedule unlinking of
17084 * old physical files.
17085 *
17086 * NOTE: any conflict in relfilenumber value will be caught in
17087 * RelationCreateStorage().
17088 */
17089 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17090
17091 /* copy main fork */
17093 rel->rd_rel->relpersistence);
17094
17095 /* copy those extra forks that exist */
17096 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17097 forkNum <= MAX_FORKNUM; forkNum++)
17098 {
17099 if (smgrexists(RelationGetSmgr(rel), forkNum))
17100 {
17101 smgrcreate(dstrel, forkNum, false);
17102
17103 /*
17104 * WAL log creation if the relation is persistent, or this is the
17105 * init fork of an unlogged relation.
17106 */
17107 if (RelationIsPermanent(rel) ||
17108 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17109 forkNum == INIT_FORKNUM))
17110 log_smgrcreate(&newrlocator, forkNum);
17111 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17112 rel->rd_rel->relpersistence);
17113 }
17114 }
17115
17116 /* drop old relation, and close new one */
17118 smgrclose(dstrel);
17119}
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:4943
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:578
ForkNumber
Definition: relpath.h:56
@ MAIN_FORKNUM
Definition: relpath.h:58
@ INIT_FORKNUM
Definition: relpath.h:61
#define MAX_FORKNUM
Definition: relpath.h:70
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition: smgr.c:481
void smgrclose(SMgrRelation reln)
Definition: smgr.c:374
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:462
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition: storage.c:478
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition: storage.c:122
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition: storage.c:187
void RelationDropStorage(Relation rel)
Definition: storage.c:207

References FlushRelationBuffers(), INIT_FORKNUM, log_smgrcreate(), MAIN_FORKNUM, MAX_FORKNUM, RelationData::rd_rel, RelationCopyStorage(), RelationCreateStorage(), RelationDropStorage(), RelationGetSmgr(), RelationIsPermanent, smgrclose(), smgrcreate(), and smgrexists().

Referenced by ATExecSetTableSpace().

◆ MarkInheritDetached()

static void MarkInheritDetached ( Relation  child_rel,
Relation  parent_rel 
)
static

Definition at line 17790 of file tablecmds.c.

17791{
17792 Relation catalogRelation;
17793 SysScanDesc scan;
17795 HeapTuple inheritsTuple;
17796 bool found = false;
17797
17798 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17799
17800 /*
17801 * Find pg_inherits entries by inhparent. (We need to scan them all in
17802 * order to verify that no other partition is pending detach.)
17803 */
17804 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17806 Anum_pg_inherits_inhparent,
17807 BTEqualStrategyNumber, F_OIDEQ,
17808 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17809 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17810 true, NULL, 1, &key);
17811
17812 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17813 {
17814 Form_pg_inherits inhForm;
17815
17816 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17817 if (inhForm->inhdetachpending)
17818 ereport(ERROR,
17819 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17820 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17821 get_rel_name(inhForm->inhrelid),
17822 get_namespace_name(parent_rel->rd_rel->relnamespace),
17823 RelationGetRelationName(parent_rel)),
17824 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17825
17826 if (inhForm->inhrelid == RelationGetRelid(child_rel))
17827 {
17828 HeapTuple newtup;
17829
17830 newtup = heap_copytuple(inheritsTuple);
17831 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17832
17833 CatalogTupleUpdate(catalogRelation,
17834 &inheritsTuple->t_self,
17835 newtup);
17836 found = true;
17837 heap_freetuple(newtup);
17838 /* keep looking, to ensure we catch others pending detach */
17839 }
17840 }
17841
17842 /* Done */
17843 systable_endscan(scan);
17844 table_close(catalogRelation, RowExclusiveLock);
17845
17846 if (!found)
17847 ereport(ERROR,
17849 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17850 RelationGetRelationName(child_rel),
17851 RelationGetRelationName(parent_rel))));
17852}
#define ERRCODE_UNDEFINED_TABLE
Definition: pgbench.c:79

References Assert(), BTEqualStrategyNumber, CatalogTupleUpdate(), ereport, errcode(), ERRCODE_UNDEFINED_TABLE, errhint(), errmsg(), ERROR, get_namespace_name(), get_rel_name(), GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, sort-test::key, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecDetachPartition().

◆ MergeAttributes()

static List * MergeAttributes ( List columns,
const List supers,
char  relpersistence,
bool  is_partition,
List **  supconstr,
List **  supnotnulls 
)
static

Definition at line 2536 of file tablecmds.c.

2538{
2539 List *inh_columns = NIL;
2540 List *constraints = NIL;
2541 List *nnconstraints = NIL;
2542 bool have_bogus_defaults = false;
2543 int child_attno;
2544 static Node bogus_marker = {0}; /* marks conflicting defaults */
2545 List *saved_columns = NIL;
2546 ListCell *lc;
2547
2548 /*
2549 * Check for and reject tables with too many columns. We perform this
2550 * check relatively early for two reasons: (a) we don't run the risk of
2551 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2552 * okay if we're processing <= 1600 columns, but could take minutes to
2553 * execute if the user attempts to create a table with hundreds of
2554 * thousands of columns.
2555 *
2556 * Note that we also need to check that we do not exceed this figure after
2557 * including columns from inherited relations.
2558 */
2559 if (list_length(columns) > MaxHeapAttributeNumber)
2560 ereport(ERROR,
2561 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2562 errmsg("tables can have at most %d columns",
2564
2565 /*
2566 * Check for duplicate names in the explicit list of attributes.
2567 *
2568 * Although we might consider merging such entries in the same way that we
2569 * handle name conflicts for inherited attributes, it seems to make more
2570 * sense to assume such conflicts are errors.
2571 *
2572 * We don't use foreach() here because we have two nested loops over the
2573 * columns list, with possible element deletions in the inner one. If we
2574 * used foreach_delete_current() it could only fix up the state of one of
2575 * the loops, so it seems cleaner to use looping over list indexes for
2576 * both loops. Note that any deletion will happen beyond where the outer
2577 * loop is, so its index never needs adjustment.
2578 */
2579 for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2580 {
2581 ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2582
2583 if (!is_partition && coldef->typeName == NULL)
2584 {
2585 /*
2586 * Typed table column option that does not belong to a column from
2587 * the type. This works because the columns from the type come
2588 * first in the list. (We omit this check for partition column
2589 * lists; those are processed separately below.)
2590 */
2591 ereport(ERROR,
2592 (errcode(ERRCODE_UNDEFINED_COLUMN),
2593 errmsg("column \"%s\" does not exist",
2594 coldef->colname)));
2595 }
2596
2597 /* restpos scans all entries beyond coldef; incr is in loop body */
2598 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2599 {
2600 ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2601
2602 if (strcmp(coldef->colname, restdef->colname) == 0)
2603 {
2604 if (coldef->is_from_type)
2605 {
2606 /*
2607 * merge the column options into the column from the type
2608 */
2609 coldef->is_not_null = restdef->is_not_null;
2610 coldef->raw_default = restdef->raw_default;
2611 coldef->cooked_default = restdef->cooked_default;
2612 coldef->constraints = restdef->constraints;
2613 coldef->is_from_type = false;
2614 columns = list_delete_nth_cell(columns, restpos);
2615 }
2616 else
2617 ereport(ERROR,
2618 (errcode(ERRCODE_DUPLICATE_COLUMN),
2619 errmsg("column \"%s\" specified more than once",
2620 coldef->colname)));
2621 }
2622 else
2623 restpos++;
2624 }
2625 }
2626
2627 /*
2628 * In case of a partition, there are no new column definitions, only dummy
2629 * ColumnDefs created for column constraints. Set them aside for now and
2630 * process them at the end.
2631 */
2632 if (is_partition)
2633 {
2634 saved_columns = columns;
2635 columns = NIL;
2636 }
2637
2638 /*
2639 * Scan the parents left-to-right, and merge their attributes to form a
2640 * list of inherited columns (inh_columns).
2641 */
2642 child_attno = 0;
2643 foreach(lc, supers)
2644 {
2645 Oid parent = lfirst_oid(lc);
2646 Relation relation;
2647 TupleDesc tupleDesc;
2648 TupleConstr *constr;
2649 AttrMap *newattmap;
2650 List *inherited_defaults;
2651 List *cols_with_defaults;
2652 List *nnconstrs;
2653 ListCell *lc1;
2654 ListCell *lc2;
2655 Bitmapset *nncols = NULL;
2656
2657 /* caller already got lock */
2658 relation = table_open(parent, NoLock);
2659
2660 /*
2661 * Check for active uses of the parent partitioned table in the
2662 * current transaction, such as being used in some manner by an
2663 * enclosing command.
2664 */
2665 if (is_partition)
2666 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2667
2668 /*
2669 * We do not allow partitioned tables and partitions to participate in
2670 * regular inheritance.
2671 */
2672 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2673 ereport(ERROR,
2674 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2675 errmsg("cannot inherit from partitioned table \"%s\"",
2676 RelationGetRelationName(relation))));
2677 if (relation->rd_rel->relispartition && !is_partition)
2678 ereport(ERROR,
2679 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2680 errmsg("cannot inherit from partition \"%s\"",
2681 RelationGetRelationName(relation))));
2682
2683 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2684 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2685 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2686 ereport(ERROR,
2687 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2688 errmsg("inherited relation \"%s\" is not a table or foreign table",
2689 RelationGetRelationName(relation))));
2690
2691 /*
2692 * If the parent is permanent, so must be all of its partitions. Note
2693 * that inheritance allows that case.
2694 */
2695 if (is_partition &&
2696 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2697 relpersistence == RELPERSISTENCE_TEMP)
2698 ereport(ERROR,
2699 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2700 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2701 RelationGetRelationName(relation))));
2702
2703 /* Permanent rels cannot inherit from temporary ones */
2704 if (relpersistence != RELPERSISTENCE_TEMP &&
2705 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2706 ereport(ERROR,
2707 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2708 errmsg(!is_partition
2709 ? "cannot inherit from temporary relation \"%s\""
2710 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2711 RelationGetRelationName(relation))));
2712
2713 /* If existing rel is temp, it must belong to this session */
2714 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2715 !relation->rd_islocaltemp)
2716 ereport(ERROR,
2717 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2718 errmsg(!is_partition
2719 ? "cannot inherit from temporary relation of another session"
2720 : "cannot create as partition of temporary relation of another session")));
2721
2722 /*
2723 * We should have an UNDER permission flag for this, but for now,
2724 * demand that creator of a child table own the parent.
2725 */
2726 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2728 RelationGetRelationName(relation));
2729
2730 tupleDesc = RelationGetDescr(relation);
2731 constr = tupleDesc->constr;
2732
2733 /*
2734 * newattmap->attnums[] will contain the child-table attribute numbers
2735 * for the attributes of this parent table. (They are not the same
2736 * for parents after the first one, nor if we have dropped columns.)
2737 */
2738 newattmap = make_attrmap(tupleDesc->natts);
2739
2740 /* We can't process inherited defaults until newattmap is complete. */
2741 inherited_defaults = cols_with_defaults = NIL;
2742
2743 /*
2744 * Request attnotnull on columns that have a not-null constraint
2745 * that's not marked NO INHERIT (even if not valid).
2746 */
2747 nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2748 true, false);
2749 foreach_ptr(CookedConstraint, cc, nnconstrs)
2750 nncols = bms_add_member(nncols, cc->attnum);
2751
2752 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2753 parent_attno++)
2754 {
2755 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2756 parent_attno - 1);
2757 char *attributeName = NameStr(attribute->attname);
2758 int exist_attno;
2759 ColumnDef *newdef;
2760 ColumnDef *mergeddef;
2761
2762 /*
2763 * Ignore dropped columns in the parent.
2764 */
2765 if (attribute->attisdropped)
2766 continue; /* leave newattmap->attnums entry as zero */
2767
2768 /*
2769 * Create new column definition
2770 */
2771 newdef = makeColumnDef(attributeName, attribute->atttypid,
2772 attribute->atttypmod, attribute->attcollation);
2773 newdef->storage = attribute->attstorage;
2774 newdef->generated = attribute->attgenerated;
2775 if (CompressionMethodIsValid(attribute->attcompression))
2776 newdef->compression =
2777 pstrdup(GetCompressionMethodName(attribute->attcompression));
2778
2779 /*
2780 * Regular inheritance children are independent enough not to
2781 * inherit identity columns. But partitions are integral part of
2782 * a partitioned table and inherit identity column.
2783 */
2784 if (is_partition)
2785 newdef->identity = attribute->attidentity;
2786
2787 /*
2788 * Does it match some previously considered column from another
2789 * parent?
2790 */
2791 exist_attno = findAttrByName(attributeName, inh_columns);
2792 if (exist_attno > 0)
2793 {
2794 /*
2795 * Yes, try to merge the two column definitions.
2796 */
2797 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2798
2799 newattmap->attnums[parent_attno - 1] = exist_attno;
2800
2801 /*
2802 * Partitions have only one parent, so conflict should never
2803 * occur.
2804 */
2805 Assert(!is_partition);
2806 }
2807 else
2808 {
2809 /*
2810 * No, create a new inherited column
2811 */
2812 newdef->inhcount = 1;
2813 newdef->is_local = false;
2814 inh_columns = lappend(inh_columns, newdef);
2815
2816 newattmap->attnums[parent_attno - 1] = ++child_attno;
2817 mergeddef = newdef;
2818 }
2819
2820 /*
2821 * mark attnotnull if parent has it
2822 */
2823 if (bms_is_member(parent_attno, nncols))
2824 mergeddef->is_not_null = true;
2825
2826 /*
2827 * Locate default/generation expression if any
2828 */
2829 if (attribute->atthasdef)
2830 {
2831 Node *this_default;
2832
2833 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2834 if (this_default == NULL)
2835 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2836 parent_attno, RelationGetRelationName(relation));
2837
2838 /*
2839 * If it's a GENERATED default, it might contain Vars that
2840 * need to be mapped to the inherited column(s)' new numbers.
2841 * We can't do that till newattmap is ready, so just remember
2842 * all the inherited default expressions for the moment.
2843 */
2844 inherited_defaults = lappend(inherited_defaults, this_default);
2845 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2846 }
2847 }
2848
2849 /*
2850 * Now process any inherited default expressions, adjusting attnos
2851 * using the completed newattmap map.
2852 */
2853 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2854 {
2855 Node *this_default = (Node *) lfirst(lc1);
2856 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2857 bool found_whole_row;
2858
2859 /* Adjust Vars to match new table's column numbering */
2860 this_default = map_variable_attnos(this_default,
2861 1, 0,
2862 newattmap,
2863 InvalidOid, &found_whole_row);
2864
2865 /*
2866 * For the moment we have to reject whole-row variables. We could
2867 * convert them, if we knew the new table's rowtype OID, but that
2868 * hasn't been assigned yet. (A variable could only appear in a
2869 * generation expression, so the error message is correct.)
2870 */
2871 if (found_whole_row)
2872 ereport(ERROR,
2873 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2874 errmsg("cannot convert whole-row table reference"),
2875 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2876 def->colname,
2877 RelationGetRelationName(relation))));
2878
2879 /*
2880 * If we already had a default from some prior parent, check to
2881 * see if they are the same. If so, no problem; if not, mark the
2882 * column as having a bogus default. Below, we will complain if
2883 * the bogus default isn't overridden by the child columns.
2884 */
2885 Assert(def->raw_default == NULL);
2886 if (def->cooked_default == NULL)
2887 def->cooked_default = this_default;
2888 else if (!equal(def->cooked_default, this_default))
2889 {
2890 def->cooked_default = &bogus_marker;
2891 have_bogus_defaults = true;
2892 }
2893 }
2894
2895 /*
2896 * Now copy the CHECK constraints of this parent, adjusting attnos
2897 * using the completed newattmap map. Identically named constraints
2898 * are merged if possible, else we throw error.
2899 */
2900 if (constr && constr->num_check > 0)
2901 {
2902 ConstrCheck *check = constr->check;
2903
2904 for (int i = 0; i < constr->num_check; i++)
2905 {
2906 char *name = check[i].ccname;
2907 Node *expr;
2908 bool found_whole_row;
2909
2910 /* ignore if the constraint is non-inheritable */
2911 if (check[i].ccnoinherit)
2912 continue;
2913
2914 /* Adjust Vars to match new table's column numbering */
2915 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2916 1, 0,
2917 newattmap,
2918 InvalidOid, &found_whole_row);
2919
2920 /*
2921 * For the moment we have to reject whole-row variables. We
2922 * could convert them, if we knew the new table's rowtype OID,
2923 * but that hasn't been assigned yet.
2924 */
2925 if (found_whole_row)
2926 ereport(ERROR,
2927 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2928 errmsg("cannot convert whole-row table reference"),
2929 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2930 name,
2931 RelationGetRelationName(relation))));
2932
2933 constraints = MergeCheckConstraint(constraints, name, expr,
2934 check[i].ccenforced);
2935 }
2936 }
2937
2938 /*
2939 * Also copy the not-null constraints from this parent. The
2940 * attnotnull markings were already installed above.
2941 */
2942 foreach_ptr(CookedConstraint, nn, nnconstrs)
2943 {
2944 Assert(nn->contype == CONSTR_NOTNULL);
2945
2946 nn->attnum = newattmap->attnums[nn->attnum - 1];
2947
2948 nnconstraints = lappend(nnconstraints, nn);
2949 }
2950
2951 free_attrmap(newattmap);
2952
2953 /*
2954 * Close the parent rel, but keep our lock on it until xact commit.
2955 * That will prevent someone else from deleting or ALTERing the parent
2956 * before the child is committed.
2957 */
2958 table_close(relation, NoLock);
2959 }
2960
2961 /*
2962 * If we had no inherited attributes, the result columns are just the
2963 * explicitly declared columns. Otherwise, we need to merge the declared
2964 * columns into the inherited column list. Although, we never have any
2965 * explicitly declared columns if the table is a partition.
2966 */
2967 if (inh_columns != NIL)
2968 {
2969 int newcol_attno = 0;
2970
2971 foreach(lc, columns)
2972 {
2973 ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2974 char *attributeName = newdef->colname;
2975 int exist_attno;
2976
2977 /*
2978 * Partitions have only one parent and have no column definitions
2979 * of their own, so conflict should never occur.
2980 */
2981 Assert(!is_partition);
2982
2983 newcol_attno++;
2984
2985 /*
2986 * Does it match some inherited column?
2987 */
2988 exist_attno = findAttrByName(attributeName, inh_columns);
2989 if (exist_attno > 0)
2990 {
2991 /*
2992 * Yes, try to merge the two column definitions.
2993 */
2994 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2995 }
2996 else
2997 {
2998 /*
2999 * No, attach new column unchanged to result columns.
3000 */
3001 inh_columns = lappend(inh_columns, newdef);
3002 }
3003 }
3004
3005 columns = inh_columns;
3006
3007 /*
3008 * Check that we haven't exceeded the legal # of columns after merging
3009 * in inherited columns.
3010 */
3011 if (list_length(columns) > MaxHeapAttributeNumber)
3012 ereport(ERROR,
3013 (errcode(ERRCODE_TOO_MANY_COLUMNS),
3014 errmsg("tables can have at most %d columns",
3016 }
3017
3018 /*
3019 * Now that we have the column definition list for a partition, we can
3020 * check whether the columns referenced in the column constraint specs
3021 * actually exist. Also, merge column defaults.
3022 */
3023 if (is_partition)
3024 {
3025 foreach(lc, saved_columns)
3026 {
3027 ColumnDef *restdef = lfirst(lc);
3028 bool found = false;
3029 ListCell *l;
3030
3031 foreach(l, columns)
3032 {
3033 ColumnDef *coldef = lfirst(l);
3034
3035 if (strcmp(coldef->colname, restdef->colname) == 0)
3036 {
3037 found = true;
3038
3039 /*
3040 * Check for conflicts related to generated columns.
3041 *
3042 * Same rules as above: generated-ness has to match the
3043 * parent, but the contents of the generation expression
3044 * can be different.
3045 */
3046 if (coldef->generated)
3047 {
3048 if (restdef->raw_default && !restdef->generated)
3049 ereport(ERROR,
3050 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3051 errmsg("column \"%s\" inherits from generated column but specifies default",
3052 restdef->colname)));
3053 if (restdef->identity)
3054 ereport(ERROR,
3055 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3056 errmsg("column \"%s\" inherits from generated column but specifies identity",
3057 restdef->colname)));
3058 }
3059 else
3060 {
3061 if (restdef->generated)
3062 ereport(ERROR,
3063 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3064 errmsg("child column \"%s\" specifies generation expression",
3065 restdef->colname),
3066 errhint("A child table column cannot be generated unless its parent column is.")));
3067 }
3068
3069 if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3070 ereport(ERROR,
3071 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3072 errmsg("column \"%s\" inherits from generated column of different kind",
3073 restdef->colname),
3074 errdetail("Parent column is %s, child column is %s.",
3075 coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3076 restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3077
3078 /*
3079 * Override the parent's default value for this column
3080 * (coldef->cooked_default) with the partition's local
3081 * definition (restdef->raw_default), if there's one. It
3082 * should be physically impossible to get a cooked default
3083 * in the local definition or a raw default in the
3084 * inherited definition, but make sure they're nulls, for
3085 * future-proofing.
3086 */
3087 Assert(restdef->cooked_default == NULL);
3088 Assert(coldef->raw_default == NULL);
3089 if (restdef->raw_default)
3090 {
3091 coldef->raw_default = restdef->raw_default;
3092 coldef->cooked_default = NULL;
3093 }
3094 }
3095 }
3096
3097 /* complain for constraints on columns not in parent */
3098 if (!found)
3099 ereport(ERROR,
3100 (errcode(ERRCODE_UNDEFINED_COLUMN),
3101 errmsg("column \"%s\" does not exist",
3102 restdef->colname)));
3103 }
3104 }
3105
3106 /*
3107 * If we found any conflicting parent default values, check to make sure
3108 * they were overridden by the child.
3109 */
3110 if (have_bogus_defaults)
3111 {
3112 foreach(lc, columns)
3113 {
3114 ColumnDef *def = lfirst(lc);
3115
3116 if (def->cooked_default == &bogus_marker)
3117 {
3118 if (def->generated)
3119 ereport(ERROR,
3120 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3121 errmsg("column \"%s\" inherits conflicting generation expressions",
3122 def->colname),
3123 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3124 else
3125 ereport(ERROR,
3126 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3127 errmsg("column \"%s\" inherits conflicting default values",
3128 def->colname),
3129 errhint("To resolve the conflict, specify a default explicitly.")));
3130 }
3131 }
3132 }
3133
3134 *supconstr = constraints;
3135 *supnotnulls = nnconstraints;
3136
3137 return columns;
3138}
AttrMap * make_attrmap(int maplen)
Definition: attmap.c:40
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
List * list_delete_nth_cell(List *list, int n)
Definition: list.c:767
ColumnDef * makeColumnDef(const char *colname, Oid typeOid, int32 typmod, Oid collOid)
Definition: makefuncs.c:565
List * RelationGetNotNullConstraints(Oid relid, bool cooked, bool include_noinh)
#define foreach_ptr(type, var, lst)
Definition: pg_list.h:469
#define list_nth_node(type, list, n)
Definition: pg_list.h:327
List * constraints
Definition: parsenodes.h:754
bool is_from_type
Definition: parsenodes.h:743
char * ccname
Definition: tupdesc.h:30
static ColumnDef * MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3406
static int findAttrByName(const char *attributeName, const List *columns)
Definition: tablecmds.c:3600
static List * MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
Definition: tablecmds.c:3157
static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3236
const char * GetCompressionMethodName(char method)
Node * TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
Definition: tupdesc.c:1085

References aclcheck_error(), ACLCHECK_NOT_OWNER, Assert(), AttrMap::attnums, bms_add_member(), bms_is_member(), ConstrCheck::ccname, TupleConstr::check, CheckTableNotInUse(), ColumnDef::colname, ColumnDef::compression, CompressionMethodIsValid, TupleDescData::constr, CONSTR_NOTNULL, ColumnDef::constraints, ColumnDef::cooked_default, elog, equal(), ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, findAttrByName(), forboth, foreach_ptr, free_attrmap(), ColumnDef::generated, get_relkind_objtype(), GetCompressionMethodName(), GetUserId(), i, ColumnDef::identity, ColumnDef::inhcount, InvalidOid, ColumnDef::is_from_type, ColumnDef::is_local, ColumnDef::is_not_null, lappend(), lfirst, lfirst_node, lfirst_oid, list_delete_nth_cell(), list_length(), list_nth_node, make_attrmap(), makeColumnDef(), map_variable_attnos(), MaxHeapAttributeNumber, MergeCheckConstraint(), MergeChildAttribute(), MergeInheritedAttribute(), name, NameStr, TupleDescData::natts, NIL, NoLock, TupleConstr::num_check, object_ownercheck(), pstrdup(), ColumnDef::raw_default, RelationData::rd_islocaltemp, RelationData::rd_rel, RelationGetDescr, RelationGetNotNullConstraints(), RelationGetRelationName, RelationGetRelid, ColumnDef::storage, stringToNode(), table_close(), table_open(), TupleDescAttr(), TupleDescGetDefault(), and ColumnDef::typeName.

Referenced by DefineRelation().

◆ MergeAttributesIntoExisting()

static void MergeAttributesIntoExisting ( Relation  child_rel,
Relation  parent_rel,
bool  ispartition 
)
static

Definition at line 17423 of file tablecmds.c.

17424{
17425 Relation attrrel;
17426 TupleDesc parent_desc;
17427
17428 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17429 parent_desc = RelationGetDescr(parent_rel);
17430
17431 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17432 {
17433 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17434 char *parent_attname = NameStr(parent_att->attname);
17435 HeapTuple tuple;
17436
17437 /* Ignore dropped columns in the parent. */
17438 if (parent_att->attisdropped)
17439 continue;
17440
17441 /* Find same column in child (matching on column name). */
17442 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17443 if (HeapTupleIsValid(tuple))
17444 {
17445 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17446
17447 if (parent_att->atttypid != child_att->atttypid ||
17448 parent_att->atttypmod != child_att->atttypmod)
17449 ereport(ERROR,
17450 (errcode(ERRCODE_DATATYPE_MISMATCH),
17451 errmsg("child table \"%s\" has different type for column \"%s\"",
17452 RelationGetRelationName(child_rel), parent_attname)));
17453
17454 if (parent_att->attcollation != child_att->attcollation)
17455 ereport(ERROR,
17456 (errcode(ERRCODE_COLLATION_MISMATCH),
17457 errmsg("child table \"%s\" has different collation for column \"%s\"",
17458 RelationGetRelationName(child_rel), parent_attname)));
17459
17460 /*
17461 * If the parent has a not-null constraint that's not NO INHERIT,
17462 * make sure the child has one too.
17463 *
17464 * Other constraints are checked elsewhere.
17465 */
17466 if (parent_att->attnotnull && !child_att->attnotnull)
17467 {
17468 HeapTuple contup;
17469
17470 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17471 parent_att->attnum);
17472 if (HeapTupleIsValid(contup) &&
17473 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17474 ereport(ERROR,
17475 errcode(ERRCODE_DATATYPE_MISMATCH),
17476 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17477 parent_attname, RelationGetRelationName(child_rel)));
17478 }
17479
17480 /*
17481 * Child column must be generated if and only if parent column is.
17482 */
17483 if (parent_att->attgenerated && !child_att->attgenerated)
17484 ereport(ERROR,
17485 (errcode(ERRCODE_DATATYPE_MISMATCH),
17486 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17487 if (child_att->attgenerated && !parent_att->attgenerated)
17488 ereport(ERROR,
17489 (errcode(ERRCODE_DATATYPE_MISMATCH),
17490 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17491
17492 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17493 ereport(ERROR,
17494 (errcode(ERRCODE_DATATYPE_MISMATCH),
17495 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17496 errdetail("Parent column is %s, child column is %s.",
17497 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17498 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17499
17500 /*
17501 * Regular inheritance children are independent enough not to
17502 * inherit identity columns. But partitions are integral part of
17503 * a partitioned table and inherit identity column.
17504 */
17505 if (ispartition)
17506 child_att->attidentity = parent_att->attidentity;
17507
17508 /*
17509 * OK, bump the child column's inheritance count. (If we fail
17510 * later on, this change will just roll back.)
17511 */
17512 if (pg_add_s16_overflow(child_att->attinhcount, 1,
17513 &child_att->attinhcount))
17514 ereport(ERROR,
17515 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17516 errmsg("too many inheritance parents"));
17517
17518 /*
17519 * In case of partitions, we must enforce that value of attislocal
17520 * is same in all partitions. (Note: there are only inherited
17521 * attributes in partitions)
17522 */
17523 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17524 {
17525 Assert(child_att->attinhcount == 1);
17526 child_att->attislocal = false;
17527 }
17528
17529 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17530 heap_freetuple(tuple);
17531 }
17532 else
17533 {
17534 ereport(ERROR,
17535 (errcode(ERRCODE_DATATYPE_MISMATCH),
17536 errmsg("child table is missing column \"%s\"", parent_attname)));
17537 }
17538 }
17539
17540 table_close(attrrel, RowExclusiveLock);
17541}

References Assert(), CatalogTupleUpdate(), ereport, errcode(), errdetail(), errmsg(), ERROR, findNotNullConstraintAttnum(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, NameStr, TupleDescData::natts, pg_add_s16_overflow(), RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), table_open(), and TupleDescAttr().

Referenced by CreateInheritance().

◆ MergeCheckConstraint()

static List * MergeCheckConstraint ( List constraints,
const char *  name,
Node expr,
bool  is_enforced 
)
static

Definition at line 3157 of file tablecmds.c.

3158{
3159 ListCell *lc;
3160 CookedConstraint *newcon;
3161
3162 foreach(lc, constraints)
3163 {
3165
3166 Assert(ccon->contype == CONSTR_CHECK);
3167
3168 /* Non-matching names never conflict */
3169 if (strcmp(ccon->name, name) != 0)
3170 continue;
3171
3172 if (equal(expr, ccon->expr))
3173 {
3174 /* OK to merge constraint with existing */
3175 if (pg_add_s16_overflow(ccon->inhcount, 1,
3176 &ccon->inhcount))
3177 ereport(ERROR,
3178 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3179 errmsg("too many inheritance parents"));
3180
3181 /*
3182 * When enforceability differs, the merged constraint should be
3183 * marked as ENFORCED because one of the parents is ENFORCED.
3184 */
3185 if (!ccon->is_enforced && is_enforced)
3186 {
3187 ccon->is_enforced = true;
3188 ccon->skip_validation = false;
3189 }
3190
3191 return constraints;
3192 }
3193
3194 ereport(ERROR,
3196 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3197 name)));
3198 }
3199
3200 /*
3201 * Constraint couldn't be merged with an existing one and also didn't
3202 * conflict with an existing one, so add it as a new one to the list.
3203 */
3205 newcon->contype = CONSTR_CHECK;
3206 newcon->name = pstrdup(name);
3207 newcon->expr = expr;
3208 newcon->inhcount = 1;
3209 newcon->is_enforced = is_enforced;
3210 newcon->skip_validation = !is_enforced;
3211 return lappend(constraints, newcon);
3212}
#define palloc0_object(type)
Definition: fe_memutils.h:75

References Assert(), CONSTR_CHECK, CookedConstraint::contype, equal(), ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, CookedConstraint::expr, CookedConstraint::inhcount, CookedConstraint::is_enforced, lappend(), lfirst, name, CookedConstraint::name, palloc0_object, pg_add_s16_overflow(), pstrdup(), and CookedConstraint::skip_validation.

Referenced by MergeAttributes().

◆ MergeChildAttribute()

static void MergeChildAttribute ( List inh_columns,
int  exist_attno,
int  newcol_attno,
const ColumnDef newdef 
)
static

Definition at line 3236 of file tablecmds.c.

3237{
3238 char *attributeName = newdef->colname;
3239 ColumnDef *inhdef;
3240 Oid inhtypeid,
3241 newtypeid;
3242 int32 inhtypmod,
3243 newtypmod;
3244 Oid inhcollid,
3245 newcollid;
3246
3247 if (exist_attno == newcol_attno)
3249 (errmsg("merging column \"%s\" with inherited definition",
3250 attributeName)));
3251 else
3253 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3254 errdetail("User-specified column moved to the position of the inherited column.")));
3255
3256 inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3257
3258 /*
3259 * Must have the same type and typmod
3260 */
3261 typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3262 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3263 if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3264 ereport(ERROR,
3265 (errcode(ERRCODE_DATATYPE_MISMATCH),
3266 errmsg("column \"%s\" has a type conflict",
3267 attributeName),
3268 errdetail("%s versus %s",
3269 format_type_with_typemod(inhtypeid, inhtypmod),
3270 format_type_with_typemod(newtypeid, newtypmod))));
3271
3272 /*
3273 * Must have the same collation
3274 */
3275 inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3276 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3277 if (inhcollid != newcollid)
3278 ereport(ERROR,
3279 (errcode(ERRCODE_COLLATION_MISMATCH),
3280 errmsg("column \"%s\" has a collation conflict",
3281 attributeName),
3282 errdetail("\"%s\" versus \"%s\"",
3283 get_collation_name(inhcollid),
3284 get_collation_name(newcollid))));
3285
3286 /*
3287 * Identity is never inherited by a regular inheritance child. Pick
3288 * child's identity definition if there's one.
3289 */
3290 inhdef->identity = newdef->identity;
3291
3292 /*
3293 * Copy storage parameter
3294 */
3295 if (inhdef->storage == 0)
3296 inhdef->storage = newdef->storage;
3297 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3298 ereport(ERROR,
3299 (errcode(ERRCODE_DATATYPE_MISMATCH),
3300 errmsg("column \"%s\" has a storage parameter conflict",
3301 attributeName),
3302 errdetail("%s versus %s",
3303 storage_name(inhdef->storage),
3304 storage_name(newdef->storage))));
3305
3306 /*
3307 * Copy compression parameter
3308 */
3309 if (inhdef->compression == NULL)
3310 inhdef->compression = newdef->compression;
3311 else if (newdef->compression != NULL)
3312 {
3313 if (strcmp(inhdef->compression, newdef->compression) != 0)
3314 ereport(ERROR,
3315 (errcode(ERRCODE_DATATYPE_MISMATCH),
3316 errmsg("column \"%s\" has a compression method conflict",
3317 attributeName),
3318 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3319 }
3320
3321 /*
3322 * Merge of not-null constraints = OR 'em together
3323 */
3324 inhdef->is_not_null |= newdef->is_not_null;
3325
3326 /*
3327 * Check for conflicts related to generated columns.
3328 *
3329 * If the parent column is generated, the child column will be made a
3330 * generated column if it isn't already. If it is a generated column,
3331 * we'll take its generation expression in preference to the parent's. We
3332 * must check that the child column doesn't specify a default value or
3333 * identity, which matches the rules for a single column in
3334 * parse_utilcmd.c.
3335 *
3336 * Conversely, if the parent column is not generated, the child column
3337 * can't be either. (We used to allow that, but it results in being able
3338 * to override the generation expression via UPDATEs through the parent.)
3339 */
3340 if (inhdef->generated)
3341 {
3342 if (newdef->raw_default && !newdef->generated)
3343 ereport(ERROR,
3344 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3345 errmsg("column \"%s\" inherits from generated column but specifies default",
3346 inhdef->colname)));
3347 if (newdef->identity)
3348 ereport(ERROR,
3349 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3350 errmsg("column \"%s\" inherits from generated column but specifies identity",
3351 inhdef->colname)));
3352 }
3353 else
3354 {
3355 if (newdef->generated)
3356 ereport(ERROR,
3357 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3358 errmsg("child column \"%s\" specifies generation expression",
3359 inhdef->colname),
3360 errhint("A child table column cannot be generated unless its parent column is.")));
3361 }
3362
3363 if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3364 ereport(ERROR,
3365 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3366 errmsg("column \"%s\" inherits from generated column of different kind",
3367 inhdef->colname),
3368 errdetail("Parent column is %s, child column is %s.",
3369 inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3370 newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3371
3372 /*
3373 * If new def has a default, override previous default
3374 */
3375 if (newdef->raw_default != NULL)
3376 {
3377 inhdef->raw_default = newdef->raw_default;
3378 inhdef->cooked_default = newdef->cooked_default;
3379 }
3380
3381 /* Mark the column as locally defined */
3382 inhdef->is_local = true;
3383}
static const char * storage_name(char c)
Definition: tablecmds.c:2451

References ColumnDef::colname, ColumnDef::compression, ColumnDef::cooked_default, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, format_type_with_typemod(), ColumnDef::generated, get_collation_name(), GetColumnDefCollation(), ColumnDef::identity, ColumnDef::is_local, ColumnDef::is_not_null, list_nth_node, NOTICE, ColumnDef::raw_default, ColumnDef::storage, storage_name(), ColumnDef::typeName, and typenameTypeIdAndMod().

Referenced by MergeAttributes().

◆ MergeConstraintsIntoExisting()

static void MergeConstraintsIntoExisting ( Relation  child_rel,
Relation  parent_rel 
)
static

Definition at line 17561 of file tablecmds.c.

17562{
17563 Relation constraintrel;
17564 SysScanDesc parent_scan;
17565 ScanKeyData parent_key;
17566 HeapTuple parent_tuple;
17567 Oid parent_relid = RelationGetRelid(parent_rel);
17568 AttrMap *attmap;
17569
17570 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17571
17572 /* Outer loop scans through the parent's constraint definitions */
17573 ScanKeyInit(&parent_key,
17574 Anum_pg_constraint_conrelid,
17575 BTEqualStrategyNumber, F_OIDEQ,
17576 ObjectIdGetDatum(parent_relid));
17577 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17578 true, NULL, 1, &parent_key);
17579
17580 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17581 RelationGetDescr(child_rel),
17582 true);
17583
17584 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17585 {
17586 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17587 SysScanDesc child_scan;
17588 ScanKeyData child_key;
17589 HeapTuple child_tuple;
17590 AttrNumber parent_attno;
17591 bool found = false;
17592
17593 if (parent_con->contype != CONSTRAINT_CHECK &&
17594 parent_con->contype != CONSTRAINT_NOTNULL)
17595 continue;
17596
17597 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17598 if (parent_con->connoinherit)
17599 continue;
17600
17601 if (parent_con->contype == CONSTRAINT_NOTNULL)
17602 parent_attno = extractNotNullColumn(parent_tuple);
17603 else
17604 parent_attno = InvalidAttrNumber;
17605
17606 /* Search for a child constraint matching this one */
17607 ScanKeyInit(&child_key,
17608 Anum_pg_constraint_conrelid,
17609 BTEqualStrategyNumber, F_OIDEQ,
17611 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17612 true, NULL, 1, &child_key);
17613
17614 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17615 {
17616 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17617 HeapTuple child_copy;
17618
17619 if (child_con->contype != parent_con->contype)
17620 continue;
17621
17622 /*
17623 * CHECK constraint are matched by constraint name, NOT NULL ones
17624 * by attribute number.
17625 */
17626 if (child_con->contype == CONSTRAINT_CHECK)
17627 {
17628 if (strcmp(NameStr(parent_con->conname),
17629 NameStr(child_con->conname)) != 0)
17630 continue;
17631 }
17632 else if (child_con->contype == CONSTRAINT_NOTNULL)
17633 {
17634 Form_pg_attribute parent_attr;
17635 Form_pg_attribute child_attr;
17636 AttrNumber child_attno;
17637
17638 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17639 child_attno = extractNotNullColumn(child_tuple);
17640 if (parent_attno != attmap->attnums[child_attno - 1])
17641 continue;
17642
17643 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17644 /* there shouldn't be constraints on dropped columns */
17645 if (parent_attr->attisdropped || child_attr->attisdropped)
17646 elog(ERROR, "found not-null constraint on dropped columns");
17647 }
17648
17649 if (child_con->contype == CONSTRAINT_CHECK &&
17650 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17651 ereport(ERROR,
17652 (errcode(ERRCODE_DATATYPE_MISMATCH),
17653 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17654 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17655
17656 /*
17657 * If the child constraint is "no inherit" then cannot merge
17658 */
17659 if (child_con->connoinherit)
17660 ereport(ERROR,
17661 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17662 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17663 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17664
17665 /*
17666 * If the child constraint is "not valid" then cannot merge with a
17667 * valid parent constraint
17668 */
17669 if (parent_con->convalidated && child_con->conenforced &&
17670 !child_con->convalidated)
17671 ereport(ERROR,
17672 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17673 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17674 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17675
17676 /*
17677 * A NOT ENFORCED child constraint cannot be merged with an
17678 * ENFORCED parent constraint. However, the reverse is allowed,
17679 * where the child constraint is ENFORCED.
17680 */
17681 if (parent_con->conenforced && !child_con->conenforced)
17682 ereport(ERROR,
17683 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17684 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17685 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17686
17687 /*
17688 * OK, bump the child constraint's inheritance count. (If we fail
17689 * later on, this change will just roll back.)
17690 */
17691 child_copy = heap_copytuple(child_tuple);
17692 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17693
17694 if (pg_add_s16_overflow(child_con->coninhcount, 1,
17695 &child_con->coninhcount))
17696 ereport(ERROR,
17697 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17698 errmsg("too many inheritance parents"));
17699
17700 /*
17701 * In case of partitions, an inherited constraint must be
17702 * inherited only once since it cannot have multiple parents and
17703 * it is never considered local.
17704 */
17705 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17706 {
17707 Assert(child_con->coninhcount == 1);
17708 child_con->conislocal = false;
17709 }
17710
17711 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17712 heap_freetuple(child_copy);
17713
17714 found = true;
17715 break;
17716 }
17717
17718 systable_endscan(child_scan);
17719
17720 if (!found)
17721 {
17722 if (parent_con->contype == CONSTRAINT_NOTNULL)
17723 ereport(ERROR,
17724 errcode(ERRCODE_DATATYPE_MISMATCH),
17725 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17726 get_attname(parent_relid,
17727 extractNotNullColumn(parent_tuple),
17728 false),
17729 RelationGetRelationName(child_rel)));
17730
17731 ereport(ERROR,
17732 (errcode(ERRCODE_DATATYPE_MISMATCH),
17733 errmsg("child table is missing constraint \"%s\"",
17734 NameStr(parent_con->conname))));
17735 }
17736 }
17737
17738 systable_endscan(parent_scan);
17739 table_close(constraintrel, RowExclusiveLock);
17740}
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
Definition: tablecmds.c:17394

References Assert(), AttrMap::attnums, BTEqualStrategyNumber, build_attrmap_by_name(), CatalogTupleUpdate(), constraints_equivalent(), elog, ereport, errcode(), errmsg(), ERROR, extractNotNullColumn(), get_attname(), GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvalidAttrNumber, NameStr, ObjectIdGetDatum(), pg_add_s16_overflow(), RelationData::rd_att, RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), table_open(), and TupleDescAttr().

Referenced by CreateInheritance().

◆ MergeInheritedAttribute()

static ColumnDef * MergeInheritedAttribute ( List inh_columns,
int  exist_attno,
const ColumnDef newdef 
)
static

Definition at line 3406 of file tablecmds.c.

3409{
3410 char *attributeName = newdef->colname;
3411 ColumnDef *prevdef;
3412 Oid prevtypeid,
3413 newtypeid;
3414 int32 prevtypmod,
3415 newtypmod;
3416 Oid prevcollid,
3417 newcollid;
3418
3420 (errmsg("merging multiple inherited definitions of column \"%s\"",
3421 attributeName)));
3422 prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3423
3424 /*
3425 * Must have the same type and typmod
3426 */
3427 typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3428 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3429 if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3430 ereport(ERROR,
3431 (errcode(ERRCODE_DATATYPE_MISMATCH),
3432 errmsg("inherited column \"%s\" has a type conflict",
3433 attributeName),
3434 errdetail("%s versus %s",
3435 format_type_with_typemod(prevtypeid, prevtypmod),
3436 format_type_with_typemod(newtypeid, newtypmod))));
3437
3438 /*
3439 * Must have the same collation
3440 */
3441 prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3442 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3443 if (prevcollid != newcollid)
3444 ereport(ERROR,
3445 (errcode(ERRCODE_COLLATION_MISMATCH),
3446 errmsg("inherited column \"%s\" has a collation conflict",
3447 attributeName),
3448 errdetail("\"%s\" versus \"%s\"",
3449 get_collation_name(prevcollid),
3450 get_collation_name(newcollid))));
3451
3452 /*
3453 * Copy/check storage parameter
3454 */
3455 if (prevdef->storage == 0)
3456 prevdef->storage = newdef->storage;
3457 else if (prevdef->storage != newdef->storage)
3458 ereport(ERROR,
3459 (errcode(ERRCODE_DATATYPE_MISMATCH),
3460 errmsg("inherited column \"%s\" has a storage parameter conflict",
3461 attributeName),
3462 errdetail("%s versus %s",
3463 storage_name(prevdef->storage),
3464 storage_name(newdef->storage))));
3465
3466 /*
3467 * Copy/check compression parameter
3468 */
3469 if (prevdef->compression == NULL)
3470 prevdef->compression = newdef->compression;
3471 else if (newdef->compression != NULL)
3472 {
3473 if (strcmp(prevdef->compression, newdef->compression) != 0)
3474 ereport(ERROR,
3475 (errcode(ERRCODE_DATATYPE_MISMATCH),
3476 errmsg("column \"%s\" has a compression method conflict",
3477 attributeName),
3478 errdetail("%s versus %s",
3479 prevdef->compression, newdef->compression)));
3480 }
3481
3482 /*
3483 * Check for GENERATED conflicts
3484 */
3485 if (prevdef->generated != newdef->generated)
3486 ereport(ERROR,
3487 (errcode(ERRCODE_DATATYPE_MISMATCH),
3488 errmsg("inherited column \"%s\" has a generation conflict",
3489 attributeName)));
3490
3491 /*
3492 * Default and other constraints are handled by the caller.
3493 */
3494
3495 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3496 &prevdef->inhcount))
3497 ereport(ERROR,
3498 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3499 errmsg("too many inheritance parents"));
3500
3501 return prevdef;
3502}

References ColumnDef::colname, ColumnDef::compression, ereport, errcode(), errdetail(), errmsg(), ERROR, format_type_with_typemod(), ColumnDef::generated, get_collation_name(), GetColumnDefCollation(), ColumnDef::inhcount, list_nth_node, NOTICE, pg_add_s16_overflow(), ColumnDef::storage, storage_name(), ColumnDef::typeName, and typenameTypeIdAndMod().

Referenced by MergeAttributes().

◆ NotNullImpliedByRelConstraints()

static bool NotNullImpliedByRelConstraints ( Relation  rel,
Form_pg_attribute  attr 
)
static

Definition at line 8079 of file tablecmds.c.

8080{
8081 NullTest *nnulltest = makeNode(NullTest);
8082
8083 nnulltest->arg = (Expr *) makeVar(1,
8084 attr->attnum,
8085 attr->atttypid,
8086 attr->atttypmod,
8087 attr->attcollation,
8088 0);
8089 nnulltest->nulltesttype = IS_NOT_NULL;
8090
8091 /*
8092 * argisrow = false is correct even for a composite column, because
8093 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8094 * case, just IS DISTINCT FROM NULL.
8095 */
8096 nnulltest->argisrow = false;
8097 nnulltest->location = -1;
8098
8099 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8100 {
8102 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8103 RelationGetRelationName(rel), NameStr(attr->attname))));
8104 return true;
8105 }
8106
8107 return false;
8108}
@ IS_NOT_NULL
Definition: primnodes.h:1957
NullTestType nulltesttype
Definition: primnodes.h:1964
ParseLoc location
Definition: primnodes.h:1967
Expr * arg
Definition: primnodes.h:1963
static bool ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
Definition: tablecmds.c:20027

References NullTest::arg, ConstraintImpliedByRelConstraint(), DEBUG1, ereport, errmsg_internal(), IS_NOT_NULL, list_make1, NullTest::location, makeNode, makeVar(), NameStr, NIL, NullTest::nulltesttype, and RelationGetRelationName.

Referenced by set_attnotnull().

◆ PartConstraintImpliedByRelConstraint()

bool PartConstraintImpliedByRelConstraint ( Relation  scanrel,
List partConstraint 
)

Definition at line 19972 of file tablecmds.c.

19974{
19975 List *existConstraint = NIL;
19976 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19977 int i;
19978
19979 if (constr && constr->has_not_null)
19980 {
19981 int natts = scanrel->rd_att->natts;
19982
19983 for (i = 1; i <= natts; i++)
19984 {
19985 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
19986
19987 /* invalid not-null constraint must be ignored here */
19988 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
19989 {
19990 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
19991 NullTest *ntest = makeNode(NullTest);
19992
19993 ntest->arg = (Expr *) makeVar(1,
19994 i,
19995 wholeatt->atttypid,
19996 wholeatt->atttypmod,
19997 wholeatt->attcollation,
19998 0);
19999 ntest->nulltesttype = IS_NOT_NULL;
20000
20001 /*
20002 * argisrow=false is correct even for a composite column,
20003 * because attnotnull does not represent a SQL-spec IS NOT
20004 * NULL test in such a case, just IS DISTINCT FROM NULL.
20005 */
20006 ntest->argisrow = false;
20007 ntest->location = -1;
20008 existConstraint = lappend(existConstraint, ntest);
20009 }
20010 }
20011 }
20012
20013 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20014}

References NullTest::arg, CompactAttribute::attisdropped, CompactAttribute::attnullability, ATTNULLABLE_VALID, ConstraintImpliedByRelConstraint(), TupleConstr::has_not_null, i, IS_NOT_NULL, lappend(), NullTest::location, makeNode, makeVar(), TupleDescData::natts, NIL, NullTest::nulltesttype, RelationData::rd_att, RelationGetDescr, TupleDescAttr(), and TupleDescCompactAttr().

Referenced by check_default_partition_contents(), DetachAddConstraintIfNeeded(), and QueuePartitionConstraintValidation().

◆ PreCommit_on_commit_actions()

void PreCommit_on_commit_actions ( void  )

Definition at line 19243 of file tablecmds.c.

19244{
19245 ListCell *l;
19246 List *oids_to_truncate = NIL;
19247 List *oids_to_drop = NIL;
19248
19249 foreach(l, on_commits)
19250 {
19251 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19252
19253 /* Ignore entry if already dropped in this xact */
19255 continue;
19256
19257 switch (oc->oncommit)
19258 {
19259 case ONCOMMIT_NOOP:
19261 /* Do nothing (there shouldn't be such entries, actually) */
19262 break;
19264
19265 /*
19266 * If this transaction hasn't accessed any temporary
19267 * relations, we can skip truncating ON COMMIT DELETE ROWS
19268 * tables, as they must still be empty.
19269 */
19271 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19272 break;
19273 case ONCOMMIT_DROP:
19274 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19275 break;
19276 }
19277 }
19278
19279 /*
19280 * Truncate relations before dropping so that all dependencies between
19281 * relations are removed after they are worked on. Doing it like this
19282 * might be a waste as it is possible that a relation being truncated will
19283 * be dropped anyway due to its parent being dropped, but this makes the
19284 * code more robust because of not having to re-check that the relation
19285 * exists at truncation time.
19286 */
19287 if (oids_to_truncate != NIL)
19288 heap_truncate(oids_to_truncate);
19289
19290 if (oids_to_drop != NIL)
19291 {
19292 ObjectAddresses *targetObjects = new_object_addresses();
19293
19294 foreach(l, oids_to_drop)
19295 {
19296 ObjectAddress object;
19297
19298 object.classId = RelationRelationId;
19299 object.objectId = lfirst_oid(l);
19300 object.objectSubId = 0;
19301
19302 Assert(!object_address_present(&object, targetObjects));
19303
19304 add_exact_object_address(&object, targetObjects);
19305 }
19306
19307 /*
19308 * Object deletion might involve toast table access (to clean up
19309 * toasted catalog entries), so ensure we have a valid snapshot.
19310 */
19312
19313 /*
19314 * Since this is an automatic drop, rather than one directly initiated
19315 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19316 */
19319
19321
19322#ifdef USE_ASSERT_CHECKING
19323
19324 /*
19325 * Note that table deletion will call remove_on_commit_action, so the
19326 * entry should get marked as deleted.
19327 */
19328 foreach(l, on_commits)
19329 {
19330 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19331
19332 if (oc->oncommit != ONCOMMIT_DROP)
19333 continue;
19334
19336 }
19337#endif
19338 }
19339}
#define PERFORM_DELETION_QUIETLY
Definition: dependency.h:94
void heap_truncate(List *relids)
Definition: heap.c:3493
@ ONCOMMIT_DELETE_ROWS
Definition: primnodes.h:60
@ ONCOMMIT_PRESERVE_ROWS
Definition: primnodes.h:59
@ ONCOMMIT_DROP
Definition: primnodes.h:61
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:271
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:669
OnCommitAction oncommit
Definition: tablecmds.c:118
int MyXactFlags
Definition: xact.c:136
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:102

References add_exact_object_address(), Assert(), ObjectAddress::classId, OnCommitItem::deleting_subid, DROP_CASCADE, GetTransactionSnapshot(), heap_truncate(), InvalidSubTransactionId, lappend_oid(), lfirst, lfirst_oid, MyXactFlags, new_object_addresses(), NIL, object_address_present(), on_commits, OnCommitItem::oncommit, ONCOMMIT_DELETE_ROWS, ONCOMMIT_DROP, ONCOMMIT_NOOP, ONCOMMIT_PRESERVE_ROWS, PERFORM_DELETION_INTERNAL, PERFORM_DELETION_QUIETLY, performMultipleDeletions(), PopActiveSnapshot(), PushActiveSnapshot(), OnCommitItem::relid, and XACT_FLAGS_ACCESSEDTEMPNAMESPACE.

Referenced by CommitTransaction(), and PrepareTransaction().

◆ QueueCheckConstraintValidation()

static void QueueCheckConstraintValidation ( List **  wqueue,
Relation  conrel,
Relation  rel,
char *  constrName,
HeapTuple  contuple,
bool  recurse,
bool  recursing,
LOCKMODE  lockmode 
)
static

Definition at line 13066 of file tablecmds.c.

13069{
13071 AlteredTableInfo *tab;
13072 HeapTuple copyTuple;
13073 Form_pg_constraint copy_con;
13074
13075 List *children = NIL;
13076 ListCell *child;
13077 NewConstraint *newcon;
13078 Datum val;
13079 char *conbin;
13080
13081 con = (Form_pg_constraint) GETSTRUCT(contuple);
13082 Assert(con->contype == CONSTRAINT_CHECK);
13083
13084 /*
13085 * If we're recursing, the parent has already done this, so skip it. Also,
13086 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13087 * for it in the children.
13088 */
13089 if (!recursing && !con->connoinherit)
13090 children = find_all_inheritors(RelationGetRelid(rel),
13091 lockmode, NULL);
13092
13093 /*
13094 * For CHECK constraints, we must ensure that we only mark the constraint
13095 * as validated on the parent if it's already validated on the children.
13096 *
13097 * We recurse before validating on the parent, to reduce risk of
13098 * deadlocks.
13099 */
13100 foreach(child, children)
13101 {
13102 Oid childoid = lfirst_oid(child);
13103 Relation childrel;
13104
13105 if (childoid == RelationGetRelid(rel))
13106 continue;
13107
13108 /*
13109 * If we are told not to recurse, there had better not be any child
13110 * tables, because we can't mark the constraint on the parent valid
13111 * unless it is valid for all child tables.
13112 */
13113 if (!recurse)
13114 ereport(ERROR,
13115 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13116 errmsg("constraint must be validated on child tables too")));
13117
13118 /* find_all_inheritors already got lock */
13119 childrel = table_open(childoid, NoLock);
13120
13121 ATExecValidateConstraint(wqueue, childrel, constrName, false,
13122 true, lockmode);
13123 table_close(childrel, NoLock);
13124 }
13125
13126 /* Queue validation for phase 3 */
13127 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13128 newcon->name = constrName;
13129 newcon->contype = CONSTR_CHECK;
13130 newcon->refrelid = InvalidOid;
13131 newcon->refindid = InvalidOid;
13132 newcon->conid = con->oid;
13133
13134 val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13135 Anum_pg_constraint_conbin);
13136 conbin = TextDatumGetCString(val);
13137 newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13138
13139 /* Find or create work queue entry for this table */
13140 tab = ATGetQueueEntry(wqueue, rel);
13141 tab->constraints = lappend(tab->constraints, newcon);
13142
13143 /*
13144 * Invalidate relcache so that others see the new validated constraint.
13145 */
13147
13148 /*
13149 * Now update the catalog, while we have the door open.
13150 */
13151 copyTuple = heap_copytuple(contuple);
13152 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13153 copy_con->convalidated = true;
13154 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13155
13156 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13157
13158 heap_freetuple(copyTuple);
13159}
long val
Definition: informix.c:689
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:631

References Assert(), ATExecValidateConstraint(), ATGetQueueEntry(), CacheInvalidateRelcache(), CatalogTupleUpdate(), NewConstraint::conid, CONSTR_CHECK, AlteredTableInfo::constraints, NewConstraint::contype, ereport, errcode(), errmsg(), ERROR, expand_generated_columns_in_expr(), find_all_inheritors(), GETSTRUCT(), heap_copytuple(), heap_freetuple(), InvalidOid, InvokeObjectPostAlterHook, lappend(), lfirst_oid, NewConstraint::name, NIL, NoLock, palloc0(), NewConstraint::qual, NewConstraint::refindid, NewConstraint::refrelid, RelationGetRelid, stringToNode(), SysCacheGetAttrNotNull(), HeapTupleData::t_self, table_close(), table_open(), TextDatumGetCString, and val.

Referenced by ATExecValidateConstraint().

◆ QueueFKConstraintValidation()

static void QueueFKConstraintValidation ( List **  wqueue,
Relation  conrel,
Relation  rel,
HeapTuple  contuple,
LOCKMODE  lockmode 
)
static

Definition at line 12966 of file tablecmds.c.

12968{
12970 AlteredTableInfo *tab;
12971 HeapTuple copyTuple;
12972 Form_pg_constraint copy_con;
12973
12974 con = (Form_pg_constraint) GETSTRUCT(contuple);
12975 Assert(con->contype == CONSTRAINT_FOREIGN);
12976 Assert(!con->convalidated);
12977
12978 if (rel->rd_rel->relkind == RELKIND_RELATION)
12979 {
12980 NewConstraint *newcon;
12981 Constraint *fkconstraint;
12982
12983 /* Queue validation for phase 3 */
12984 fkconstraint = makeNode(Constraint);
12985 /* for now this is all we need */
12986 fkconstraint->conname = pstrdup(NameStr(con->conname));
12987
12988 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12989 newcon->name = fkconstraint->conname;
12990 newcon->contype = CONSTR_FOREIGN;
12991 newcon->refrelid = con->confrelid;
12992 newcon->refindid = con->conindid;
12993 newcon->conid = con->oid;
12994 newcon->qual = (Node *) fkconstraint;
12995
12996 /* Find or create work queue entry for this table */
12997 tab = ATGetQueueEntry(wqueue, rel);
12998 tab->constraints = lappend(tab->constraints, newcon);
12999 }
13000
13001 /*
13002 * If the table at either end of the constraint is partitioned, we need to
13003 * recurse and handle every constraint that is a child of this constraint.
13004 */
13005 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13006 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13007 {
13008 ScanKeyData pkey;
13009 SysScanDesc pscan;
13010 HeapTuple childtup;
13011
13012 ScanKeyInit(&pkey,
13013 Anum_pg_constraint_conparentid,
13014 BTEqualStrategyNumber, F_OIDEQ,
13015 ObjectIdGetDatum(con->oid));
13016
13017 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13018 true, NULL, 1, &pkey);
13019
13020 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13021 {
13022 Form_pg_constraint childcon;
13023 Relation childrel;
13024
13025 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13026
13027 /*
13028 * If the child constraint has already been validated, no further
13029 * action is required for it or its descendants, as they are all
13030 * valid.
13031 */
13032 if (childcon->convalidated)
13033 continue;
13034
13035 childrel = table_open(childcon->conrelid, lockmode);
13036
13037 QueueFKConstraintValidation(wqueue, conrel, childrel, childtup,
13038 lockmode);
13039 table_close(childrel, NoLock);
13040 }
13041
13042 systable_endscan(pscan);
13043 }
13044
13045 /*
13046 * Now update the catalog, while we have the door open.
13047 */
13048 copyTuple = heap_copytuple(contuple);
13049 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13050 copy_con->convalidated = true;
13051 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13052
13053 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13054
13055 heap_freetuple(copyTuple);
13056}

References Assert(), ATGetQueueEntry(), BTEqualStrategyNumber, CatalogTupleUpdate(), NewConstraint::conid, Constraint::conname, CONSTR_FOREIGN, AlteredTableInfo::constraints, NewConstraint::contype, get_rel_relkind(), GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, lappend(), makeNode, NewConstraint::name, NameStr, NoLock, ObjectIdGetDatum(), palloc0(), pstrdup(), NewConstraint::qual, QueueFKConstraintValidation(), RelationData::rd_rel, NewConstraint::refindid, NewConstraint::refrelid, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecValidateConstraint(), AttachPartitionForeignKey(), and QueueFKConstraintValidation().

◆ QueueNNConstraintValidation()

static void QueueNNConstraintValidation ( List **  wqueue,
Relation  conrel,
Relation  rel,
HeapTuple  contuple,
bool  recurse,
bool  recursing,
LOCKMODE  lockmode 
)
static

Definition at line 13169 of file tablecmds.c.

13172{
13174 AlteredTableInfo *tab;
13175 HeapTuple copyTuple;
13176 Form_pg_constraint copy_con;
13177 List *children = NIL;
13179 char *colname;
13180
13181 con = (Form_pg_constraint) GETSTRUCT(contuple);
13182 Assert(con->contype == CONSTRAINT_NOTNULL);
13183
13184 attnum = extractNotNullColumn(contuple);
13185
13186 /*
13187 * If we're recursing, we've already done this for parent, so skip it.
13188 * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13189 * look for it in the children.
13190 *
13191 * We recurse before validating on the parent, to reduce risk of
13192 * deadlocks.
13193 */
13194 if (!recursing && !con->connoinherit)
13195 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13196
13197 colname = get_attname(RelationGetRelid(rel), attnum, false);
13198 foreach_oid(childoid, children)
13199 {
13200 Relation childrel;
13201 HeapTuple contup;
13202 Form_pg_constraint childcon;
13203 char *conname;
13204
13205 if (childoid == RelationGetRelid(rel))
13206 continue;
13207
13208 /*
13209 * If we are told not to recurse, there had better not be any child
13210 * tables, because we can't mark the constraint on the parent valid
13211 * unless it is valid for all child tables.
13212 */
13213 if (!recurse)
13214 ereport(ERROR,
13215 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13216 errmsg("constraint must be validated on child tables too"));
13217
13218 /*
13219 * The column on child might have a different attnum, so search by
13220 * column name.
13221 */
13222 contup = findNotNullConstraint(childoid, colname);
13223 if (!contup)
13224 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13225 colname, get_rel_name(childoid));
13226 childcon = (Form_pg_constraint) GETSTRUCT(contup);
13227 if (childcon->convalidated)
13228 continue;
13229
13230 /* find_all_inheritors already got lock */
13231 childrel = table_open(childoid, NoLock);
13232 conname = pstrdup(NameStr(childcon->conname));
13233
13234 /* XXX improve ATExecValidateConstraint API to avoid double search */
13235 ATExecValidateConstraint(wqueue, childrel, conname,
13236 false, true, lockmode);
13237 table_close(childrel, NoLock);
13238 }
13239
13240 /* Set attnotnull appropriately without queueing another validation */
13241 set_attnotnull(NULL, rel, attnum, true, false);
13242
13243 tab = ATGetQueueEntry(wqueue, rel);
13244 tab->verify_new_notnull = true;
13245
13246 /*
13247 * Invalidate relcache so that others see the new validated constraint.
13248 */
13250
13251 /*
13252 * Now update the catalogs, while we have the door open.
13253 */
13254 copyTuple = heap_copytuple(contuple);
13255 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13256 copy_con->convalidated = true;
13257 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13258
13259 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13260
13261 heap_freetuple(copyTuple);
13262}

References Assert(), ATExecValidateConstraint(), ATGetQueueEntry(), attnum, CacheInvalidateRelcache(), CatalogTupleUpdate(), elog, ereport, errcode(), errmsg(), ERROR, extractNotNullColumn(), find_all_inheritors(), findNotNullConstraint(), foreach_oid, get_attname(), get_rel_name(), GETSTRUCT(), heap_copytuple(), heap_freetuple(), InvokeObjectPostAlterHook, NameStr, NIL, NoLock, pstrdup(), RelationGetRelid, set_attnotnull(), HeapTupleData::t_self, table_close(), table_open(), and AlteredTableInfo::verify_new_notnull.

Referenced by ATExecValidateConstraint().

◆ QueuePartitionConstraintValidation()

static void QueuePartitionConstraintValidation ( List **  wqueue,
Relation  scanrel,
List partConstraint,
bool  validate_default 
)
static

Definition at line 20090 of file tablecmds.c.

20093{
20094 /*
20095 * Based on the table's existing constraints, determine whether or not we
20096 * may skip scanning the table.
20097 */
20098 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20099 {
20100 if (!validate_default)
20102 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20103 RelationGetRelationName(scanrel))));
20104 else
20106 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20107 RelationGetRelationName(scanrel))));
20108 return;
20109 }
20110
20111 /*
20112 * Constraints proved insufficient. For plain relations, queue a
20113 * validation item now; for partitioned tables, recurse to process each
20114 * partition.
20115 */
20116 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20117 {
20118 AlteredTableInfo *tab;
20119
20120 /* Grab a work queue entry. */
20121 tab = ATGetQueueEntry(wqueue, scanrel);
20122 Assert(tab->partition_constraint == NULL);
20123 tab->partition_constraint = (Expr *) linitial(partConstraint);
20124 tab->validate_default = validate_default;
20125 }
20126 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20127 {
20128 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20129 int i;
20130
20131 for (i = 0; i < partdesc->nparts; i++)
20132 {
20133 Relation part_rel;
20134 List *thisPartConstraint;
20135
20136 /*
20137 * This is the minimum lock we need to prevent deadlocks.
20138 */
20139 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20140
20141 /*
20142 * Adjust the constraint for scanrel so that it matches this
20143 * partition's attribute numbers.
20144 */
20145 thisPartConstraint =
20146 map_partition_varattnos(partConstraint, 1,
20147 part_rel, scanrel);
20148
20149 QueuePartitionConstraintValidation(wqueue, part_rel,
20150 thisPartConstraint,
20151 validate_default);
20152 table_close(part_rel, NoLock); /* keep lock till commit */
20153 }
20154 }
20155}

References AccessExclusiveLock, Assert(), ATGetQueueEntry(), DEBUG1, ereport, errmsg_internal(), i, linitial, map_partition_varattnos(), NoLock, PartitionDescData::nparts, PartitionDescData::oids, PartConstraintImpliedByRelConstraint(), AlteredTableInfo::partition_constraint, QueuePartitionConstraintValidation(), RelationData::rd_rel, RelationGetPartitionDesc(), RelationGetRelationName, table_close(), table_open(), and AlteredTableInfo::validate_default.

Referenced by ATExecAttachPartition(), and QueuePartitionConstraintValidation().

◆ RangeVarCallbackForAlterRelation()

static void RangeVarCallbackForAlterRelation ( const RangeVar rv,
Oid  relid,
Oid  oldrelid,
void *  arg 
)
static

Definition at line 19509 of file tablecmds.c.

19511{
19512 Node *stmt = (Node *) arg;
19513 ObjectType reltype;
19514 HeapTuple tuple;
19515 Form_pg_class classform;
19516 AclResult aclresult;
19517 char relkind;
19518
19519 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19520 if (!HeapTupleIsValid(tuple))
19521 return; /* concurrently dropped */
19522 classform = (Form_pg_class) GETSTRUCT(tuple);
19523 relkind = classform->relkind;
19524
19525 /* Must own relation. */
19526 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19528
19529 /* No system table modifications unless explicitly allowed. */
19530 if (!allowSystemTableMods && IsSystemClass(relid, classform))
19531 ereport(ERROR,
19532 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19533 errmsg("permission denied: \"%s\" is a system catalog",
19534 rv->relname)));
19535
19536 /*
19537 * Extract the specified relation type from the statement parse tree.
19538 *
19539 * Also, for ALTER .. RENAME, check permissions: the user must (still)
19540 * have CREATE rights on the containing namespace.
19541 */
19542 if (IsA(stmt, RenameStmt))
19543 {
19544 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19546 if (aclresult != ACLCHECK_OK)
19547 aclcheck_error(aclresult, OBJECT_SCHEMA,
19548 get_namespace_name(classform->relnamespace));
19549 reltype = ((RenameStmt *) stmt)->renameType;
19550 }
19551 else if (IsA(stmt, AlterObjectSchemaStmt))
19552 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19553
19554 else if (IsA(stmt, AlterTableStmt))
19555 reltype = ((AlterTableStmt *) stmt)->objtype;
19556 else
19557 {
19558 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19559 reltype = OBJECT_TABLE; /* placate compiler */
19560 }
19561
19562 /*
19563 * For compatibility with prior releases, we allow ALTER TABLE to be used
19564 * with most other types of relations (but not composite types). We allow
19565 * similar flexibility for ALTER INDEX in the case of RENAME, but not
19566 * otherwise. Otherwise, the user must select the correct form of the
19567 * command for the relation at issue.
19568 */
19569 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19570 ereport(ERROR,
19571 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19572 errmsg("\"%s\" is not a sequence", rv->relname)));
19573
19574 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19575 ereport(ERROR,
19576 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19577 errmsg("\"%s\" is not a view", rv->relname)));
19578
19579 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19580 ereport(ERROR,
19581 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19582 errmsg("\"%s\" is not a materialized view", rv->relname)));
19583
19584 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19585 ereport(ERROR,
19586 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19587 errmsg("\"%s\" is not a foreign table", rv->relname)));
19588
19589 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19590 ereport(ERROR,
19591 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19592 errmsg("\"%s\" is not a composite type", rv->relname)));
19593
19594 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19595 relkind != RELKIND_PARTITIONED_INDEX
19596 && !IsA(stmt, RenameStmt))
19597 ereport(ERROR,
19598 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19599 errmsg("\"%s\" is not an index", rv->relname)));
19600
19601 /*
19602 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19603 * TYPE for that.
19604 */
19605 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19606 ereport(ERROR,
19607 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19608 errmsg("\"%s\" is a composite type", rv->relname),
19609 /* translator: %s is an SQL ALTER command */
19610 errhint("Use %s instead.",
19611 "ALTER TYPE")));
19612
19613 /*
19614 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19615 * to a different schema, such as indexes and TOAST tables.
19616 */
19618 {
19619 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19620 ereport(ERROR,
19621 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19622 errmsg("cannot change schema of index \"%s\"",
19623 rv->relname),
19624 errhint("Change the schema of the table instead.")));
19625 else if (relkind == RELKIND_COMPOSITE_TYPE)
19626 ereport(ERROR,
19627 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19628 errmsg("cannot change schema of composite type \"%s\"",
19629 rv->relname),
19630 /* translator: %s is an SQL ALTER command */
19631 errhint("Use %s instead.",
19632 "ALTER TYPE")));
19633 else if (relkind == RELKIND_TOASTVALUE)
19634 ereport(ERROR,
19635 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19636 errmsg("cannot change schema of TOAST table \"%s\"",
19637 rv->relname),
19638 errhint("Change the schema of the table instead.")));
19639 }
19640
19641 ReleaseSysCache(tuple);
19642}
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:86
ObjectType
Definition: parsenodes.h:2316
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2335
@ OBJECT_VIEW
Definition: parsenodes.h:2368
@ OBJECT_TYPE
Definition: parsenodes.h:2366

References ACL_CREATE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, allowSystemTableMods, arg, elog, ereport, errcode(), errhint(), errmsg(), ERROR, get_namespace_name(), get_rel_relkind(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), HeapTupleIsValid, IsA, IsSystemClass(), nodeTag, object_aclcheck(), OBJECT_FOREIGN_TABLE, OBJECT_INDEX, OBJECT_MATVIEW, object_ownercheck(), OBJECT_SCHEMA, OBJECT_SEQUENCE, OBJECT_TABLE, OBJECT_TYPE, OBJECT_VIEW, ObjectIdGetDatum(), ReleaseSysCache(), RangeVar::relname, SearchSysCache1(), and stmt.

Referenced by AlterTableLookupRelation(), AlterTableNamespace(), and RenameRelation().

◆ RangeVarCallbackForAttachIndex()

static void RangeVarCallbackForAttachIndex ( const RangeVar rv,
Oid  relOid,
Oid  oldRelOid,
void *  arg 
)
static

Definition at line 21480 of file tablecmds.c.

21482{
21484 Form_pg_class classform;
21485 HeapTuple tuple;
21486
21487 state = (struct AttachIndexCallbackState *) arg;
21488
21489 if (!state->lockedParentTbl)
21490 {
21491 LockRelationOid(state->parentTblOid, AccessShareLock);
21492 state->lockedParentTbl = true;
21493 }
21494
21495 /*
21496 * If we previously locked some other heap, and the name we're looking up
21497 * no longer refers to an index on that relation, release the now-useless
21498 * lock. XXX maybe we should do *after* we verify whether the index does
21499 * not actually belong to the same relation ...
21500 */
21501 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21502 {
21503 UnlockRelationOid(state->partitionOid, AccessShareLock);
21504 state->partitionOid = InvalidOid;
21505 }
21506
21507 /* Didn't find a relation, so no need for locking or permission checks. */
21508 if (!OidIsValid(relOid))
21509 return;
21510
21511 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21512 if (!HeapTupleIsValid(tuple))
21513 return; /* concurrently dropped, so nothing to do */
21514 classform = (Form_pg_class) GETSTRUCT(tuple);
21515 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21516 classform->relkind != RELKIND_INDEX)
21517 ereport(ERROR,
21518 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21519 errmsg("\"%s\" is not an index", rv->relname)));
21520 ReleaseSysCache(tuple);
21521
21522 /*
21523 * Since we need only examine the heap's tupledesc, an access share lock
21524 * on it (preventing any DDL) is sufficient.
21525 */
21526 state->partitionOid = IndexGetRelation(relOid, false);
21527 LockRelationOid(state->partitionOid, AccessShareLock);
21528}
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:229

References AccessShareLock, arg, ereport, errcode(), errmsg(), ERROR, GETSTRUCT(), HeapTupleIsValid, IndexGetRelation(), InvalidOid, LockRelationOid(), ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), RangeVar::relname, SearchSysCache1(), and UnlockRelationOid().

Referenced by ATExecAttachPartitionIdx().

◆ RangeVarCallbackForDropRelation()

static void RangeVarCallbackForDropRelation ( const RangeVar rel,
Oid  relOid,
Oid  oldRelOid,
void *  arg 
)
static

Definition at line 1692 of file tablecmds.c.

1694{
1695 HeapTuple tuple;
1697 char expected_relkind;
1698 bool is_partition;
1699 Form_pg_class classform;
1701 bool invalid_system_index = false;
1702
1703 state = (struct DropRelationCallbackState *) arg;
1704 heap_lockmode = state->heap_lockmode;
1705
1706 /*
1707 * If we previously locked some other index's heap, and the name we're
1708 * looking up no longer refers to that relation, release the now-useless
1709 * lock.
1710 */
1711 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1712 {
1714 state->heapOid = InvalidOid;
1715 }
1716
1717 /*
1718 * Similarly, if we previously locked some other partition's heap, and the
1719 * name we're looking up no longer refers to that relation, release the
1720 * now-useless lock.
1721 */
1722 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1723 {
1725 state->partParentOid = InvalidOid;
1726 }
1727
1728 /* Didn't find a relation, so no need for locking or permission checks. */
1729 if (!OidIsValid(relOid))
1730 return;
1731
1732 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1733 if (!HeapTupleIsValid(tuple))
1734 return; /* concurrently dropped, so nothing to do */
1735 classform = (Form_pg_class) GETSTRUCT(tuple);
1736 is_partition = classform->relispartition;
1737
1738 /* Pass back some data to save lookups in RemoveRelations */
1739 state->actual_relkind = classform->relkind;
1740 state->actual_relpersistence = classform->relpersistence;
1741
1742 /*
1743 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1744 * but RemoveRelations() can only pass one relkind for a given relation.
1745 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1746 * That means we must be careful before giving the wrong type error when
1747 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1748 * exists with indexes.
1749 */
1750 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1751 expected_relkind = RELKIND_RELATION;
1752 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1753 expected_relkind = RELKIND_INDEX;
1754 else
1755 expected_relkind = classform->relkind;
1756
1757 if (state->expected_relkind != expected_relkind)
1758 DropErrorMsgWrongType(rel->relname, classform->relkind,
1759 state->expected_relkind);
1760
1761 /* Allow DROP to either table owner or schema owner */
1762 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1763 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1765 get_relkind_objtype(classform->relkind),
1766 rel->relname);
1767
1768 /*
1769 * Check the case of a system index that might have been invalidated by a
1770 * failed concurrent process and allow its drop. For the time being, this
1771 * only concerns indexes of toast relations that became invalid during a
1772 * REINDEX CONCURRENTLY process.
1773 */
1774 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1775 {
1776 HeapTuple locTuple;
1777 Form_pg_index indexform;
1778 bool indisvalid;
1779
1780 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1781 if (!HeapTupleIsValid(locTuple))
1782 {
1783 ReleaseSysCache(tuple);
1784 return;
1785 }
1786
1787 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1788 indisvalid = indexform->indisvalid;
1789 ReleaseSysCache(locTuple);
1790
1791 /* Mark object as being an invalid index of system catalogs */
1792 if (!indisvalid)
1793 invalid_system_index = true;
1794 }
1795
1796 /* In the case of an invalid index, it is fine to bypass this check */
1797 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1798 ereport(ERROR,
1799 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1800 errmsg("permission denied: \"%s\" is a system catalog",
1801 rel->relname)));
1802
1803 ReleaseSysCache(tuple);
1804
1805 /*
1806 * In DROP INDEX, attempt to acquire lock on the parent table before
1807 * locking the index. index_drop() will need this anyway, and since
1808 * regular queries lock tables before their indexes, we risk deadlock if
1809 * we do it the other way around. No error if we don't find a pg_index
1810 * entry, though --- the relation may have been dropped. Note that this
1811 * code will execute for either plain or partitioned indexes.
1812 */
1813 if (expected_relkind == RELKIND_INDEX &&
1814 relOid != oldRelOid)
1815 {
1816 state->heapOid = IndexGetRelation(relOid, true);
1817 if (OidIsValid(state->heapOid))
1819 }
1820
1821 /*
1822 * Similarly, if the relation is a partition, we must acquire lock on its
1823 * parent before locking the partition. That's because queries lock the
1824 * parent before its partitions, so we risk deadlock if we do it the other
1825 * way around.
1826 */
1827 if (is_partition && relOid != oldRelOid)
1828 {
1829 state->partParentOid = get_partition_parent(relOid, true);
1830 if (OidIsValid(state->partParentOid))
1831 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1832 }
1833}
FormData_pg_index * Form_pg_index
Definition: pg_index.h:70
static void DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
Definition: tablecmds.c:1501

References AccessExclusiveLock, aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, arg, DropErrorMsgWrongType(), ereport, errcode(), errmsg(), ERROR, DropRelationCallbackState::expected_relkind, get_partition_parent(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), DropRelationCallbackState::heap_lockmode, HeapTupleIsValid, IndexGetRelation(), InvalidOid, IsSystemClass(), LockRelationOid(), object_ownercheck(), ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), RangeVar::relname, SearchSysCache1(), and UnlockRelationOid().

Referenced by RemoveRelations().

◆ RangeVarCallbackForRenameAttribute()

static void RangeVarCallbackForRenameAttribute ( const RangeVar rv,
Oid  relid,
Oid  oldrelid,
void *  arg 
)
static

Definition at line 3979 of file tablecmds.c.

3981{
3982 HeapTuple tuple;
3983 Form_pg_class form;
3984
3985 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3986 if (!HeapTupleIsValid(tuple))
3987 return; /* concurrently dropped */
3988 form = (Form_pg_class) GETSTRUCT(tuple);
3989 renameatt_check(relid, form, false);
3990 ReleaseSysCache(tuple);
3991}
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition: tablecmds.c:3785

References GETSTRUCT(), HeapTupleIsValid, ObjectIdGetDatum(), ReleaseSysCache(), renameatt_check(), and SearchSysCache1().

Referenced by renameatt(), and RenameConstraint().

◆ RangeVarCallbackForTruncate()

static void RangeVarCallbackForTruncate ( const RangeVar relation,
Oid  relId,
Oid  oldRelId,
void *  arg 
)
static

Definition at line 19453 of file tablecmds.c.

19455{
19456 HeapTuple tuple;
19457
19458 /* Nothing to do if the relation was not found. */
19459 if (!OidIsValid(relId))
19460 return;
19461
19462 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19463 if (!HeapTupleIsValid(tuple)) /* should not happen */
19464 elog(ERROR, "cache lookup failed for relation %u", relId);
19465
19468
19469 ReleaseSysCache(tuple);
19470}

References elog, ERROR, GETSTRUCT(), HeapTupleIsValid, ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), SearchSysCache1(), truncate_check_perms(), and truncate_check_rel().

Referenced by ExecuteTruncate().

◆ RangeVarCallbackMaintainsTable()

void RangeVarCallbackMaintainsTable ( const RangeVar relation,
Oid  relId,
Oid  oldRelId,
void *  arg 
)

Definition at line 19417 of file tablecmds.c.

19419{
19420 char relkind;
19421 AclResult aclresult;
19422
19423 /* Nothing to do if the relation was not found. */
19424 if (!OidIsValid(relId))
19425 return;
19426
19427 /*
19428 * If the relation does exist, check whether it's an index. But note that
19429 * the relation might have been dropped between the time we did the name
19430 * lookup and now. In that case, there's nothing to do.
19431 */
19432 relkind = get_rel_relkind(relId);
19433 if (!relkind)
19434 return;
19435 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19436 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19437 ereport(ERROR,
19438 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19439 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19440
19441 /* Check permissions */
19442 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19443 if (aclresult != ACLCHECK_OK)
19444 aclcheck_error(aclresult,
19446 relation->relname);
19447}
#define ACL_MAINTAIN
Definition: parsenodes.h:90

References ACL_MAINTAIN, aclcheck_error(), ACLCHECK_OK, ereport, errcode(), errmsg(), ERROR, get_rel_relkind(), get_relkind_objtype(), GetUserId(), OidIsValid, pg_class_aclcheck(), and RangeVar::relname.

Referenced by cluster(), ExecRefreshMatView(), and ReindexTable().

◆ RangeVarCallbackOwnsRelation()

void RangeVarCallbackOwnsRelation ( const RangeVar relation,
Oid  relId,
Oid  oldRelId,
void *  arg 
)

Definition at line 19477 of file tablecmds.c.

19479{
19480 HeapTuple tuple;
19481
19482 /* Nothing to do if the relation was not found. */
19483 if (!OidIsValid(relId))
19484 return;
19485
19486 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19487 if (!HeapTupleIsValid(tuple)) /* should not happen */
19488 elog(ERROR, "cache lookup failed for relation %u", relId);
19489
19490 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19492 relation->relname);
19493
19494 if (!allowSystemTableMods &&
19495 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19496 ereport(ERROR,
19497 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19498 errmsg("permission denied: \"%s\" is a system catalog",
19499 relation->relname)));
19500
19501 ReleaseSysCache(tuple);
19502}

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, elog, ereport, errcode(), errmsg(), ERROR, get_rel_relkind(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), HeapTupleIsValid, IsSystemClass(), object_ownercheck(), ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), RangeVar::relname, and SearchSysCache1().

Referenced by AlterSequence(), and ProcessUtilitySlow().

◆ RebuildConstraintComment()

static void RebuildConstraintComment ( AlteredTableInfo tab,
AlterTablePass  pass,
Oid  objid,
Relation  rel,
List domname,
const char *  conname 
)
static

Definition at line 15765 of file tablecmds.c.

15768{
15769 CommentStmt *cmd;
15770 char *comment_str;
15771 AlterTableCmd *newcmd;
15772
15773 /* Look for comment for object wanted, and leave if none */
15774 comment_str = GetComment(objid, ConstraintRelationId, 0);
15775 if (comment_str == NULL)
15776 return;
15777
15778 /* Build CommentStmt node, copying all input data for safety */
15779 cmd = makeNode(CommentStmt);
15780 if (rel)
15781 {
15783 cmd->object = (Node *)
15786 makeString(pstrdup(conname)));
15787 }
15788 else
15789 {
15791 cmd->object = (Node *)
15793 makeString(pstrdup(conname)));
15794 }
15795 cmd->comment = comment_str;
15796
15797 /* Append it to list of commands */
15798 newcmd = makeNode(AlterTableCmd);
15799 newcmd->subtype = AT_ReAddComment;
15800 newcmd->def = (Node *) cmd;
15801 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15802}
TypeName * makeTypeNameFromNameList(List *names)
Definition: makefuncs.c:531
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2357
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2330
#define list_make3(x1, x2, x3)
Definition: pg_list.h:216
#define list_make2(x1, x2)
Definition: pg_list.h:214
char * comment
Definition: parsenodes.h:3351
ObjectType objtype
Definition: parsenodes.h:3349
Node * object
Definition: parsenodes.h:3350

References AT_ReAddComment, CommentStmt::comment, copyObject, AlterTableCmd::def, get_namespace_name(), GetComment(), lappend(), list_make2, list_make3, makeNode, makeString(), makeTypeNameFromNameList(), CommentStmt::object, OBJECT_DOMCONSTRAINT, OBJECT_TABCONSTRAINT, CommentStmt::objtype, pstrdup(), RelationGetNamespace, RelationGetRelationName, AlteredTableInfo::subcmds, and AlterTableCmd::subtype.

Referenced by ATPostAlterTypeParse().

◆ refuseDupeIndexAttach()

static void refuseDupeIndexAttach ( Relation  parentIdx,
Relation  partIdx,
Relation  partitionTbl 
)
static

Definition at line 21696 of file tablecmds.c.

21697{
21698 Oid existingIdx;
21699
21700 existingIdx = index_get_partition(partitionTbl,
21701 RelationGetRelid(parentIdx));
21702 if (OidIsValid(existingIdx))
21703 ereport(ERROR,
21704 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21705 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21706 RelationGetRelationName(partIdx),
21707 RelationGetRelationName(parentIdx)),
21708 errdetail("Another index is already attached for partition \"%s\".",
21709 RelationGetRelationName(partitionTbl))));
21710}

References ereport, errcode(), errdetail(), errmsg(), ERROR, index_get_partition(), OidIsValid, RelationGetRelationName, and RelationGetRelid.

Referenced by ATExecAttachPartitionIdx().

◆ register_on_commit_action()

void register_on_commit_action ( Oid  relid,
OnCommitAction  action 
)

Definition at line 19184 of file tablecmds.c.

19185{
19186 OnCommitItem *oc;
19187 MemoryContext oldcxt;
19188
19189 /*
19190 * We needn't bother registering the relation unless there is an ON COMMIT
19191 * action we need to take.
19192 */
19194 return;
19195
19197
19198 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
19199 oc->relid = relid;
19200 oc->oncommit = action;
19203
19204 /*
19205 * We use lcons() here so that ON COMMIT actions are processed in reverse
19206 * order of registration. That might not be essential but it seems
19207 * reasonable.
19208 */
19210
19211 MemoryContextSwitchTo(oldcxt);
19212}
List * lcons(void *datum, List *list)
Definition: list.c:495
MemoryContext CacheMemoryContext
Definition: mcxt.c:168

References generate_unaccent_rules::action, CacheMemoryContext, OnCommitItem::creating_subid, OnCommitItem::deleting_subid, GetCurrentSubTransactionId(), InvalidSubTransactionId, lcons(), MemoryContextSwitchTo(), on_commits, OnCommitItem::oncommit, ONCOMMIT_NOOP, ONCOMMIT_PRESERVE_ROWS, palloc(), and OnCommitItem::relid.

Referenced by heap_create_with_catalog().

◆ relation_mark_replica_identity()

static void relation_mark_replica_identity ( Relation  rel,
char  ri_type,
Oid  indexOid,
bool  is_internal 
)
static

Definition at line 18325 of file tablecmds.c.

18327{
18328 Relation pg_index;
18329 Relation pg_class;
18330 HeapTuple pg_class_tuple;
18331 HeapTuple pg_index_tuple;
18332 Form_pg_class pg_class_form;
18333 Form_pg_index pg_index_form;
18334 ListCell *index;
18335
18336 /*
18337 * Check whether relreplident has changed, and update it if so.
18338 */
18339 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18340 pg_class_tuple = SearchSysCacheCopy1(RELOID,
18342 if (!HeapTupleIsValid(pg_class_tuple))
18343 elog(ERROR, "cache lookup failed for relation \"%s\"",
18345 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18346 if (pg_class_form->relreplident != ri_type)
18347 {
18348 pg_class_form->relreplident = ri_type;
18349 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18350 }
18351 table_close(pg_class, RowExclusiveLock);
18352 heap_freetuple(pg_class_tuple);
18353
18354 /*
18355 * Update the per-index indisreplident flags correctly.
18356 */
18357 pg_index = table_open(IndexRelationId, RowExclusiveLock);
18358 foreach(index, RelationGetIndexList(rel))
18359 {
18360 Oid thisIndexOid = lfirst_oid(index);
18361 bool dirty = false;
18362
18363 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18364 ObjectIdGetDatum(thisIndexOid));
18365 if (!HeapTupleIsValid(pg_index_tuple))
18366 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18367 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18368
18369 if (thisIndexOid == indexOid)
18370 {
18371 /* Set the bit if not already set. */
18372 if (!pg_index_form->indisreplident)
18373 {
18374 dirty = true;
18375 pg_index_form->indisreplident = true;
18376 }
18377 }
18378 else
18379 {
18380 /* Unset the bit if set. */
18381 if (pg_index_form->indisreplident)
18382 {
18383 dirty = true;
18384 pg_index_form->indisreplident = false;
18385 }
18386 }
18387
18388 if (dirty)
18389 {
18390 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18391 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18392 InvalidOid, is_internal);
18393
18394 /*
18395 * Invalidate the relcache for the table, so that after we commit
18396 * all sessions will refresh the table's replica identity index
18397 * before attempting any UPDATE or DELETE on the table. (If we
18398 * changed the table's pg_class row above, then a relcache inval
18399 * is already queued due to that; but we might not have.)
18400 */
18402 }
18403 heap_freetuple(pg_index_tuple);
18404 }
18405
18406 table_close(pg_index, RowExclusiveLock);
18407}
Definition: type.h:96

References CacheInvalidateRelcache(), CatalogTupleUpdate(), elog, ERROR, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidOid, InvokeObjectPostAlterHookArg, lfirst_oid, ObjectIdGetDatum(), RelationGetIndexList(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecReplicaIdentity().

◆ RememberAllDependentForRebuilding()

static void RememberAllDependentForRebuilding ( AlteredTableInfo tab,
AlterTableType  subtype,
Relation  rel,
AttrNumber  attnum,
const char *  colName 
)
static

Definition at line 14989 of file tablecmds.c.

14991{
14992 Relation depRel;
14993 ScanKeyData key[3];
14994 SysScanDesc scan;
14995 HeapTuple depTup;
14996
14997 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
14998
14999 depRel = table_open(DependRelationId, RowExclusiveLock);
15000
15001 ScanKeyInit(&key[0],
15002 Anum_pg_depend_refclassid,
15003 BTEqualStrategyNumber, F_OIDEQ,
15004 ObjectIdGetDatum(RelationRelationId));
15005 ScanKeyInit(&key[1],
15006 Anum_pg_depend_refobjid,
15007 BTEqualStrategyNumber, F_OIDEQ,
15009 ScanKeyInit(&key[2],
15010 Anum_pg_depend_refobjsubid,
15011 BTEqualStrategyNumber, F_INT4EQ,
15013
15014 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15015 NULL, 3, key);
15016
15017 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15018 {
15019 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15020 ObjectAddress foundObject;
15021
15022 foundObject.classId = foundDep->classid;
15023 foundObject.objectId = foundDep->objid;
15024 foundObject.objectSubId = foundDep->objsubid;
15025
15026 switch (foundObject.classId)
15027 {
15028 case RelationRelationId:
15029 {
15030 char relKind = get_rel_relkind(foundObject.objectId);
15031
15032 if (relKind == RELKIND_INDEX ||
15033 relKind == RELKIND_PARTITIONED_INDEX)
15034 {
15035 Assert(foundObject.objectSubId == 0);
15036 RememberIndexForRebuilding(foundObject.objectId, tab);
15037 }
15038 else if (relKind == RELKIND_SEQUENCE)
15039 {
15040 /*
15041 * This must be a SERIAL column's sequence. We need
15042 * not do anything to it.
15043 */
15044 Assert(foundObject.objectSubId == 0);
15045 }
15046 else
15047 {
15048 /* Not expecting any other direct dependencies... */
15049 elog(ERROR, "unexpected object depending on column: %s",
15050 getObjectDescription(&foundObject, false));
15051 }
15052 break;
15053 }
15054
15055 case ConstraintRelationId:
15056 Assert(foundObject.objectSubId == 0);
15057 RememberConstraintForRebuilding(foundObject.objectId, tab);
15058 break;
15059
15060 case ProcedureRelationId:
15061
15062 /*
15063 * A new-style SQL function can depend on a column, if that
15064 * column is referenced in the parsed function body. Ideally
15065 * we'd automatically update the function by deparsing and
15066 * reparsing it, but that's risky and might well fail anyhow.
15067 * FIXME someday.
15068 *
15069 * This is only a problem for AT_AlterColumnType, not
15070 * AT_SetExpression.
15071 */
15072 if (subtype == AT_AlterColumnType)
15073 ereport(ERROR,
15074 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15075 errmsg("cannot alter type of a column used by a function or procedure"),
15076 errdetail("%s depends on column \"%s\"",
15077 getObjectDescription(&foundObject, false),
15078 colName)));
15079 break;
15080
15081 case RewriteRelationId:
15082
15083 /*
15084 * View/rule bodies have pretty much the same issues as
15085 * function bodies. FIXME someday.
15086 */
15087 if (subtype == AT_AlterColumnType)
15088 ereport(ERROR,
15089 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15090 errmsg("cannot alter type of a column used by a view or rule"),
15091 errdetail("%s depends on column \"%s\"",
15092 getObjectDescription(&foundObject, false),
15093 colName)));
15094 break;
15095
15096 case TriggerRelationId:
15097
15098 /*
15099 * A trigger can depend on a column because the column is
15100 * specified as an update target, or because the column is
15101 * used in the trigger's WHEN condition. The first case would
15102 * not require any extra work, but the second case would
15103 * require updating the WHEN expression, which has the same
15104 * issues as above. Since we can't easily tell which case
15105 * applies, we punt for both. FIXME someday.
15106 */
15107 if (subtype == AT_AlterColumnType)
15108 ereport(ERROR,
15109 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15110 errmsg("cannot alter type of a column used in a trigger definition"),
15111 errdetail("%s depends on column \"%s\"",
15112 getObjectDescription(&foundObject, false),
15113 colName)));
15114 break;
15115
15116 case PolicyRelationId:
15117
15118 /*
15119 * A policy can depend on a column because the column is
15120 * specified in the policy's USING or WITH CHECK qual
15121 * expressions. It might be possible to rewrite and recheck
15122 * the policy expression, but punt for now. It's certainly
15123 * easy enough to remove and recreate the policy; still, FIXME
15124 * someday.
15125 */
15126 if (subtype == AT_AlterColumnType)
15127 ereport(ERROR,
15128 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15129 errmsg("cannot alter type of a column used in a policy definition"),
15130 errdetail("%s depends on column \"%s\"",
15131 getObjectDescription(&foundObject, false),
15132 colName)));
15133 break;
15134
15135 case AttrDefaultRelationId:
15136 {
15138
15139 if (col.objectId == RelationGetRelid(rel) &&
15140 col.objectSubId == attnum)
15141 {
15142 /*
15143 * Ignore the column's own default expression. The
15144 * caller deals with it.
15145 */
15146 }
15147 else
15148 {
15149 /*
15150 * This must be a reference from the expression of a
15151 * generated column elsewhere in the same table.
15152 * Changing the type/generated expression of a column
15153 * that is used by a generated column is not allowed
15154 * by SQL standard, so just punt for now. It might be
15155 * doable with some thinking and effort.
15156 */
15157 if (subtype == AT_AlterColumnType)
15158 ereport(ERROR,
15159 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15160 errmsg("cannot alter type of a column used by a generated column"),
15161 errdetail("Column \"%s\" is used by generated column \"%s\".",
15162 colName,
15164 col.objectSubId,
15165 false))));
15166 }
15167 break;
15168 }
15169
15170 case StatisticExtRelationId:
15171
15172 /*
15173 * Give the extended-stats machinery a chance to fix anything
15174 * that this column type change would break.
15175 */
15176 RememberStatisticsForRebuilding(foundObject.objectId, tab);
15177 break;
15178
15179 case PublicationRelRelationId:
15180
15181 /*
15182 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15183 * clause. Same issues as above. FIXME someday.
15184 */
15185 if (subtype == AT_AlterColumnType)
15186 ereport(ERROR,
15187 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15188 errmsg("cannot alter type of a column used by a publication WHERE clause"),
15189 errdetail("%s depends on column \"%s\"",
15190 getObjectDescription(&foundObject, false),
15191 colName)));
15192 break;
15193
15194 default:
15195
15196 /*
15197 * We don't expect any other sorts of objects to depend on a
15198 * column.
15199 */
15200 elog(ERROR, "unexpected object depending on column: %s",
15201 getObjectDescription(&foundObject, false));
15202 break;
15203 }
15204 }
15205
15206 systable_endscan(scan);
15207 table_close(depRel, NoLock);
15208}
ObjectAddress GetAttrDefaultColumnAddress(Oid attrdefoid)
Definition: pg_attrdef.c:320
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15302
static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15353
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15246

References Assert(), AT_AlterColumnType, AT_SetExpression, attnum, BTEqualStrategyNumber, ObjectAddress::classId, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, get_attname(), get_rel_relkind(), GetAttrDefaultColumnAddress(), getObjectDescription(), GETSTRUCT(), HeapTupleIsValid, Int32GetDatum(), sort-test::key, NoLock, ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, RelationGetRelid, RememberConstraintForRebuilding(), RememberIndexForRebuilding(), RememberStatisticsForRebuilding(), RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecAlterColumnType(), and ATExecSetExpression().

◆ RememberClusterOnForRebuilding()

static void RememberClusterOnForRebuilding ( Oid  indoid,
AlteredTableInfo tab 
)
static

Definition at line 15230 of file tablecmds.c.

15231{
15232 if (!get_index_isclustered(indoid))
15233 return;
15234
15235 if (tab->clusterOnIndex)
15236 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15237
15238 tab->clusterOnIndex = get_rel_name(indoid);
15239}
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3741

References AlteredTableInfo::clusterOnIndex, elog, ERROR, get_index_isclustered(), get_rel_name(), and AlteredTableInfo::relid.

Referenced by RememberConstraintForRebuilding(), and RememberIndexForRebuilding().

◆ RememberConstraintForRebuilding()

static void RememberConstraintForRebuilding ( Oid  conoid,
AlteredTableInfo tab 
)
static

Definition at line 15246 of file tablecmds.c.

15247{
15248 /*
15249 * This de-duplication check is critical for two independent reasons: we
15250 * mustn't try to recreate the same constraint twice, and if a constraint
15251 * depends on more than one column whose type is to be altered, we must
15252 * capture its definition string before applying any of the column type
15253 * changes. ruleutils.c will get confused if we ask again later.
15254 */
15255 if (!list_member_oid(tab->changedConstraintOids, conoid))
15256 {
15257 /* OK, capture the constraint's existing definition string */
15258 char *defstring = pg_get_constraintdef_command(conoid);
15259 Oid indoid;
15260
15261 /*
15262 * It is critical to create not-null constraints ahead of primary key
15263 * indexes; otherwise, the not-null constraint would be created by the
15264 * primary key, and the constraint name would be wrong.
15265 */
15266 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15267 {
15268 tab->changedConstraintOids = lcons_oid(conoid,
15270 tab->changedConstraintDefs = lcons(defstring,
15272 }
15273 else
15274 {
15275
15277 conoid);
15279 defstring);
15280 }
15281
15282 /*
15283 * For the index of a constraint, if any, remember if it is used for
15284 * the table's replica identity or if it is a clustered index, so that
15285 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15286 * those properties.
15287 */
15288 indoid = get_constraint_index(conoid);
15289 if (OidIsValid(indoid))
15290 {
15292 RememberClusterOnForRebuilding(indoid, tab);
15293 }
15294 }
15295}
List * lcons_oid(Oid datum, List *list)
Definition: list.c:531
char * pg_get_constraintdef_command(Oid constraintId)
Definition: ruleutils.c:2184
static void RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15215
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15230

References AlteredTableInfo::changedConstraintDefs, AlteredTableInfo::changedConstraintOids, get_constraint_index(), get_constraint_type(), lappend(), lappend_oid(), lcons(), lcons_oid(), list_member_oid(), OidIsValid, pg_get_constraintdef_command(), RememberClusterOnForRebuilding(), and RememberReplicaIdentityForRebuilding().

Referenced by RememberAllDependentForRebuilding(), and RememberIndexForRebuilding().

◆ RememberIndexForRebuilding()

static void RememberIndexForRebuilding ( Oid  indoid,
AlteredTableInfo tab 
)
static

Definition at line 15302 of file tablecmds.c.

15303{
15304 /*
15305 * This de-duplication check is critical for two independent reasons: we
15306 * mustn't try to recreate the same index twice, and if an index depends
15307 * on more than one column whose type is to be altered, we must capture
15308 * its definition string before applying any of the column type changes.
15309 * ruleutils.c will get confused if we ask again later.
15310 */
15311 if (!list_member_oid(tab->changedIndexOids, indoid))
15312 {
15313 /*
15314 * Before adding it as an index-to-rebuild, we'd better see if it
15315 * belongs to a constraint, and if so rebuild the constraint instead.
15316 * Typically this check fails, because constraint indexes normally
15317 * have only dependencies on their constraint. But it's possible for
15318 * such an index to also have direct dependencies on table columns,
15319 * for example with a partial exclusion constraint.
15320 */
15321 Oid conoid = get_index_constraint(indoid);
15322
15323 if (OidIsValid(conoid))
15324 {
15326 }
15327 else
15328 {
15329 /* OK, capture the index's existing definition string */
15330 char *defstring = pg_get_indexdef_string(indoid);
15331
15333 indoid);
15335 defstring);
15336
15337 /*
15338 * Remember if this index is used for the table's replica identity
15339 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15340 * can queue up commands necessary to restore those properties.
15341 */
15343 RememberClusterOnForRebuilding(indoid, tab);
15344 }
15345 }
15346}
Oid get_index_constraint(Oid indexId)
Definition: pg_depend.c:988
char * pg_get_indexdef_string(Oid indexrelid)
Definition: ruleutils.c:1225

References AlteredTableInfo::changedIndexDefs, AlteredTableInfo::changedIndexOids, get_index_constraint(), lappend(), lappend_oid(), list_member_oid(), OidIsValid, pg_get_indexdef_string(), RememberClusterOnForRebuilding(), RememberConstraintForRebuilding(), and RememberReplicaIdentityForRebuilding().

Referenced by RememberAllDependentForRebuilding().

◆ RememberReplicaIdentityForRebuilding()

static void RememberReplicaIdentityForRebuilding ( Oid  indoid,
AlteredTableInfo tab 
)
static

Definition at line 15215 of file tablecmds.c.

15216{
15217 if (!get_index_isreplident(indoid))
15218 return;
15219
15220 if (tab->replicaIdentityIndex)
15221 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15222
15223 tab->replicaIdentityIndex = get_rel_name(indoid);
15224}
bool get_index_isreplident(Oid index_oid)
Definition: lsyscache.c:3695

References elog, ERROR, get_index_isreplident(), get_rel_name(), AlteredTableInfo::relid, and AlteredTableInfo::replicaIdentityIndex.

Referenced by RememberConstraintForRebuilding(), and RememberIndexForRebuilding().

◆ RememberStatisticsForRebuilding()

static void RememberStatisticsForRebuilding ( Oid  stxoid,
AlteredTableInfo tab 
)
static

Definition at line 15353 of file tablecmds.c.

15354{
15355 /*
15356 * This de-duplication check is critical for two independent reasons: we
15357 * mustn't try to recreate the same statistics object twice, and if the
15358 * statistics object depends on more than one column whose type is to be
15359 * altered, we must capture its definition string before applying any of
15360 * the type changes. ruleutils.c will get confused if we ask again later.
15361 */
15362 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15363 {
15364 /* OK, capture the statistics object's existing definition string */
15365 char *defstring = pg_get_statisticsobjdef_string(stxoid);
15366
15368 stxoid);
15370 defstring);
15371 }
15372}
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition: ruleutils.c:1627

References AlteredTableInfo::changedStatisticsDefs, AlteredTableInfo::changedStatisticsOids, lappend(), lappend_oid(), list_member_oid(), and pg_get_statisticsobjdef_string().

Referenced by RememberAllDependentForRebuilding().

◆ remove_on_commit_action()

void remove_on_commit_action ( Oid  relid)

Definition at line 19220 of file tablecmds.c.

19221{
19222 ListCell *l;
19223
19224 foreach(l, on_commits)
19225 {
19226 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19227
19228 if (oc->relid == relid)
19229 {
19231 break;
19232 }
19233 }
19234}

References OnCommitItem::deleting_subid, GetCurrentSubTransactionId(), lfirst, on_commits, and OnCommitItem::relid.

Referenced by heap_drop_with_catalog().

◆ RemoveInheritance()

static void RemoveInheritance ( Relation  child_rel,
Relation  parent_rel,
bool  expect_detached 
)
static

Definition at line 17873 of file tablecmds.c.

17874{
17875 Relation catalogRelation;
17876 SysScanDesc scan;
17877 ScanKeyData key[3];
17878 HeapTuple attributeTuple,
17879 constraintTuple;
17880 AttrMap *attmap;
17881 List *connames;
17882 List *nncolumns;
17883 bool found;
17884 bool is_partitioning;
17885
17886 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17887
17888 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17889 RelationGetRelid(parent_rel),
17890 expect_detached,
17891 RelationGetRelationName(child_rel));
17892 if (!found)
17893 {
17894 if (is_partitioning)
17895 ereport(ERROR,
17897 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17898 RelationGetRelationName(child_rel),
17899 RelationGetRelationName(parent_rel))));
17900 else
17901 ereport(ERROR,
17903 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17904 RelationGetRelationName(parent_rel),
17905 RelationGetRelationName(child_rel))));
17906 }
17907
17908 /*
17909 * Search through child columns looking for ones matching parent rel
17910 */
17911 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17912 ScanKeyInit(&key[0],
17913 Anum_pg_attribute_attrelid,
17914 BTEqualStrategyNumber, F_OIDEQ,
17916 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17917 true, NULL, 1, key);
17918 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17919 {
17920 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17921
17922 /* Ignore if dropped or not inherited */
17923 if (att->attisdropped)
17924 continue;
17925 if (att->attinhcount <= 0)
17926 continue;
17927
17929 NameStr(att->attname)))
17930 {
17931 /* Decrement inhcount and possibly set islocal to true */
17932 HeapTuple copyTuple = heap_copytuple(attributeTuple);
17933 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
17934
17935 copy_att->attinhcount--;
17936 if (copy_att->attinhcount == 0)
17937 copy_att->attislocal = true;
17938
17939 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
17940 heap_freetuple(copyTuple);
17941 }
17942 }
17943 systable_endscan(scan);
17944 table_close(catalogRelation, RowExclusiveLock);
17945
17946 /*
17947 * Likewise, find inherited check and not-null constraints and disinherit
17948 * them. To do this, we first need a list of the names of the parent's
17949 * check constraints. (We cheat a bit by only checking for name matches,
17950 * assuming that the expressions will match.)
17951 *
17952 * For NOT NULL columns, we store column numbers to match, mapping them in
17953 * to the child rel's attribute numbers.
17954 */
17955 attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
17956 RelationGetDescr(parent_rel),
17957 false);
17958
17959 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
17960 ScanKeyInit(&key[0],
17961 Anum_pg_constraint_conrelid,
17962 BTEqualStrategyNumber, F_OIDEQ,
17963 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17964 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17965 true, NULL, 1, key);
17966
17967 connames = NIL;
17968 nncolumns = NIL;
17969
17970 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17971 {
17972 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17973
17974 if (con->connoinherit)
17975 continue;
17976
17977 if (con->contype == CONSTRAINT_CHECK)
17978 connames = lappend(connames, pstrdup(NameStr(con->conname)));
17979 if (con->contype == CONSTRAINT_NOTNULL)
17980 {
17981 AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
17982
17983 nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
17984 }
17985 }
17986
17987 systable_endscan(scan);
17988
17989 /* Now scan the child's constraints to find matches */
17990 ScanKeyInit(&key[0],
17991 Anum_pg_constraint_conrelid,
17992 BTEqualStrategyNumber, F_OIDEQ,
17994 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17995 true, NULL, 1, key);
17996
17997 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17998 {
17999 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18000 bool match = false;
18001
18002 /*
18003 * Match CHECK constraints by name, not-null constraints by column
18004 * number, and ignore all others.
18005 */
18006 if (con->contype == CONSTRAINT_CHECK)
18007 {
18008 foreach_ptr(char, chkname, connames)
18009 {
18010 if (con->contype == CONSTRAINT_CHECK &&
18011 strcmp(NameStr(con->conname), chkname) == 0)
18012 {
18013 match = true;
18014 connames = foreach_delete_current(connames, chkname);
18015 break;
18016 }
18017 }
18018 }
18019 else if (con->contype == CONSTRAINT_NOTNULL)
18020 {
18021 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18022
18023 foreach_int(prevattno, nncolumns)
18024 {
18025 if (prevattno == child_attno)
18026 {
18027 match = true;
18028 nncolumns = foreach_delete_current(nncolumns, prevattno);
18029 break;
18030 }
18031 }
18032 }
18033 else
18034 continue;
18035
18036 if (match)
18037 {
18038 /* Decrement inhcount and possibly set islocal to true */
18039 HeapTuple copyTuple = heap_copytuple(constraintTuple);
18040 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18041
18042 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18043 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18044 RelationGetRelid(child_rel), NameStr(copy_con->conname));
18045
18046 copy_con->coninhcount--;
18047 if (copy_con->coninhcount == 0)
18048 copy_con->conislocal = true;
18049
18050 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
18051 heap_freetuple(copyTuple);
18052 }
18053 }
18054
18055 /* We should have matched all constraints */
18056 if (connames != NIL || nncolumns != NIL)
18057 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18058 list_length(connames) + list_length(nncolumns),
18059 RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18060
18061 systable_endscan(scan);
18062 table_close(catalogRelation, RowExclusiveLock);
18063
18065 RelationRelationId,
18066 RelationGetRelid(parent_rel),
18067 child_dependency_type(is_partitioning));
18068
18069 /*
18070 * Post alter hook of this inherits. Since object_access_hook doesn't take
18071 * multiple object identifiers, we relay oid of parent relation using
18072 * auxiliary_id argument.
18073 */
18074 InvokeObjectPostAlterHookArg(InheritsRelationId,
18075 RelationGetRelid(child_rel), 0,
18076 RelationGetRelid(parent_rel), false);
18077}
bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending, const char *childname)
Definition: pg_inherits.c:552
bool SearchSysCacheExistsAttName(Oid relid, const char *attname)
Definition: syscache.c:522
#define child_dependency_type(child_is_partition)
Definition: tablecmds.c:365

References AttrMap::attnums, BTEqualStrategyNumber, build_attrmap_by_name(), CatalogTupleUpdate(), child_dependency_type, DeleteInheritsTuple(), drop_parent_dependency(), elog, ereport, errcode(), ERRCODE_UNDEFINED_TABLE, errmsg(), ERROR, extractNotNullColumn(), foreach_delete_current, foreach_int, foreach_ptr, GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHookArg, sort-test::key, lappend(), lappend_int(), list_length(), NameStr, NIL, ObjectIdGetDatum(), pstrdup(), RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), SearchSysCacheExistsAttName(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecDetachPartition(), ATExecDropInherit(), and DetachPartitionFinalize().

◆ RemoveInheritedConstraint()

static void RemoveInheritedConstraint ( Relation  conrel,
Relation  trigrel,
Oid  conoid,
Oid  conrelid 
)
static

Definition at line 11894 of file tablecmds.c.

11896{
11897 ObjectAddresses *objs;
11898 HeapTuple consttup;
11900 SysScanDesc scan;
11901 HeapTuple trigtup;
11902
11904 Anum_pg_constraint_conrelid,
11905 BTEqualStrategyNumber, F_OIDEQ,
11906 ObjectIdGetDatum(conrelid));
11907
11908 scan = systable_beginscan(conrel,
11909 ConstraintRelidTypidNameIndexId,
11910 true, NULL, 1, &key);
11911 objs = new_object_addresses();
11912 while ((consttup = systable_getnext(scan)) != NULL)
11913 {
11914 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11915
11916 if (conform->conparentid != conoid)
11917 continue;
11918 else
11919 {
11920 ObjectAddress addr;
11921 SysScanDesc scan2;
11922 ScanKeyData key2;
11924
11925 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11926 add_exact_object_address(&addr, objs);
11927
11928 /*
11929 * First we must delete the dependency record that binds the
11930 * constraint records together.
11931 */
11932 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11933 conform->oid,
11935 ConstraintRelationId,
11936 conoid);
11937 Assert(n == 1); /* actually only one is expected */
11938
11939 /*
11940 * Now search for the triggers for this constraint and set them up
11941 * for deletion too
11942 */
11943 ScanKeyInit(&key2,
11944 Anum_pg_trigger_tgconstraint,
11945 BTEqualStrategyNumber, F_OIDEQ,
11946 ObjectIdGetDatum(conform->oid));
11947 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11948 true, NULL, 1, &key2);
11949 while ((trigtup = systable_getnext(scan2)) != NULL)
11950 {
11951 ObjectAddressSet(addr, TriggerRelationId,
11952 ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11953 add_exact_object_address(&addr, objs);
11954 }
11955 systable_endscan(scan2);
11956 }
11957 }
11958 /* make the dependency deletions visible */
11962 systable_endscan(scan);
11963}
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:224
long deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype, Oid refclassId, Oid refobjectId)
Definition: pg_depend.c:398

References add_exact_object_address(), Assert(), BTEqualStrategyNumber, CommandCounterIncrement(), deleteDependencyRecordsForSpecific(), DEPENDENCY_INTERNAL, DROP_RESTRICT, GETSTRUCT(), sort-test::key, new_object_addresses(), ObjectAddressSet, ObjectIdGetDatum(), PERFORM_DELETION_INTERNAL, performMultipleDeletions(), PG_USED_FOR_ASSERTS_ONLY, ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by AttachPartitionForeignKey().

◆ RemoveRelations()

void RemoveRelations ( DropStmt drop)

Definition at line 1528 of file tablecmds.c.

1529{
1530 ObjectAddresses *objects;
1531 char relkind;
1532 ListCell *cell;
1533 int flags = 0;
1534 LOCKMODE lockmode = AccessExclusiveLock;
1535
1536 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1537 if (drop->concurrent)
1538 {
1539 /*
1540 * Note that for temporary relations this lock may get upgraded later
1541 * on, but as no other session can access a temporary relation, this
1542 * is actually fine.
1543 */
1544 lockmode = ShareUpdateExclusiveLock;
1545 Assert(drop->removeType == OBJECT_INDEX);
1546 if (list_length(drop->objects) != 1)
1547 ereport(ERROR,
1548 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1549 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1550 if (drop->behavior == DROP_CASCADE)
1551 ereport(ERROR,
1552 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1553 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1554 }
1555
1556 /*
1557 * First we identify all the relations, then we delete them in a single
1558 * performMultipleDeletions() call. This is to avoid unwanted DROP
1559 * RESTRICT errors if one of the relations depends on another.
1560 */
1561
1562 /* Determine required relkind */
1563 switch (drop->removeType)
1564 {
1565 case OBJECT_TABLE:
1566 relkind = RELKIND_RELATION;
1567 break;
1568
1569 case OBJECT_INDEX:
1570 relkind = RELKIND_INDEX;
1571 break;
1572
1573 case OBJECT_SEQUENCE:
1574 relkind = RELKIND_SEQUENCE;
1575 break;
1576
1577 case OBJECT_VIEW:
1578 relkind = RELKIND_VIEW;
1579 break;
1580
1581 case OBJECT_MATVIEW:
1582 relkind = RELKIND_MATVIEW;
1583 break;
1584
1586 relkind = RELKIND_FOREIGN_TABLE;
1587 break;
1588
1589 default:
1590 elog(ERROR, "unrecognized drop object type: %d",
1591 (int) drop->removeType);
1592 relkind = 0; /* keep compiler quiet */
1593 break;
1594 }
1595
1596 /* Lock and validate each relation; build a list of object addresses */
1597 objects = new_object_addresses();
1598
1599 foreach(cell, drop->objects)
1600 {
1601 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1602 Oid relOid;
1603 ObjectAddress obj;
1605
1606 /*
1607 * These next few steps are a great deal like relation_openrv, but we
1608 * don't bother building a relcache entry since we don't need it.
1609 *
1610 * Check for shared-cache-inval messages before trying to access the
1611 * relation. This is needed to cover the case where the name
1612 * identifies a rel that has been dropped and recreated since the
1613 * start of our transaction: if we don't flush the old syscache entry,
1614 * then we'll latch onto that entry and suffer an error later.
1615 */
1617
1618 /* Look up the appropriate relation using namespace search. */
1619 state.expected_relkind = relkind;
1620 state.heap_lockmode = drop->concurrent ?
1622 /* We must initialize these fields to show that no locks are held: */
1623 state.heapOid = InvalidOid;
1624 state.partParentOid = InvalidOid;
1625
1626 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1628 &state);
1629
1630 /* Not there? */
1631 if (!OidIsValid(relOid))
1632 {
1633 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1634 continue;
1635 }
1636
1637 /*
1638 * Decide if concurrent mode needs to be used here or not. The
1639 * callback retrieved the rel's persistence for us.
1640 */
1641 if (drop->concurrent &&
1642 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1643 {
1644 Assert(list_length(drop->objects) == 1 &&
1645 drop->removeType == OBJECT_INDEX);
1647 }
1648
1649 /*
1650 * Concurrent index drop cannot be used with partitioned indexes,
1651 * either.
1652 */
1653 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1654 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1655 ereport(ERROR,
1656 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1657 errmsg("cannot drop partitioned index \"%s\" concurrently",
1658 rel->relname)));
1659
1660 /*
1661 * If we're told to drop a partitioned index, we must acquire lock on
1662 * all the children of its parent partitioned table before proceeding.
1663 * Otherwise we'd try to lock the child index partitions before their
1664 * tables, leading to potential deadlock against other sessions that
1665 * will lock those objects in the other order.
1666 */
1667 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1668 (void) find_all_inheritors(state.heapOid,
1669 state.heap_lockmode,
1670 NULL);
1671
1672 /* OK, we're ready to delete this one */
1673 obj.classId = RelationRelationId;
1674 obj.objectId = relOid;
1675 obj.objectSubId = 0;
1676
1677 add_exact_object_address(&obj, objects);
1678 }
1679
1680 performMultipleDeletions(objects, drop->behavior, flags);
1681
1682 free_object_addresses(objects);
1683}
#define PERFORM_DELETION_CONCURRENTLY
Definition: dependency.h:93
void AcceptInvalidationMessages(void)
Definition: inval.c:930
RangeVar * makeRangeVarFromNameList(const List *names)
Definition: namespace.c:3554
bool missing_ok
Definition: parsenodes.h:3326
List * objects
Definition: parsenodes.h:3323
ObjectType removeType
Definition: parsenodes.h:3324
bool concurrent
Definition: parsenodes.h:3327
DropBehavior behavior
Definition: parsenodes.h:3325
static void DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
Definition: tablecmds.c:1453
static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:1692

References AcceptInvalidationMessages(), AccessExclusiveLock, add_exact_object_address(), Assert(), DropStmt::behavior, ObjectAddress::classId, DropStmt::concurrent, DROP_CASCADE, DropErrorMsgNonExistent(), elog, ereport, errcode(), errmsg(), ERROR, find_all_inheritors(), free_object_addresses(), InvalidOid, lfirst, list_length(), makeRangeVarFromNameList(), DropStmt::missing_ok, new_object_addresses(), OBJECT_FOREIGN_TABLE, OBJECT_INDEX, OBJECT_MATVIEW, OBJECT_SEQUENCE, OBJECT_TABLE, OBJECT_VIEW, ObjectAddress::objectId, DropStmt::objects, ObjectAddress::objectSubId, OidIsValid, PERFORM_DELETION_CONCURRENTLY, performMultipleDeletions(), RangeVarCallbackForDropRelation(), RangeVarGetRelidExtended(), RangeVar::relname, DropStmt::removeType, RVR_MISSING_OK, and ShareUpdateExclusiveLock.

Referenced by ExecDropStmt().

◆ rename_constraint_internal()

static ObjectAddress rename_constraint_internal ( Oid  myrelid,
Oid  mytypid,
const char *  oldconname,
const char *  newconname,
bool  recurse,
bool  recursing,
int  expected_parents 
)
static

Definition at line 4037 of file tablecmds.c.

4044{
4045 Relation targetrelation = NULL;
4046 Oid constraintOid;
4047 HeapTuple tuple;
4049 ObjectAddress address;
4050
4051 Assert(!myrelid || !mytypid);
4052
4053 if (mytypid)
4054 {
4055 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4056 }
4057 else
4058 {
4059 targetrelation = relation_open(myrelid, AccessExclusiveLock);
4060
4061 /*
4062 * don't tell it whether we're recursing; we allow changing typed
4063 * tables here
4064 */
4065 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4066
4067 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4068 }
4069
4070 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4071 if (!HeapTupleIsValid(tuple))
4072 elog(ERROR, "cache lookup failed for constraint %u",
4073 constraintOid);
4074 con = (Form_pg_constraint) GETSTRUCT(tuple);
4075
4076 if (myrelid &&
4077 (con->contype == CONSTRAINT_CHECK ||
4078 con->contype == CONSTRAINT_NOTNULL) &&
4079 !con->connoinherit)
4080 {
4081 if (recurse)
4082 {
4083 List *child_oids,
4084 *child_numparents;
4085 ListCell *lo,
4086 *li;
4087
4088 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4089 &child_numparents);
4090
4091 forboth(lo, child_oids, li, child_numparents)
4092 {
4093 Oid childrelid = lfirst_oid(lo);
4094 int numparents = lfirst_int(li);
4095
4096 if (childrelid == myrelid)
4097 continue;
4098
4099 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4100 }
4101 }
4102 else
4103 {
4104 if (expected_parents == 0 &&
4106 ereport(ERROR,
4107 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4108 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4109 oldconname)));
4110 }
4111
4112 if (con->coninhcount > expected_parents)
4113 ereport(ERROR,
4114 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4115 errmsg("cannot rename inherited constraint \"%s\"",
4116 oldconname)));
4117 }
4118
4119 if (con->conindid
4120 && (con->contype == CONSTRAINT_PRIMARY
4121 || con->contype == CONSTRAINT_UNIQUE
4122 || con->contype == CONSTRAINT_EXCLUSION))
4123 /* rename the index; this renames the constraint as well */
4124 RenameRelationInternal(con->conindid, newconname, false, true);
4125 else
4126 RenameConstraintById(constraintOid, newconname);
4127
4128 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4129
4130 ReleaseSysCache(tuple);
4131
4132 if (targetrelation)
4133 {
4134 /*
4135 * Invalidate relcache so as others can see the new constraint name.
4136 */
4137 CacheInvalidateRelcache(targetrelation);
4138
4139 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4140 }
4141
4142 return address;
4143}
void RenameConstraintById(Oid conId, const char *newname)
Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
Oid get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
static ObjectAddress rename_constraint_internal(Oid myrelid, Oid mytypid, const char *oldconname, const char *newconname, bool recurse, bool recursing, int expected_parents)
Definition: tablecmds.c:4037

References AccessExclusiveLock, Assert(), CacheInvalidateRelcache(), elog, ereport, errcode(), errmsg(), ERROR, find_all_inheritors(), find_inheritance_children(), forboth, get_domain_constraint_oid(), get_relation_constraint_oid(), GETSTRUCT(), HeapTupleIsValid, InvalidOid, lfirst_int, lfirst_oid, NIL, NoLock, ObjectAddressSet, ObjectIdGetDatum(), relation_close(), relation_open(), RelationGetForm, ReleaseSysCache(), rename_constraint_internal(), renameatt_check(), RenameConstraintById(), RenameRelationInternal(), and SearchSysCache1().

Referenced by rename_constraint_internal(), and RenameConstraint().

◆ renameatt()

ObjectAddress renameatt ( RenameStmt stmt)

Definition at line 3999 of file tablecmds.c.

4000{
4001 Oid relid;
4003 ObjectAddress address;
4004
4005 /* lock level taken here should match renameatt_internal */
4007 stmt->missing_ok ? RVR_MISSING_OK : 0,
4009 NULL);
4010
4011 if (!OidIsValid(relid))
4012 {
4014 (errmsg("relation \"%s\" does not exist, skipping",
4015 stmt->relation->relname)));
4016 return InvalidObjectAddress;
4017 }
4018
4019 attnum =
4020 renameatt_internal(relid,
4021 stmt->subname, /* old att name */
4022 stmt->newname, /* new att name */
4023 stmt->relation->inh, /* recursive? */
4024 false, /* recursing? */
4025 0, /* expected inhcount */
4026 stmt->behavior);
4027
4028 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4029
4030 return address;
4031}
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition: tablecmds.c:3834
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:3979

References AccessExclusiveLock, attnum, ereport, errmsg(), InvalidObjectAddress, NOTICE, ObjectAddressSubSet, OidIsValid, RangeVarCallbackForRenameAttribute(), RangeVarGetRelidExtended(), renameatt_internal(), RVR_MISSING_OK, and stmt.

Referenced by ExecRenameStmt().

◆ renameatt_check()

static void renameatt_check ( Oid  myrelid,
Form_pg_class  classform,
bool  recursing 
)
static

Definition at line 3785 of file tablecmds.c.

3786{
3787 char relkind = classform->relkind;
3788
3789 if (classform->reloftype && !recursing)
3790 ereport(ERROR,
3791 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3792 errmsg("cannot rename column of typed table")));
3793
3794 /*
3795 * Renaming the columns of sequences or toast tables doesn't actually
3796 * break anything from the system's point of view, since internal
3797 * references are by attnum. But it doesn't seem right to allow users to
3798 * change names that are hardcoded into the system, hence the following
3799 * restriction.
3800 */
3801 if (relkind != RELKIND_RELATION &&
3802 relkind != RELKIND_VIEW &&
3803 relkind != RELKIND_MATVIEW &&
3804 relkind != RELKIND_COMPOSITE_TYPE &&
3805 relkind != RELKIND_INDEX &&
3806 relkind != RELKIND_PARTITIONED_INDEX &&
3807 relkind != RELKIND_FOREIGN_TABLE &&
3808 relkind != RELKIND_PARTITIONED_TABLE)
3809 ereport(ERROR,
3810 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3811 errmsg("cannot rename columns of relation \"%s\"",
3812 NameStr(classform->relname)),
3814
3815 /*
3816 * permissions checking. only the owner of a class can change its schema.
3817 */
3818 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3820 NameStr(classform->relname));
3821 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3822 ereport(ERROR,
3823 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3824 errmsg("permission denied: \"%s\" is a system catalog",
3825 NameStr(classform->relname))));
3826}

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, ereport, errcode(), errdetail_relkind_not_supported(), errmsg(), ERROR, get_rel_relkind(), get_relkind_objtype(), GetUserId(), IsSystemClass(), NameStr, and object_ownercheck().

Referenced by RangeVarCallbackForRenameAttribute(), rename_constraint_internal(), and renameatt_internal().

◆ renameatt_internal()

static AttrNumber renameatt_internal ( Oid  myrelid,
const char *  oldattname,
const char *  newattname,
bool  recurse,
bool  recursing,
int  expected_parents,
DropBehavior  behavior 
)
static

Definition at line 3834 of file tablecmds.c.

3841{
3842 Relation targetrelation;
3843 Relation attrelation;
3844 HeapTuple atttup;
3845 Form_pg_attribute attform;
3847
3848 /*
3849 * Grab an exclusive lock on the target table, which we will NOT release
3850 * until end of transaction.
3851 */
3852 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3853 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3854
3855 /*
3856 * if the 'recurse' flag is set then we are supposed to rename this
3857 * attribute in all classes that inherit from 'relname' (as well as in
3858 * 'relname').
3859 *
3860 * any permissions or problems with duplicate attributes will cause the
3861 * whole transaction to abort, which is what we want -- all or nothing.
3862 */
3863 if (recurse)
3864 {
3865 List *child_oids,
3866 *child_numparents;
3867 ListCell *lo,
3868 *li;
3869
3870 /*
3871 * we need the number of parents for each child so that the recursive
3872 * calls to renameatt() can determine whether there are any parents
3873 * outside the inheritance hierarchy being processed.
3874 */
3875 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3876 &child_numparents);
3877
3878 /*
3879 * find_all_inheritors does the recursive search of the inheritance
3880 * hierarchy, so all we have to do is process all of the relids in the
3881 * list that it returns.
3882 */
3883 forboth(lo, child_oids, li, child_numparents)
3884 {
3885 Oid childrelid = lfirst_oid(lo);
3886 int numparents = lfirst_int(li);
3887
3888 if (childrelid == myrelid)
3889 continue;
3890 /* note we need not recurse again */
3891 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3892 }
3893 }
3894 else
3895 {
3896 /*
3897 * If we are told not to recurse, there had better not be any child
3898 * tables; else the rename would put them out of step.
3899 *
3900 * expected_parents will only be 0 if we are not already recursing.
3901 */
3902 if (expected_parents == 0 &&
3904 ereport(ERROR,
3905 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3906 errmsg("inherited column \"%s\" must be renamed in child tables too",
3907 oldattname)));
3908 }
3909
3910 /* rename attributes in typed tables of composite type */
3911 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3912 {
3913 List *child_oids;
3914 ListCell *lo;
3915
3916 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3917 RelationGetRelationName(targetrelation),
3918 behavior);
3919
3920 foreach(lo, child_oids)
3921 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3922 }
3923
3924 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3925
3926 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3927 if (!HeapTupleIsValid(atttup))
3928 ereport(ERROR,
3929 (errcode(ERRCODE_UNDEFINED_COLUMN),
3930 errmsg("column \"%s\" does not exist",
3931 oldattname)));
3932 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3933
3934 attnum = attform->attnum;
3935 if (attnum <= 0)
3936 ereport(ERROR,
3937 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3938 errmsg("cannot rename system column \"%s\"",
3939 oldattname)));
3940
3941 /*
3942 * if the attribute is inherited, forbid the renaming. if this is a
3943 * top-level call to renameatt(), then expected_parents will be 0, so the
3944 * effect of this code will be to prohibit the renaming if the attribute
3945 * is inherited at all. if this is a recursive call to renameatt(),
3946 * expected_parents will be the number of parents the current relation has
3947 * within the inheritance hierarchy being processed, so we'll prohibit the
3948 * renaming only if there are additional parents from elsewhere.
3949 */
3950 if (attform->attinhcount > expected_parents)
3951 ereport(ERROR,
3952 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3953 errmsg("cannot rename inherited column \"%s\"",
3954 oldattname)));
3955
3956 /* new name should not already exist */
3957 (void) check_for_column_name_collision(targetrelation, newattname, false);
3958
3959 /* apply the update */
3960 namestrcpy(&(attform->attname), newattname);
3961
3962 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3963
3964 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3965
3966 heap_freetuple(atttup);
3967
3968 table_close(attrelation, RowExclusiveLock);
3969
3970 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3971
3972 return attnum;
3973}
void namestrcpy(Name name, const char *str)
Definition: name.c:233

References AccessExclusiveLock, attnum, CatalogTupleUpdate(), check_for_column_name_collision(), ereport, errcode(), errmsg(), ERROR, find_all_inheritors(), find_inheritance_children(), find_typed_table_dependencies(), forboth, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, lfirst_int, lfirst_oid, namestrcpy(), NIL, NoLock, RelationData::rd_rel, relation_close(), relation_open(), RelationGetForm, RelationGetRelationName, renameatt_check(), renameatt_internal(), RowExclusiveLock, SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by renameatt(), and renameatt_internal().

◆ RenameConstraint()

ObjectAddress RenameConstraint ( RenameStmt stmt)

Definition at line 4146 of file tablecmds.c.

4147{
4148 Oid relid = InvalidOid;
4149 Oid typid = InvalidOid;
4150
4151 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4152 {
4153 Relation rel;
4154 HeapTuple tup;
4155
4156 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4157 rel = table_open(TypeRelationId, RowExclusiveLock);
4158 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4159 if (!HeapTupleIsValid(tup))
4160 elog(ERROR, "cache lookup failed for type %u", typid);
4161 checkDomainOwner(tup);
4162 ReleaseSysCache(tup);
4163 table_close(rel, NoLock);
4164 }
4165 else
4166 {
4167 /* lock level taken here should match rename_constraint_internal */
4169 stmt->missing_ok ? RVR_MISSING_OK : 0,
4171 NULL);
4172 if (!OidIsValid(relid))
4173 {
4175 (errmsg("relation \"%s\" does not exist, skipping",
4176 stmt->relation->relname)));
4177 return InvalidObjectAddress;
4178 }
4179 }
4180
4181 return
4182 rename_constraint_internal(relid, typid,
4183 stmt->subname,
4184 stmt->newname,
4185 (stmt->relation &&
4186 stmt->relation->inh), /* recursive? */
4187 false, /* recursing? */
4188 0 /* expected inhcount */ );
4189}
void checkDomainOwner(HeapTuple tup)
Definition: typecmds.c:3477

References AccessExclusiveLock, castNode, checkDomainOwner(), elog, ereport, errmsg(), ERROR, HeapTupleIsValid, InvalidObjectAddress, InvalidOid, makeTypeNameFromNameList(), NoLock, NOTICE, OBJECT_DOMCONSTRAINT, ObjectIdGetDatum(), OidIsValid, RangeVarCallbackForRenameAttribute(), RangeVarGetRelidExtended(), ReleaseSysCache(), rename_constraint_internal(), RowExclusiveLock, RVR_MISSING_OK, SearchSysCache1(), stmt, table_close(), table_open(), and typenameTypeId().

Referenced by ExecRenameStmt().

◆ RenameRelation()

ObjectAddress RenameRelation ( RenameStmt stmt)

Definition at line 4196 of file tablecmds.c.

4197{
4198 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4199 Oid relid;
4200 ObjectAddress address;
4201
4202 /*
4203 * Grab an exclusive lock on the target table, index, sequence, view,
4204 * materialized view, or foreign table, which we will NOT release until
4205 * end of transaction.
4206 *
4207 * Lock level used here should match RenameRelationInternal, to avoid lock
4208 * escalation. However, because ALTER INDEX can be used with any relation
4209 * type, we mustn't believe without verification.
4210 */
4211 for (;;)
4212 {
4213 LOCKMODE lockmode;
4214 char relkind;
4215 bool obj_is_index;
4216
4217 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4218
4219 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4220 stmt->missing_ok ? RVR_MISSING_OK : 0,
4222 stmt);
4223
4224 if (!OidIsValid(relid))
4225 {
4227 (errmsg("relation \"%s\" does not exist, skipping",
4228 stmt->relation->relname)));
4229 return InvalidObjectAddress;
4230 }
4231
4232 /*
4233 * We allow mismatched statement and object types (e.g., ALTER INDEX
4234 * to rename a table), but we might've used the wrong lock level. If
4235 * that happens, retry with the correct lock level. We don't bother
4236 * if we already acquired AccessExclusiveLock with an index, however.
4237 */
4238 relkind = get_rel_relkind(relid);
4239 obj_is_index = (relkind == RELKIND_INDEX ||
4240 relkind == RELKIND_PARTITIONED_INDEX);
4241 if (obj_is_index || is_index_stmt == obj_is_index)
4242 break;
4243
4244 UnlockRelationOid(relid, lockmode);
4245 is_index_stmt = obj_is_index;
4246 }
4247
4248 /* Do the work */
4249 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4250
4251 ObjectAddressSet(address, RelationRelationId, relid);
4252
4253 return address;
4254}

References AccessExclusiveLock, ereport, errmsg(), get_rel_relkind(), InvalidObjectAddress, NOTICE, OBJECT_INDEX, ObjectAddressSet, OidIsValid, RangeVarCallbackForAlterRelation(), RangeVarGetRelidExtended(), RenameRelationInternal(), RVR_MISSING_OK, ShareUpdateExclusiveLock, stmt, and UnlockRelationOid().

Referenced by ExecRenameStmt().

◆ RenameRelationInternal()

void RenameRelationInternal ( Oid  myrelid,
const char *  newrelname,
bool  is_internal,
bool  is_index 
)

Definition at line 4260 of file tablecmds.c.

4261{
4262 Relation targetrelation;
4263 Relation relrelation; /* for RELATION relation */
4264 ItemPointerData otid;
4265 HeapTuple reltup;
4266 Form_pg_class relform;
4267 Oid namespaceId;
4268
4269 /*
4270 * Grab a lock on the target relation, which we will NOT release until end
4271 * of transaction. We need at least a self-exclusive lock so that
4272 * concurrent DDL doesn't overwrite the rename if they start updating
4273 * while still seeing the old version. The lock also guards against
4274 * triggering relcache reloads in concurrent sessions, which might not
4275 * handle this information changing under them. For indexes, we can use a
4276 * reduced lock level because RelationReloadIndexInfo() handles indexes
4277 * specially.
4278 */
4279 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4280 namespaceId = RelationGetNamespace(targetrelation);
4281
4282 /*
4283 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4284 */
4285 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4286
4287 reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4288 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4289 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4290 otid = reltup->t_self;
4291 relform = (Form_pg_class) GETSTRUCT(reltup);
4292
4293 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4294 ereport(ERROR,
4295 (errcode(ERRCODE_DUPLICATE_TABLE),
4296 errmsg("relation \"%s\" already exists",
4297 newrelname)));
4298
4299 /*
4300 * RenameRelation is careful not to believe the caller's idea of the
4301 * relation kind being handled. We don't have to worry about this, but
4302 * let's not be totally oblivious to it. We can process an index as
4303 * not-an-index, but not the other way around.
4304 */
4305 Assert(!is_index ||
4306 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4307 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4308
4309 /*
4310 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4311 * because it's a copy...)
4312 */
4313 namestrcpy(&(relform->relname), newrelname);
4314
4315 CatalogTupleUpdate(relrelation, &otid, reltup);
4316 UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4317
4318 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4319 InvalidOid, is_internal);
4320
4321 heap_freetuple(reltup);
4322 table_close(relrelation, RowExclusiveLock);
4323
4324 /*
4325 * Also rename the associated type, if any.
4326 */
4327 if (OidIsValid(targetrelation->rd_rel->reltype))
4328 RenameTypeInternal(targetrelation->rd_rel->reltype,
4329 newrelname, namespaceId);
4330
4331 /*
4332 * Also rename the associated constraint, if any.
4333 */
4334 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4335 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4336 {
4337 Oid constraintId = get_index_constraint(myrelid);
4338
4339 if (OidIsValid(constraintId))
4340 RenameConstraintById(constraintId, newrelname);
4341 }
4342
4343 /*
4344 * Close rel, but keep lock!
4345 */
4346 relation_close(targetrelation, NoLock);
4347}
void RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
Definition: pg_type.c:765

References AccessExclusiveLock, Assert(), CatalogTupleUpdate(), elog, ereport, errcode(), errmsg(), ERROR, get_index_constraint(), get_relname_relid(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InplaceUpdateTupleLock, InvalidOid, InvokeObjectPostAlterHookArg, namestrcpy(), NoLock, ObjectIdGetDatum(), OidIsValid, RelationData::rd_rel, relation_close(), relation_open(), RelationGetNamespace, RenameConstraintById(), RenameTypeInternal(), RowExclusiveLock, SearchSysCacheLockedCopy1(), ShareUpdateExclusiveLock, HeapTupleData::t_self, table_close(), table_open(), and UnlockTuple().

Referenced by ATExecAddIndexConstraint(), finish_heap_swap(), rename_constraint_internal(), RenameRelation(), and RenameType().

◆ ResetRelRewrite()

void ResetRelRewrite ( Oid  myrelid)

Definition at line 4353 of file tablecmds.c.

4354{
4355 Relation relrelation; /* for RELATION relation */
4356 HeapTuple reltup;
4357 Form_pg_class relform;
4358
4359 /*
4360 * Find relation's pg_class tuple.
4361 */
4362 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4363
4364 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4365 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4366 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4367 relform = (Form_pg_class) GETSTRUCT(reltup);
4368
4369 /*
4370 * Update pg_class tuple.
4371 */
4372 relform->relrewrite = InvalidOid;
4373
4374 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4375
4376 heap_freetuple(reltup);
4377 table_close(relrelation, RowExclusiveLock);
4378}

References CatalogTupleUpdate(), elog, ERROR, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidOid, ObjectIdGetDatum(), RowExclusiveLock, SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), and table_open().

Referenced by finish_heap_swap().

◆ set_attnotnull()

static void set_attnotnull ( List **  wqueue,
Relation  rel,
AttrNumber  attnum,
bool  is_valid,
bool  queue_validation 
)
static

Definition at line 7830 of file tablecmds.c.

7832{
7833 Form_pg_attribute attr;
7834 CompactAttribute *thisatt;
7835
7836 Assert(!queue_validation || wqueue);
7837
7839
7840 /*
7841 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7842 * attribute.
7843 */
7844 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7845 if (attr->attisdropped)
7846 return;
7847
7848 if (!attr->attnotnull)
7849 {
7850 Relation attr_rel;
7851 HeapTuple tuple;
7852
7853 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7854
7856 if (!HeapTupleIsValid(tuple))
7857 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7858 attnum, RelationGetRelid(rel));
7859
7860 thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7862
7863 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7864
7865 attr->attnotnull = true;
7866 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7867
7868 /*
7869 * If the nullness isn't already proven by validated constraints, have
7870 * ALTER TABLE phase 3 test for it.
7871 */
7872 if (queue_validation && wqueue &&
7874 {
7875 AlteredTableInfo *tab;
7876
7877 tab = ATGetQueueEntry(wqueue, rel);
7878 tab->verify_new_notnull = true;
7879 }
7880
7882
7883 table_close(attr_rel, RowExclusiveLock);
7884 heap_freetuple(tuple);
7885 }
7886 else
7887 {
7889 }
7890}
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition: tablecmds.c:8079

References Assert(), ATGetQueueEntry(), CompactAttribute::attnullability, ATTNULLABLE_VALID, attnum, CacheInvalidateRelcache(), CatalogTupleUpdate(), CheckAlterTableIsSafe(), CommandCounterIncrement(), elog, ERROR, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, NotNullImpliedByRelConstraints(), RelationGetDescr, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttNum(), HeapTupleData::t_self, table_close(), table_open(), TupleDescAttr(), TupleDescCompactAttr(), and AlteredTableInfo::verify_new_notnull.

Referenced by ATAddCheckNNConstraint(), ATExecSetNotNull(), DefineRelation(), and QueueNNConstraintValidation().

◆ SetIndexStorageProperties()

static void SetIndexStorageProperties ( Relation  rel,
Relation  attrelation,
AttrNumber  attnum,
bool  setstorage,
char  newstorage,
bool  setcompression,
char  newcompression,
LOCKMODE  lockmode 
)
static

Definition at line 9095 of file tablecmds.c.

9100{
9101 ListCell *lc;
9102
9103 foreach(lc, RelationGetIndexList(rel))
9104 {
9105 Oid indexoid = lfirst_oid(lc);
9106 Relation indrel;
9107 AttrNumber indattnum = 0;
9108 HeapTuple tuple;
9109
9110 indrel = index_open(indexoid, lockmode);
9111
9112 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9113 {
9114 if (indrel->rd_index->indkey.values[i] == attnum)
9115 {
9116 indattnum = i + 1;
9117 break;
9118 }
9119 }
9120
9121 if (indattnum == 0)
9122 {
9123 index_close(indrel, lockmode);
9124 continue;
9125 }
9126
9127 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9128
9129 if (HeapTupleIsValid(tuple))
9130 {
9131 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9132
9133 if (setstorage)
9134 attrtuple->attstorage = newstorage;
9135
9136 if (setcompression)
9137 attrtuple->attcompression = newcompression;
9138
9139 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9140
9141 InvokeObjectPostAlterHook(RelationRelationId,
9142 RelationGetRelid(rel),
9143 attrtuple->attnum);
9144
9145 heap_freetuple(tuple);
9146 }
9147
9148 index_close(indrel, lockmode);
9149 }
9150}

References attnum, CatalogTupleUpdate(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, i, index_close(), index_open(), InvokeObjectPostAlterHook, lfirst_oid, RelationData::rd_index, RelationGetIndexList(), RelationGetRelid, SearchSysCacheCopyAttNum(), and HeapTupleData::t_self.

Referenced by ATExecSetCompression(), and ATExecSetStorage().

◆ SetRelationHasSubclass()

void SetRelationHasSubclass ( Oid  relationId,
bool  relhassubclass 
)

Definition at line 3637 of file tablecmds.c.

3638{
3639 Relation relationRelation;
3640 HeapTuple tuple;
3641 Form_pg_class classtuple;
3642
3644 ShareUpdateExclusiveLock, false) ||
3645 CheckRelationOidLockedByMe(relationId,
3646 ShareRowExclusiveLock, true));
3647
3648 /*
3649 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3650 */
3651 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3652 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3653 if (!HeapTupleIsValid(tuple))
3654 elog(ERROR, "cache lookup failed for relation %u", relationId);
3655 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3656
3657 if (classtuple->relhassubclass != relhassubclass)
3658 {
3659 classtuple->relhassubclass = relhassubclass;
3660 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3661 }
3662 else
3663 {
3664 /* no need to change tuple, but force relcache rebuild anyway */
3666 }
3667
3668 heap_freetuple(tuple);
3669 table_close(relationRelation, RowExclusiveLock);
3670}
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1665

References Assert(), CacheInvalidateRelcacheByTuple(), CatalogTupleUpdate(), CheckRelationOidLockedByMe(), elog, ERROR, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, ObjectIdGetDatum(), RowExclusiveLock, SearchSysCacheCopy1, ShareRowExclusiveLock, ShareUpdateExclusiveLock, HeapTupleData::t_self, table_close(), and table_open().

Referenced by acquire_inherited_sample_rows(), index_create(), IndexSetParentIndex(), and StoreCatalogInheritance1().

◆ SetRelationTableSpace()

void SetRelationTableSpace ( Relation  rel,
Oid  newTableSpaceId,
RelFileNumber  newRelFilenumber 
)

Definition at line 3740 of file tablecmds.c.

3743{
3744 Relation pg_class;
3745 HeapTuple tuple;
3746 ItemPointerData otid;
3747 Form_pg_class rd_rel;
3748 Oid reloid = RelationGetRelid(rel);
3749
3750 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3751
3752 /* Get a modifiable copy of the relation's pg_class row. */
3753 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3754
3755 tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3756 if (!HeapTupleIsValid(tuple))
3757 elog(ERROR, "cache lookup failed for relation %u", reloid);
3758 otid = tuple->t_self;
3759 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3760
3761 /* Update the pg_class row. */
3762 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3763 InvalidOid : newTableSpaceId;
3764 if (RelFileNumberIsValid(newRelFilenumber))
3765 rd_rel->relfilenode = newRelFilenumber;
3766 CatalogTupleUpdate(pg_class, &otid, tuple);
3767 UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3768
3769 /*
3770 * Record dependency on tablespace. This is only required for relations
3771 * that have no physical storage.
3772 */
3773 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3774 changeDependencyOnTablespace(RelationRelationId, reloid,
3775 rd_rel->reltablespace);
3776
3777 heap_freetuple(tuple);
3778 table_close(pg_class, RowExclusiveLock);
3779}
void changeDependencyOnTablespace(Oid classId, Oid objectId, Oid newTablespaceId)
Definition: pg_shdepend.c:391

References Assert(), CatalogTupleUpdate(), changeDependencyOnTablespace(), CheckRelationTableSpaceMove(), elog, ERROR, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InplaceUpdateTupleLock, InvalidOid, MyDatabaseTableSpace, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetRelid, RelFileNumberIsValid, RowExclusiveLock, SearchSysCacheLockedCopy1(), HeapTupleData::t_self, table_close(), table_open(), and UnlockTuple().

Referenced by ATExecSetTableSpace(), ATExecSetTableSpaceNoStorage(), and reindex_index().

◆ storage_name()

static const char * storage_name ( char  c)
static

Definition at line 2451 of file tablecmds.c.

2452{
2453 switch (c)
2454 {
2455 case TYPSTORAGE_PLAIN:
2456 return "PLAIN";
2457 case TYPSTORAGE_EXTERNAL:
2458 return "EXTERNAL";
2459 case TYPSTORAGE_EXTENDED:
2460 return "EXTENDED";
2461 case TYPSTORAGE_MAIN:
2462 return "MAIN";
2463 default:
2464 return "???";
2465 }
2466}
char * c

Referenced by MergeChildAttribute(), and MergeInheritedAttribute().

◆ StoreCatalogInheritance()

static void StoreCatalogInheritance ( Oid  relationId,
List supers,
bool  child_is_partition 
)
static

Definition at line 3511 of file tablecmds.c.

3513{
3514 Relation relation;
3515 int32 seqNumber;
3516 ListCell *entry;
3517
3518 /*
3519 * sanity checks
3520 */
3521 Assert(OidIsValid(relationId));
3522
3523 if (supers == NIL)
3524 return;
3525
3526 /*
3527 * Store INHERITS information in pg_inherits using direct ancestors only.
3528 * Also enter dependencies on the direct ancestors, and make sure they are
3529 * marked with relhassubclass = true.
3530 *
3531 * (Once upon a time, both direct and indirect ancestors were found here
3532 * and then entered into pg_ipl. Since that catalog doesn't exist
3533 * anymore, there's no need to look for indirect ancestors.)
3534 */
3535 relation = table_open(InheritsRelationId, RowExclusiveLock);
3536
3537 seqNumber = 1;
3538 foreach(entry, supers)
3539 {
3540 Oid parentOid = lfirst_oid(entry);
3541
3542 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3543 child_is_partition);
3544 seqNumber++;
3545 }
3546
3547 table_close(relation, RowExclusiveLock);
3548}

References Assert(), lfirst_oid, NIL, OidIsValid, RowExclusiveLock, StoreCatalogInheritance1(), table_close(), and table_open().

Referenced by DefineRelation().

◆ StoreCatalogInheritance1()

static void StoreCatalogInheritance1 ( Oid  relationId,
Oid  parentOid,
int32  seqNumber,
Relation  inhRelation,
bool  child_is_partition 
)
static

Definition at line 3555 of file tablecmds.c.

3558{
3559 ObjectAddress childobject,
3560 parentobject;
3561
3562 /* store the pg_inherits row */
3563 StoreSingleInheritance(relationId, parentOid, seqNumber);
3564
3565 /*
3566 * Store a dependency too
3567 */
3568 parentobject.classId = RelationRelationId;
3569 parentobject.objectId = parentOid;
3570 parentobject.objectSubId = 0;
3571 childobject.classId = RelationRelationId;
3572 childobject.objectId = relationId;
3573 childobject.objectSubId = 0;
3574
3575 recordDependencyOn(&childobject, &parentobject,
3576 child_dependency_type(child_is_partition));
3577
3578 /*
3579 * Post creation hook of this inheritance. Since object_access_hook
3580 * doesn't take multiple object identifiers, we relay oid of parent
3581 * relation using auxiliary_id argument.
3582 */
3583 InvokeObjectPostAlterHookArg(InheritsRelationId,
3584 relationId, 0,
3585 parentOid, false);
3586
3587 /*
3588 * Mark the parent as having subclasses.
3589 */
3590 SetRelationHasSubclass(parentOid, true);
3591}
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
Definition: pg_inherits.c:508
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition: tablecmds.c:3637

References child_dependency_type, ObjectAddress::classId, InvokeObjectPostAlterHookArg, ObjectAddress::objectId, ObjectAddress::objectSubId, recordDependencyOn(), SetRelationHasSubclass(), and StoreSingleInheritance().

Referenced by CreateInheritance(), and StoreCatalogInheritance().

◆ transformColumnNameList()

static int transformColumnNameList ( Oid  relId,
List colList,
int16 attnums,
Oid atttypids,
Oid attcollids 
)
static

Definition at line 13276 of file tablecmds.c.

13278{
13279 ListCell *l;
13280 int attnum;
13281
13282 attnum = 0;
13283 foreach(l, colList)
13284 {
13285 char *attname = strVal(lfirst(l));
13286 HeapTuple atttuple;
13287 Form_pg_attribute attform;
13288
13289 atttuple = SearchSysCacheAttName(relId, attname);
13290 if (!HeapTupleIsValid(atttuple))
13291 ereport(ERROR,
13292 (errcode(ERRCODE_UNDEFINED_COLUMN),
13293 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13294 attname)));
13295 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13296 if (attform->attnum < 0)
13297 ereport(ERROR,
13298 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13299 errmsg("system columns cannot be used in foreign keys")));
13300 if (attnum >= INDEX_MAX_KEYS)
13301 ereport(ERROR,
13302 (errcode(ERRCODE_TOO_MANY_COLUMNS),
13303 errmsg("cannot have more than %d keys in a foreign key",
13304 INDEX_MAX_KEYS)));
13305 attnums[attnum] = attform->attnum;
13306 if (atttypids != NULL)
13307 atttypids[attnum] = attform->atttypid;
13308 if (attcollids != NULL)
13309 attcollids[attnum] = attform->attcollation;
13310 ReleaseSysCache(atttuple);
13311 attnum++;
13312 }
13313
13314 return attnum;
13315}

References attname, attnum, ereport, errcode(), errmsg(), ERROR, GETSTRUCT(), HeapTupleIsValid, INDEX_MAX_KEYS, lfirst, ReleaseSysCache(), SearchSysCacheAttName(), and strVal.

Referenced by ATAddForeignKeyConstraint().

◆ transformFkeyCheckAttrs()

static Oid transformFkeyCheckAttrs ( Relation  pkrel,
int  numattrs,
int16 attnums,
bool  with_period,
Oid opclasses,
bool *  pk_has_without_overlaps 
)
static

Definition at line 13434 of file tablecmds.c.

13438{
13439 Oid indexoid = InvalidOid;
13440 bool found = false;
13441 bool found_deferrable = false;
13442 List *indexoidlist;
13443 ListCell *indexoidscan;
13444 int i,
13445 j;
13446
13447 /*
13448 * Reject duplicate appearances of columns in the referenced-columns list.
13449 * Such a case is forbidden by the SQL standard, and even if we thought it
13450 * useful to allow it, there would be ambiguity about how to match the
13451 * list to unique indexes (in particular, it'd be unclear which index
13452 * opclass goes with which FK column).
13453 */
13454 for (i = 0; i < numattrs; i++)
13455 {
13456 for (j = i + 1; j < numattrs; j++)
13457 {
13458 if (attnums[i] == attnums[j])
13459 ereport(ERROR,
13460 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13461 errmsg("foreign key referenced-columns list must not contain duplicates")));
13462 }
13463 }
13464
13465 /*
13466 * Get the list of index OIDs for the table from the relcache, and look up
13467 * each one in the pg_index syscache, and match unique indexes to the list
13468 * of attnums we are given.
13469 */
13470 indexoidlist = RelationGetIndexList(pkrel);
13471
13472 foreach(indexoidscan, indexoidlist)
13473 {
13474 HeapTuple indexTuple;
13475 Form_pg_index indexStruct;
13476
13477 indexoid = lfirst_oid(indexoidscan);
13478 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13479 if (!HeapTupleIsValid(indexTuple))
13480 elog(ERROR, "cache lookup failed for index %u", indexoid);
13481 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13482
13483 /*
13484 * Must have the right number of columns; must be unique (or if
13485 * temporal then exclusion instead) and not a partial index; forget it
13486 * if there are any expressions, too. Invalid indexes are out as well.
13487 */
13488 if (indexStruct->indnkeyatts == numattrs &&
13489 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13490 indexStruct->indisvalid &&
13491 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13492 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13493 {
13494 Datum indclassDatum;
13495 oidvector *indclass;
13496
13497 /* Must get indclass the hard way */
13498 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13499 Anum_pg_index_indclass);
13500 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13501
13502 /*
13503 * The given attnum list may match the index columns in any order.
13504 * Check for a match, and extract the appropriate opclasses while
13505 * we're at it.
13506 *
13507 * We know that attnums[] is duplicate-free per the test at the
13508 * start of this function, and we checked above that the number of
13509 * index columns agrees, so if we find a match for each attnums[]
13510 * entry then we must have a one-to-one match in some order.
13511 */
13512 for (i = 0; i < numattrs; i++)
13513 {
13514 found = false;
13515 for (j = 0; j < numattrs; j++)
13516 {
13517 if (attnums[i] == indexStruct->indkey.values[j])
13518 {
13519 opclasses[i] = indclass->values[j];
13520 found = true;
13521 break;
13522 }
13523 }
13524 if (!found)
13525 break;
13526 }
13527 /* The last attribute in the index must be the PERIOD FK part */
13528 if (found && with_period)
13529 {
13530 int16 periodattnum = attnums[numattrs - 1];
13531
13532 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13533 }
13534
13535 /*
13536 * Refuse to use a deferrable unique/primary key. This is per SQL
13537 * spec, and there would be a lot of interesting semantic problems
13538 * if we tried to allow it.
13539 */
13540 if (found && !indexStruct->indimmediate)
13541 {
13542 /*
13543 * Remember that we found an otherwise matching index, so that
13544 * we can generate a more appropriate error message.
13545 */
13546 found_deferrable = true;
13547 found = false;
13548 }
13549
13550 /* We need to know whether the index has WITHOUT OVERLAPS */
13551 if (found)
13552 *pk_has_without_overlaps = indexStruct->indisexclusion;
13553 }
13554 ReleaseSysCache(indexTuple);
13555 if (found)
13556 break;
13557 }
13558
13559 if (!found)
13560 {
13561 if (found_deferrable)
13562 ereport(ERROR,
13563 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13564 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13565 RelationGetRelationName(pkrel))));
13566 else
13567 ereport(ERROR,
13568 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13569 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13570 RelationGetRelationName(pkrel))));
13571 }
13572
13573 list_free(indexoidlist);
13574
13575 return indexoid;
13576}
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:456
Definition: c.h:697
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:704

References DatumGetPointer(), elog, ereport, errcode(), errmsg(), ERROR, GETSTRUCT(), heap_attisnull(), HeapTupleIsValid, i, InvalidOid, j, lfirst_oid, list_free(), ObjectIdGetDatum(), RelationGetIndexList(), RelationGetRelationName, ReleaseSysCache(), SearchSysCache1(), SysCacheGetAttrNotNull(), and oidvector::values.

Referenced by ATAddForeignKeyConstraint().

◆ transformFkeyGetPrimaryKey()

static int transformFkeyGetPrimaryKey ( Relation  pkrel,
Oid indexOid,
List **  attnamelist,
int16 attnums,
Oid atttypids,
Oid attcollids,
Oid opclasses,
bool *  pk_has_without_overlaps 
)
static

Definition at line 13331 of file tablecmds.c.

13335{
13336 List *indexoidlist;
13337 ListCell *indexoidscan;
13338 HeapTuple indexTuple = NULL;
13339 Form_pg_index indexStruct = NULL;
13340 Datum indclassDatum;
13341 oidvector *indclass;
13342 int i;
13343
13344 /*
13345 * Get the list of index OIDs for the table from the relcache, and look up
13346 * each one in the pg_index syscache until we find one marked primary key
13347 * (hopefully there isn't more than one such). Insist it's valid, too.
13348 */
13349 *indexOid = InvalidOid;
13350
13351 indexoidlist = RelationGetIndexList(pkrel);
13352
13353 foreach(indexoidscan, indexoidlist)
13354 {
13355 Oid indexoid = lfirst_oid(indexoidscan);
13356
13357 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13358 if (!HeapTupleIsValid(indexTuple))
13359 elog(ERROR, "cache lookup failed for index %u", indexoid);
13360 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13361 if (indexStruct->indisprimary && indexStruct->indisvalid)
13362 {
13363 /*
13364 * Refuse to use a deferrable primary key. This is per SQL spec,
13365 * and there would be a lot of interesting semantic problems if we
13366 * tried to allow it.
13367 */
13368 if (!indexStruct->indimmediate)
13369 ereport(ERROR,
13370 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13371 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13372 RelationGetRelationName(pkrel))));
13373
13374 *indexOid = indexoid;
13375 break;
13376 }
13377 ReleaseSysCache(indexTuple);
13378 }
13379
13380 list_free(indexoidlist);
13381
13382 /*
13383 * Check that we found it
13384 */
13385 if (!OidIsValid(*indexOid))
13386 ereport(ERROR,
13387 (errcode(ERRCODE_UNDEFINED_OBJECT),
13388 errmsg("there is no primary key for referenced table \"%s\"",
13389 RelationGetRelationName(pkrel))));
13390
13391 /* Must get indclass the hard way */
13392 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13393 Anum_pg_index_indclass);
13394 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13395
13396 /*
13397 * Now build the list of PK attributes from the indkey definition (we
13398 * assume a primary key cannot have expressional elements)
13399 */
13400 *attnamelist = NIL;
13401 for (i = 0; i < indexStruct->indnkeyatts; i++)
13402 {
13403 int pkattno = indexStruct->indkey.values[i];
13404
13405 attnums[i] = pkattno;
13406 atttypids[i] = attnumTypeId(pkrel, pkattno);
13407 attcollids[i] = attnumCollationId(pkrel, pkattno);
13408 opclasses[i] = indclass->values[i];
13409 *attnamelist = lappend(*attnamelist,
13410 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13411 }
13412
13413 *pk_has_without_overlaps = indexStruct->indisexclusion;
13414
13415 ReleaseSysCache(indexTuple);
13416
13417 return i;
13418}
Oid attnumCollationId(Relation rd, int attid)
Oid attnumTypeId(Relation rd, int attid)
const NameData * attnumAttName(Relation rd, int attid)

References attnumAttName(), attnumCollationId(), attnumTypeId(), DatumGetPointer(), elog, ereport, errcode(), errmsg(), ERROR, GETSTRUCT(), HeapTupleIsValid, i, InvalidOid, lappend(), lfirst_oid, list_free(), makeString(), NameStr, NIL, ObjectIdGetDatum(), OidIsValid, pstrdup(), RelationGetIndexList(), RelationGetRelationName, ReleaseSysCache(), SearchSysCache1(), SysCacheGetAttrNotNull(), and oidvector::values.

Referenced by ATAddForeignKeyConstraint().

◆ transformPartitionSpec()

static PartitionSpec * transformPartitionSpec ( Relation  rel,
PartitionSpec partspec 
)
static

Definition at line 19650 of file tablecmds.c.

19651{
19652 PartitionSpec *newspec;
19653 ParseState *pstate;
19654 ParseNamespaceItem *nsitem;
19655 ListCell *l;
19656
19657 newspec = makeNode(PartitionSpec);
19658
19659 newspec->strategy = partspec->strategy;
19660 newspec->partParams = NIL;
19661 newspec->location = partspec->location;
19662
19663 /* Check valid number of columns for strategy */
19664 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19665 list_length(partspec->partParams) != 1)
19666 ereport(ERROR,
19667 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19668 errmsg("cannot use \"list\" partition strategy with more than one column")));
19669
19670 /*
19671 * Create a dummy ParseState and insert the target relation as its sole
19672 * rangetable entry. We need a ParseState for transformExpr.
19673 */
19674 pstate = make_parsestate(NULL);
19675 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19676 NULL, false, true);
19677 addNSItemToQuery(pstate, nsitem, true, true, true);
19678
19679 /* take care of any partition expressions */
19680 foreach(l, partspec->partParams)
19681 {
19683
19684 if (pelem->expr)
19685 {
19686 /* Copy, to avoid scribbling on the input */
19687 pelem = copyObject(pelem);
19688
19689 /* Now do parse transformation of the expression */
19690 pelem->expr = transformExpr(pstate, pelem->expr,
19692
19693 /* we have to fix its collations too */
19694 assign_expr_collations(pstate, pelem->expr);
19695 }
19696
19697 newspec->partParams = lappend(newspec->partParams, pelem);
19698 }
19699
19700 return newspec;
19701}
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:118
@ EXPR_KIND_PARTITION_EXPRESSION
Definition: parse_node.h:80
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:883
List * partParams
Definition: parsenodes.h:897
ParseLoc location
Definition: parsenodes.h:898
PartitionStrategy strategy
Definition: parsenodes.h:896

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), assign_expr_collations(), copyObject, ereport, errcode(), errmsg(), ERROR, PartitionElem::expr, EXPR_KIND_PARTITION_EXPRESSION, lappend(), lfirst_node, list_length(), PartitionSpec::location, make_parsestate(), makeNode, NIL, PARTITION_STRATEGY_LIST, PartitionSpec::partParams, PartitionSpec::strategy, and transformExpr().

Referenced by DefineRelation().

◆ truncate_check_activity()

static void truncate_check_activity ( Relation  rel)
static

Definition at line 2428 of file tablecmds.c.

2429{
2430 /*
2431 * Don't allow truncate on temp tables of other backends ... their local
2432 * buffer manager is not going to cope.
2433 */
2434 if (RELATION_IS_OTHER_TEMP(rel))
2435 ereport(ERROR,
2436 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2437 errmsg("cannot truncate temporary tables of other sessions")));
2438
2439 /*
2440 * Also check for active uses of the relation in the current transaction,
2441 * including open scans and pending AFTER trigger events.
2442 */
2443 CheckTableNotInUse(rel, "TRUNCATE");
2444}

References CheckTableNotInUse(), ereport, errcode(), errmsg(), ERROR, and RELATION_IS_OTHER_TEMP.

Referenced by ExecuteTruncate(), and ExecuteTruncateGuts().

◆ truncate_check_perms()

static void truncate_check_perms ( Oid  relid,
Form_pg_class  reltuple 
)
static

Definition at line 2410 of file tablecmds.c.

2411{
2412 char *relname = NameStr(reltuple->relname);
2413 AclResult aclresult;
2414
2415 /* Permissions checks */
2416 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2417 if (aclresult != ACLCHECK_OK)
2418 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2419 relname);
2420}
#define ACL_TRUNCATE
Definition: parsenodes.h:80

References ACL_TRUNCATE, aclcheck_error(), ACLCHECK_OK, get_relkind_objtype(), GetUserId(), NameStr, pg_class_aclcheck(), and relname.

Referenced by ExecuteTruncateGuts(), and RangeVarCallbackForTruncate().

◆ truncate_check_rel()

static void truncate_check_rel ( Oid  relid,
Form_pg_class  reltuple 
)
static

Definition at line 2362 of file tablecmds.c.

2363{
2364 char *relname = NameStr(reltuple->relname);
2365
2366 /*
2367 * Only allow truncate on regular tables, foreign tables using foreign
2368 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2369 * latter are only being included here for the following checks; no
2370 * physical truncation will occur in their case.).
2371 */
2372 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2373 {
2374 Oid serverid = GetForeignServerIdByRelId(relid);
2375 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2376
2377 if (!fdwroutine->ExecForeignTruncate)
2378 ereport(ERROR,
2379 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2380 errmsg("cannot truncate foreign table \"%s\"",
2381 relname)));
2382 }
2383 else if (reltuple->relkind != RELKIND_RELATION &&
2384 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2385 ereport(ERROR,
2386 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2387 errmsg("\"%s\" is not a table", relname)));
2388
2389 /*
2390 * Most system catalogs can't be truncated at all, or at least not unless
2391 * allow_system_table_mods=on. As an exception, however, we allow
2392 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2393 * to change its relfilenode to match the old cluster, and allowing a
2394 * TRUNCATE command to be executed is the easiest way of doing that.
2395 */
2396 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2397 && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2398 ereport(ERROR,
2399 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2400 errmsg("permission denied: \"%s\" is a system catalog",
2401 relname)));
2402
2404}
bool IsBinaryUpgrade
Definition: globals.c:122
#define InvokeObjectTruncateHook(objectId)
Definition: objectaccess.h:191

References allowSystemTableMods, ereport, errcode(), errmsg(), ERROR, FdwRoutine::ExecForeignTruncate, GetFdwRoutineByServerId(), GetForeignServerIdByRelId(), InvokeObjectTruncateHook, IsBinaryUpgrade, IsSystemClass(), NameStr, and relname.

Referenced by ExecuteTruncate(), ExecuteTruncateGuts(), and RangeVarCallbackForTruncate().

◆ tryAttachPartitionForeignKey()

static bool tryAttachPartitionForeignKey ( List **  wqueue,
ForeignKeyCacheInfo fk,
Relation  partition,
Oid  parentConstrOid,
int  numfks,
AttrNumber mapped_conkey,
AttrNumber confkey,
Oid conpfeqop,
Oid  parentInsTrigger,
Oid  parentUpdTrigger,
Relation  trigrel 
)
static

Definition at line 11670 of file tablecmds.c.

11681{
11682 HeapTuple parentConstrTup;
11683 Form_pg_constraint parentConstr;
11684 HeapTuple partcontup;
11685 Form_pg_constraint partConstr;
11686
11687 parentConstrTup = SearchSysCache1(CONSTROID,
11688 ObjectIdGetDatum(parentConstrOid));
11689 if (!HeapTupleIsValid(parentConstrTup))
11690 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11691 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11692
11693 /*
11694 * Do some quick & easy initial checks. If any of these fail, we cannot
11695 * use this constraint.
11696 */
11697 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11698 {
11699 ReleaseSysCache(parentConstrTup);
11700 return false;
11701 }
11702 for (int i = 0; i < numfks; i++)
11703 {
11704 if (fk->conkey[i] != mapped_conkey[i] ||
11705 fk->confkey[i] != confkey[i] ||
11706 fk->conpfeqop[i] != conpfeqop[i])
11707 {
11708 ReleaseSysCache(parentConstrTup);
11709 return false;
11710 }
11711 }
11712
11713 /* Looks good so far; perform more extensive checks. */
11714 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11715 if (!HeapTupleIsValid(partcontup))
11716 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11717 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11718
11719 /*
11720 * An error should be raised if the constraint enforceability is
11721 * different. Returning false without raising an error, as we do for other
11722 * attributes, could lead to a duplicate constraint with the same
11723 * enforceability as the parent. While this may be acceptable, it may not
11724 * be ideal. Therefore, it's better to raise an error and allow the user
11725 * to correct the enforceability before proceeding.
11726 */
11727 if (partConstr->conenforced != parentConstr->conenforced)
11728 ereport(ERROR,
11729 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11730 errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11731 NameStr(parentConstr->conname),
11732 NameStr(partConstr->conname),
11733 RelationGetRelationName(partition))));
11734
11735 if (OidIsValid(partConstr->conparentid) ||
11736 partConstr->condeferrable != parentConstr->condeferrable ||
11737 partConstr->condeferred != parentConstr->condeferred ||
11738 partConstr->confupdtype != parentConstr->confupdtype ||
11739 partConstr->confdeltype != parentConstr->confdeltype ||
11740 partConstr->confmatchtype != parentConstr->confmatchtype)
11741 {
11742 ReleaseSysCache(parentConstrTup);
11743 ReleaseSysCache(partcontup);
11744 return false;
11745 }
11746
11747 ReleaseSysCache(parentConstrTup);
11748 ReleaseSysCache(partcontup);
11749
11750 /* Looks good! Attach this constraint. */
11751 AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11752 parentConstrOid, parentInsTrigger,
11753 parentUpdTrigger, trigrel);
11754
11755 return true;
11756}
static void AttachPartitionForeignKey(List **wqueue, Relation partition, Oid partConstrOid, Oid parentConstrOid, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11766

References AttachPartitionForeignKey(), ForeignKeyCacheInfo::confrelid, ForeignKeyCacheInfo::conoid, elog, ereport, errcode(), errmsg(), ERROR, GETSTRUCT(), HeapTupleIsValid, i, NameStr, ForeignKeyCacheInfo::nkeys, ObjectIdGetDatum(), OidIsValid, RelationGetRelationName, ReleaseSysCache(), and SearchSysCache1().

Referenced by addFkRecurseReferencing(), and CloneFkReferencing().

◆ TryReuseForeignKey()

static void TryReuseForeignKey ( Oid  oldId,
Constraint con 
)
static

Definition at line 15838 of file tablecmds.c.

15839{
15840 HeapTuple tup;
15841 Datum adatum;
15842 ArrayType *arr;
15843 Oid *rawarr;
15844 int numkeys;
15845 int i;
15846
15847 Assert(con->contype == CONSTR_FOREIGN);
15848 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15849
15850 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15851 if (!HeapTupleIsValid(tup)) /* should not happen */
15852 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15853
15854 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15855 Anum_pg_constraint_conpfeqop);
15856 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15857 numkeys = ARR_DIMS(arr)[0];
15858 /* test follows the one in ri_FetchConstraintInfo() */
15859 if (ARR_NDIM(arr) != 1 ||
15860 ARR_HASNULL(arr) ||
15861 ARR_ELEMTYPE(arr) != OIDOID)
15862 elog(ERROR, "conpfeqop is not a 1-D Oid array");
15863 rawarr = (Oid *) ARR_DATA_PTR(arr);
15864
15865 /* stash a List of the operator Oids in our Constraint node */
15866 for (i = 0; i < numkeys; i++)
15867 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15868
15869 ReleaseSysCache(tup);
15870}
#define ARR_NDIM(a)
Definition: array.h:290
#define ARR_DATA_PTR(a)
Definition: array.h:322
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_ELEMTYPE(a)
Definition: array.h:292
#define ARR_DIMS(a)
Definition: array.h:294
#define ARR_HASNULL(a)
Definition: array.h:291

References ARR_DATA_PTR, ARR_DIMS, ARR_ELEMTYPE, ARR_HASNULL, ARR_NDIM, Assert(), CONSTR_FOREIGN, Constraint::contype, DatumGetArrayTypeP, elog, ERROR, HeapTupleIsValid, i, lappend_oid(), NIL, ObjectIdGetDatum(), Constraint::old_conpfeqop, ReleaseSysCache(), SearchSysCache1(), and SysCacheGetAttrNotNull().

Referenced by ATPostAlterTypeParse().

◆ TryReuseIndex()

static void TryReuseIndex ( Oid  oldId,
IndexStmt stmt 
)
static

Definition at line 15809 of file tablecmds.c.

15810{
15811 if (CheckIndexCompatible(oldId,
15812 stmt->accessMethod,
15813 stmt->indexParams,
15814 stmt->excludeOpNames,
15815 stmt->iswithoutoverlaps))
15816 {
15817 Relation irel = index_open(oldId, NoLock);
15818
15819 /* If it's a partitioned index, there is no storage to share. */
15820 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15821 {
15822 stmt->oldNumber = irel->rd_locator.relNumber;
15823 stmt->oldCreateSubid = irel->rd_createSubid;
15824 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15825 }
15826 index_close(irel, NoLock);
15827 }
15828}
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
Definition: indexcmds.c:178

References CheckIndexCompatible(), index_close(), index_open(), NoLock, RelationData::rd_createSubid, RelationData::rd_firstRelfilelocatorSubid, RelationData::rd_locator, RelationData::rd_rel, RelFileLocator::relNumber, and stmt.

Referenced by ATPostAlterTypeParse().

◆ validateFkOnDeleteSetColumns()

static int validateFkOnDeleteSetColumns ( int  numfks,
const int16 fkattnums,
int  numfksetcols,
int16 fksetcolsattnums,
List fksetcols 
)
static

Definition at line 10611 of file tablecmds.c.

10614{
10615 int numcolsout = 0;
10616
10617 for (int i = 0; i < numfksetcols; i++)
10618 {
10619 int16 setcol_attnum = fksetcolsattnums[i];
10620 bool seen = false;
10621
10622 /* Make sure it's in fkattnums[] */
10623 for (int j = 0; j < numfks; j++)
10624 {
10625 if (fkattnums[j] == setcol_attnum)
10626 {
10627 seen = true;
10628 break;
10629 }
10630 }
10631
10632 if (!seen)
10633 {
10634 char *col = strVal(list_nth(fksetcols, i));
10635
10636 ereport(ERROR,
10637 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10638 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10639 }
10640
10641 /* Now check for dups */
10642 seen = false;
10643 for (int j = 0; j < numcolsout; j++)
10644 {
10645 if (fksetcolsattnums[j] == setcol_attnum)
10646 {
10647 seen = true;
10648 break;
10649 }
10650 }
10651 if (!seen)
10652 fksetcolsattnums[numcolsout++] = setcol_attnum;
10653 }
10654 return numcolsout;
10655}

References ereport, errcode(), errmsg(), ERROR, i, j, list_nth(), and strVal.

Referenced by ATAddForeignKeyConstraint().

◆ validateForeignKeyConstraint()

static void validateForeignKeyConstraint ( char *  conname,
Relation  rel,
Relation  pkrel,
Oid  pkindOid,
Oid  constraintOid,
bool  hasperiod 
)
static

Definition at line 13643 of file tablecmds.c.

13649{
13650 TupleTableSlot *slot;
13651 TableScanDesc scan;
13652 Trigger trig = {0};
13653 Snapshot snapshot;
13654 MemoryContext oldcxt;
13655 MemoryContext perTupCxt;
13656
13658 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13659
13660 /*
13661 * Build a trigger call structure; we'll need it either way.
13662 */
13663 trig.tgoid = InvalidOid;
13664 trig.tgname = conname;
13666 trig.tgisinternal = true;
13667 trig.tgconstrrelid = RelationGetRelid(pkrel);
13668 trig.tgconstrindid = pkindOid;
13669 trig.tgconstraint = constraintOid;
13670 trig.tgdeferrable = false;
13671 trig.tginitdeferred = false;
13672 /* we needn't fill in remaining fields */
13673
13674 /*
13675 * See if we can do it with a single LEFT JOIN query. A false result
13676 * indicates we must proceed with the fire-the-trigger method. We can't do
13677 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13678 * left joins.
13679 */
13680 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13681 return;
13682
13683 /*
13684 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13685 * if that tuple had just been inserted. If any of those fail, it should
13686 * ereport(ERROR) and that's that.
13687 */
13688 snapshot = RegisterSnapshot(GetLatestSnapshot());
13689 slot = table_slot_create(rel, NULL);
13690 scan = table_beginscan(rel, snapshot, 0, NULL);
13691
13693 "validateForeignKeyConstraint",
13695 oldcxt = MemoryContextSwitchTo(perTupCxt);
13696
13697 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13698 {
13699 LOCAL_FCINFO(fcinfo, 0);
13700 TriggerData trigdata = {0};
13701
13703
13704 /*
13705 * Make a call to the trigger function
13706 *
13707 * No parameters are passed, but we do set a context
13708 */
13709 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13710
13711 /*
13712 * We assume RI_FKey_check_ins won't look at flinfo...
13713 */
13714 trigdata.type = T_TriggerData;
13716 trigdata.tg_relation = rel;
13717 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13718 trigdata.tg_trigslot = slot;
13719 trigdata.tg_trigger = &trig;
13720
13721 fcinfo->context = (Node *) &trigdata;
13722
13723 RI_FKey_check_ins(fcinfo);
13724
13725 MemoryContextReset(perTupCxt);
13726 }
13727
13728 MemoryContextSwitchTo(oldcxt);
13729 MemoryContextDelete(perTupCxt);
13730 table_endscan(scan);
13731 UnregisterSnapshot(snapshot);
13733}
#define MemSet(start, val, len)
Definition: c.h:991
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1833
#define SizeForFunctionCallInfo(nargs)
Definition: fmgr.h:102
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
Datum RI_FKey_check_ins(PG_FUNCTION_ARGS)
Definition: ri_triggers.c:474
bool RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1519
NodeTag type
Definition: trigger.h:33
Relation tg_relation
Definition: trigger.h:35
TriggerEvent tg_event
Definition: trigger.h:34
TupleTableSlot * tg_trigslot
Definition: trigger.h:39
Trigger * tg_trigger
Definition: trigger.h:38
HeapTuple tg_trigtuple
Definition: trigger.h:36
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:92
#define TRIGGER_EVENT_ROW
Definition: trigger.h:98
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:92

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, CHECK_FOR_INTERRUPTS, CurrentMemoryContext, DEBUG1, ereport, errmsg_internal(), ExecDropSingleTupleTableSlot(), ExecFetchSlotHeapTuple(), ForwardScanDirection, GetLatestSnapshot(), InvalidOid, LOCAL_FCINFO, MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), MemSet, RegisterSnapshot(), RelationGetRelid, RI_FKey_check_ins(), RI_Initial_Check(), SizeForFunctionCallInfo, table_beginscan(), table_endscan(), table_scan_getnextslot(), table_slot_create(), TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, Trigger::tgconstraint, Trigger::tgconstrindid, Trigger::tgconstrrelid, Trigger::tgdeferrable, Trigger::tgenabled, Trigger::tginitdeferred, Trigger::tgisinternal, Trigger::tgname, Trigger::tgoid, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_ROW, TRIGGER_FIRES_ON_ORIGIN, TriggerData::type, and UnregisterSnapshot().

Referenced by ATRewriteTables().

◆ validatePartitionedIndex()

static void validatePartitionedIndex ( Relation  partedIdx,
Relation  partedTbl 
)
static

Definition at line 21719 of file tablecmds.c.

21720{
21721 Relation inheritsRel;
21722 SysScanDesc scan;
21724 int tuples = 0;
21725 HeapTuple inhTup;
21726 bool updated = false;
21727
21728 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21729
21730 /*
21731 * Scan pg_inherits for this parent index. Count each valid index we find
21732 * (verifying the pg_index entry for each), and if we reach the total
21733 * amount we expect, we can mark this parent index as valid.
21734 */
21735 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21736 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21737 BTEqualStrategyNumber, F_OIDEQ,
21739 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21740 NULL, 1, &key);
21741 while ((inhTup = systable_getnext(scan)) != NULL)
21742 {
21743 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21744 HeapTuple indTup;
21745 Form_pg_index indexForm;
21746
21747 indTup = SearchSysCache1(INDEXRELID,
21748 ObjectIdGetDatum(inhForm->inhrelid));
21749 if (!HeapTupleIsValid(indTup))
21750 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21751 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21752 if (indexForm->indisvalid)
21753 tuples += 1;
21754 ReleaseSysCache(indTup);
21755 }
21756
21757 /* Done with pg_inherits */
21758 systable_endscan(scan);
21759 table_close(inheritsRel, AccessShareLock);
21760
21761 /*
21762 * If we found as many inherited indexes as the partitioned table has
21763 * partitions, we're good; update pg_index to set indisvalid.
21764 */
21765 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21766 {
21767 Relation idxRel;
21768 HeapTuple indTup;
21769 Form_pg_index indexForm;
21770
21771 idxRel = table_open(IndexRelationId, RowExclusiveLock);
21772 indTup = SearchSysCacheCopy1(INDEXRELID,
21774 if (!HeapTupleIsValid(indTup))
21775 elog(ERROR, "cache lookup failed for index %u",
21776 RelationGetRelid(partedIdx));
21777 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21778
21779 indexForm->indisvalid = true;
21780 updated = true;
21781
21782 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21783
21785 heap_freetuple(indTup);
21786 }
21787
21788 /*
21789 * If this index is in turn a partition of a larger index, validating it
21790 * might cause the parent to become valid also. Try that.
21791 */
21792 if (updated && partedIdx->rd_rel->relispartition)
21793 {
21794 Oid parentIdxId,
21795 parentTblId;
21796 Relation parentIdx,
21797 parentTbl;
21798
21799 /* make sure we see the validation we just did */
21801
21802 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21803 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21804 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21805 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21806 Assert(!parentIdx->rd_index->indisvalid);
21807
21808 validatePartitionedIndex(parentIdx, parentTbl);
21809
21812 }
21813}

References AccessExclusiveLock, AccessShareLock, Assert(), BTEqualStrategyNumber, CatalogTupleUpdate(), CommandCounterIncrement(), elog, ERROR, get_partition_parent(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, sort-test::key, ObjectIdGetDatum(), RelationData::rd_index, RelationData::rd_rel, relation_close(), relation_open(), RelationGetPartitionDesc(), RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, ScanKeyInit(), SearchSysCache1(), SearchSysCacheCopy1, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), table_open(), and validatePartitionedIndex().

Referenced by ATExecAttachPartitionIdx(), and validatePartitionedIndex().

◆ verifyNotNullPKCompatible()

static void verifyNotNullPKCompatible ( HeapTuple  tuple,
const char *  colname 
)
static

Definition at line 9543 of file tablecmds.c.

9544{
9546
9547 if (conForm->contype != CONSTRAINT_NOTNULL)
9548 elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9549
9550 /* a NO INHERIT constraint is no good */
9551 if (conForm->connoinherit)
9552 ereport(ERROR,
9553 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9554 errmsg("cannot create primary key on column \"%s\"", colname),
9555 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9556 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9557 NameStr(conForm->conname), colname,
9558 get_rel_name(conForm->conrelid), "NO INHERIT"),
9559 errhint("You might need to make the existing constraint inheritable using %s.",
9560 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9561
9562 /* an unvalidated constraint is no good */
9563 if (!conForm->convalidated)
9564 ereport(ERROR,
9565 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9566 errmsg("cannot create primary key on column \"%s\"", colname),
9567 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9568 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9569 NameStr(conForm->conname), colname,
9570 get_rel_name(conForm->conrelid), "NOT VALID"),
9571 errhint("You might need to validate it using %s.",
9572 "ALTER TABLE ... VALIDATE CONSTRAINT"));
9573}

References elog, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, get_rel_name(), GETSTRUCT(), and NameStr.

Referenced by ATPrepAddPrimaryKey().

◆ verifyPartitionIndexNotNull()

static void verifyPartitionIndexNotNull ( IndexInfo iinfo,
Relation  partition 
)
static

Definition at line 21821 of file tablecmds.c.

21822{
21823 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21824 {
21826 iinfo->ii_IndexAttrNumbers[i] - 1);
21827
21828 if (!att->attnotnull)
21829 ereport(ERROR,
21830 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21831 errmsg("invalid primary key definition"),
21832 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21833 NameStr(att->attname),
21834 RelationGetRelationName(partition)));
21835 }
21836}
int ii_NumIndexKeyAttrs
Definition: execnodes.h:197
AttrNumber ii_IndexAttrNumbers[INDEX_MAX_KEYS]
Definition: execnodes.h:198

References ereport, errcode(), errdetail(), errmsg(), ERROR, i, IndexInfo::ii_IndexAttrNumbers, IndexInfo::ii_NumIndexKeyAttrs, NameStr, RelationGetDescr, RelationGetRelationName, and TupleDescAttr().

Referenced by ATExecAttachPartitionIdx().

Variable Documentation

◆ dropmsgstringarray

const struct dropmsgstrings dropmsgstringarray[]
static

Definition at line 255 of file tablecmds.c.

Referenced by DropErrorMsgNonExistent(), and DropErrorMsgWrongType().

◆ on_commits