Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
PostgreSQL Source Code git master
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. Unless
10716 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10717 * supplied name with an underscore and digit(s) appended.
10718 */
10720 RelationGetRelid(rel),
10721 constraintname))
10722 conname = ChooseConstraintName(constraintname,
10723 NULL,
10724 "",
10726 else
10727 conname = constraintname;
10728
10729 if (fkconstraint->conname == NULL)
10730 fkconstraint->conname = pstrdup(conname);
10731
10732 if (OidIsValid(parentConstr))
10733 {
10734 conislocal = false;
10735 coninhcount = 1;
10736 connoinherit = false;
10737 }
10738 else
10739 {
10740 conislocal = true;
10741 coninhcount = 0;
10742
10743 /*
10744 * always inherit for partitioned tables, never for legacy inheritance
10745 */
10746 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10747 }
10748
10749 /*
10750 * Record the FK constraint in pg_constraint.
10751 */
10752 constrOid = CreateConstraintEntry(conname,
10754 CONSTRAINT_FOREIGN,
10755 fkconstraint->deferrable,
10756 fkconstraint->initdeferred,
10757 fkconstraint->is_enforced,
10758 fkconstraint->initially_valid,
10759 parentConstr,
10760 RelationGetRelid(rel),
10761 fkattnum,
10762 numfks,
10763 numfks,
10764 InvalidOid, /* not a domain constraint */
10765 indexOid,
10766 RelationGetRelid(pkrel),
10767 pkattnum,
10768 pfeqoperators,
10769 ppeqoperators,
10770 ffeqoperators,
10771 numfks,
10772 fkconstraint->fk_upd_action,
10773 fkconstraint->fk_del_action,
10774 fkdelsetcols,
10775 numfkdelsetcols,
10776 fkconstraint->fk_matchtype,
10777 NULL, /* no exclusion constraint */
10778 NULL, /* no check constraint */
10779 NULL,
10780 conislocal, /* islocal */
10781 coninhcount, /* inhcount */
10782 connoinherit, /* conNoInherit */
10783 with_period, /* conPeriod */
10784 is_internal); /* is_internal */
10785
10786 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10787
10788 /*
10789 * In partitioning cases, create the dependency entries for this
10790 * constraint. (For non-partitioned cases, relevant entries were created
10791 * by CreateConstraintEntry.)
10792 *
10793 * On the referenced side, we need the constraint to have an internal
10794 * dependency on its parent constraint; this means that this constraint
10795 * cannot be dropped on its own -- only through the parent constraint. It
10796 * also means the containing partition cannot be dropped on its own, but
10797 * it can be detached, at which point this dependency is removed (after
10798 * verifying that no rows are referenced via this FK.)
10799 *
10800 * When processing the referencing side, we link the constraint via the
10801 * special partitioning dependencies: the parent constraint is the primary
10802 * dependent, and the partition on which the foreign key exists is the
10803 * secondary dependency. That way, this constraint is dropped if either
10804 * of these objects is.
10805 *
10806 * Note that this is only necessary for the subsidiary pg_constraint rows
10807 * in partitions; the topmost row doesn't need any of this.
10808 */
10809 if (OidIsValid(parentConstr))
10810 {
10811 ObjectAddress referenced;
10812
10813 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10814
10815 Assert(fkside != addFkBothSides);
10816 if (fkside == addFkReferencedSide)
10817 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10818 else
10819 {
10820 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10821 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10822 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10823 }
10824 }
10825
10826 /* make new constraint visible, in case we add more */
10828
10829 return address;
10830}
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:1703
#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
Form_pg_class rd_rel
Definition: rel.h:111
void CommandCounterIncrement(void)
Definition: xact.c:1100

References addFkBothSides, addFkReferencedSide, Assert(), ChooseConstraintName(), CommandCounterIncrement(), Constraint::conname, CONSTRAINT_RELATION, ConstraintNameIsUsed(), CreateConstraintEntry(), Constraint::deferrable, DEPENDENCY_INTERNAL, DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, ereport, errcode(), errmsg(), ERROR, 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 10865 of file tablecmds.c.

10874{
10875 Oid deleteTriggerOid = InvalidOid,
10876 updateTriggerOid = InvalidOid;
10877
10880
10881 /*
10882 * Create action triggers to enforce the constraint, or skip them if the
10883 * constraint is NOT ENFORCED.
10884 */
10885 if (fkconstraint->is_enforced)
10887 RelationGetRelid(pkrel),
10888 fkconstraint,
10889 parentConstr, indexOid,
10890 parentDelTrigger, parentUpdTrigger,
10891 &deleteTriggerOid, &updateTriggerOid);
10892
10893 /*
10894 * If the referenced table is partitioned, recurse on ourselves to handle
10895 * each partition. We need one pg_constraint row created for each
10896 * partition in addition to the pg_constraint row for the parent table.
10897 */
10898 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10899 {
10900 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10901
10902 for (int i = 0; i < pd->nparts; i++)
10903 {
10904 Relation partRel;
10905 AttrMap *map;
10906 AttrNumber *mapped_pkattnum;
10907 Oid partIndexId;
10908 ObjectAddress address;
10909
10910 /* XXX would it be better to acquire these locks beforehand? */
10911 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10912
10913 /*
10914 * Map the attribute numbers in the referenced side of the FK
10915 * definition to match the partition's column layout.
10916 */
10918 RelationGetDescr(pkrel),
10919 false);
10920 if (map)
10921 {
10922 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10923 for (int j = 0; j < numfks; j++)
10924 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10925 }
10926 else
10927 mapped_pkattnum = pkattnum;
10928
10929 /* Determine the index to use at this level */
10930 partIndexId = index_get_partition(partRel, indexOid);
10931 if (!OidIsValid(partIndexId))
10932 elog(ERROR, "index for %u not found in partition %s",
10933 indexOid, RelationGetRelationName(partRel));
10934
10935 /* Create entry at this level ... */
10937 fkconstraint->conname, fkconstraint, rel,
10938 partRel, partIndexId, parentConstr,
10939 numfks, mapped_pkattnum,
10940 fkattnum, pfeqoperators, ppeqoperators,
10941 ffeqoperators, numfkdelsetcols,
10942 fkdelsetcols, true, with_period);
10943 /* ... and recurse to our children */
10944 addFkRecurseReferenced(fkconstraint, rel, partRel,
10945 partIndexId, address.objectId, numfks,
10946 mapped_pkattnum, fkattnum,
10947 pfeqoperators, ppeqoperators, ffeqoperators,
10948 numfkdelsetcols, fkdelsetcols,
10949 old_check_ok,
10950 deleteTriggerOid, updateTriggerOid,
10951 with_period);
10952
10953 /* Done -- clean up (but keep the lock) */
10954 table_close(partRel, NoLock);
10955 if (map)
10956 {
10957 pfree(mapped_pkattnum);
10958 free_attrmap(map);
10959 }
10960 }
10961 }
10962}
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:225
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:1528
void * palloc(Size size)
Definition: mcxt.c:1321
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:13795
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:10865

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 11003 of file tablecmds.c.

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

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 12771 of file tablecmds.c.

12775{
12776 Form_pg_constraint currcon;
12777 Oid conoid;
12778 ScanKeyData pkey;
12779 SysScanDesc pscan;
12780 HeapTuple childtup;
12781
12782 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12783 conoid = currcon->oid;
12784
12785 ScanKeyInit(&pkey,
12786 Anum_pg_constraint_conparentid,
12787 BTEqualStrategyNumber, F_OIDEQ,
12788 ObjectIdGetDatum(conoid));
12789
12790 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12791 true, NULL, 1, &pkey);
12792
12793 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12794 {
12795 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12796 Relation childrel;
12797
12798 childrel = table_open(childcon->conrelid, lockmode);
12799
12800 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12801 childtup, recurse, otherrelids, lockmode);
12802 table_close(childrel, NoLock);
12803 }
12804
12805 systable_endscan(pscan);
12806}
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:12517

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 12722 of file tablecmds.c.

12730{
12731 Form_pg_constraint currcon;
12732 Oid conoid;
12733 ScanKeyData pkey;
12734 SysScanDesc pscan;
12735 HeapTuple childtup;
12736
12737 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12738 conoid = currcon->oid;
12739
12740 ScanKeyInit(&pkey,
12741 Anum_pg_constraint_conparentid,
12742 BTEqualStrategyNumber, F_OIDEQ,
12743 ObjectIdGetDatum(conoid));
12744
12745 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12746 true, NULL, 1, &pkey);
12747
12748 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12749 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12750 pkrelid, childtup, lockmode,
12751 ReferencedParentDelTrigger,
12752 ReferencedParentUpdTrigger,
12753 ReferencingParentInsTrigger,
12754 ReferencingParentUpdTrigger);
12755
12756 systable_endscan(pscan);
12757}
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:12374

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 12653 of file tablecmds.c.

12656{
12657 HeapTuple tgtuple;
12658 ScanKeyData tgkey;
12659 SysScanDesc tgscan;
12660
12661 ScanKeyInit(&tgkey,
12662 Anum_pg_trigger_tgconstraint,
12663 BTEqualStrategyNumber, F_OIDEQ,
12664 ObjectIdGetDatum(conoid));
12665 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12666 NULL, 1, &tgkey);
12667 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12668 {
12669 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12670 Form_pg_trigger copy_tg;
12671 HeapTuple tgCopyTuple;
12672
12673 /*
12674 * Remember OIDs of other relation(s) involved in FK constraint.
12675 * (Note: it's likely that we could skip forcing a relcache inval for
12676 * other rels that don't have a trigger whose properties change, but
12677 * let's be conservative.)
12678 */
12679 if (tgform->tgrelid != RelationGetRelid(rel))
12680 *otherrelids = list_append_unique_oid(*otherrelids,
12681 tgform->tgrelid);
12682
12683 /*
12684 * Update enable status and deferrability of RI_FKey_noaction_del,
12685 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12686 * triggers, but not others; see createForeignKeyActionTriggers and
12687 * CreateFKCheckTrigger.
12688 */
12689 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12690 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12691 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12692 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12693 continue;
12694
12695 tgCopyTuple = heap_copytuple(tgtuple);
12696 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12697
12698 copy_tg->tgdeferrable = deferrable;
12699 copy_tg->tginitdeferred = initdeferred;
12700 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12701
12702 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12703
12704 heap_freetuple(tgCopyTuple);
12705 }
12706
12707 systable_endscan(tgscan);
12708}
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 12813 of file tablecmds.c.

12815{
12816 HeapTuple copyTuple;
12817 Form_pg_constraint copy_con;
12818
12819 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12820 cmdcon->alterInheritability);
12821
12822 copyTuple = heap_copytuple(contuple);
12823 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12824
12825 if (cmdcon->alterEnforceability)
12826 {
12827 copy_con->conenforced = cmdcon->is_enforced;
12828
12829 /*
12830 * NB: The convalidated status is irrelevant when the constraint is
12831 * set to NOT ENFORCED, but for consistency, it should still be set
12832 * appropriately. Similarly, if the constraint is later changed to
12833 * ENFORCED, validation will be performed during phase 3, so it makes
12834 * sense to mark it as valid in that case.
12835 */
12836 copy_con->convalidated = cmdcon->is_enforced;
12837 }
12838 if (cmdcon->alterDeferrability)
12839 {
12840 copy_con->condeferrable = cmdcon->deferrable;
12841 copy_con->condeferred = cmdcon->initdeferred;
12842 }
12843 if (cmdcon->alterInheritability)
12844 copy_con->connoinherit = cmdcon->noinherit;
12845
12846 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12847 InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12848
12849 /* Make new constraint flags visible to others */
12850 CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12851
12852 heap_freetuple(copyTuple);
12853}
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 19043 of file tablecmds.c.

19045{
19046 List *indexList;
19047 ListCell *l;
19048
19049 indexList = RelationGetIndexList(rel);
19050
19051 foreach(l, indexList)
19052 {
19053 Oid indexOid = lfirst_oid(l);
19054 ObjectAddress thisobj;
19055
19056 thisobj.classId = RelationRelationId;
19057 thisobj.objectId = indexOid;
19058 thisobj.objectSubId = 0;
19059
19060 /*
19061 * Note: currently, the index will not have its own dependency on the
19062 * namespace, so we don't need to do changeDependencyFor(). There's no
19063 * row type in pg_type, either.
19064 *
19065 * XXX this objsMoved test may be pointless -- surely we have a single
19066 * dependency link from a relation to each index?
19067 */
19068 if (!object_address_present(&thisobj, objsMoved))
19069 {
19070 AlterRelationNamespaceInternal(classRel, indexOid,
19071 oldNspOid, newNspOid,
19072 false, objsMoved);
19073 add_exact_object_address(&thisobj, objsMoved);
19074 }
19075 }
19076
19077 list_free(indexList);
19078}
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:4836
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
Definition: tablecmds.c:18966

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 18966 of file tablecmds.c.

18970{
18971 HeapTuple classTup;
18972 Form_pg_class classForm;
18973 ObjectAddress thisobj;
18974 bool already_done = false;
18975
18976 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
18977 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
18978 if (!HeapTupleIsValid(classTup))
18979 elog(ERROR, "cache lookup failed for relation %u", relOid);
18980 classForm = (Form_pg_class) GETSTRUCT(classTup);
18981
18982 Assert(classForm->relnamespace == oldNspOid);
18983
18984 thisobj.classId = RelationRelationId;
18985 thisobj.objectId = relOid;
18986 thisobj.objectSubId = 0;
18987
18988 /*
18989 * If the object has already been moved, don't move it again. If it's
18990 * already in the right place, don't move it, but still fire the object
18991 * access hook.
18992 */
18993 already_done = object_address_present(&thisobj, objsMoved);
18994 if (!already_done && oldNspOid != newNspOid)
18995 {
18996 ItemPointerData otid = classTup->t_self;
18997
18998 /* check for duplicate name (more friendly than unique-index failure) */
18999 if (get_relname_relid(NameStr(classForm->relname),
19000 newNspOid) != InvalidOid)
19001 ereport(ERROR,
19002 (errcode(ERRCODE_DUPLICATE_TABLE),
19003 errmsg("relation \"%s\" already exists in schema \"%s\"",
19004 NameStr(classForm->relname),
19005 get_namespace_name(newNspOid))));
19006
19007 /* classTup is a copy, so OK to scribble on */
19008 classForm->relnamespace = newNspOid;
19009
19010 CatalogTupleUpdate(classRel, &otid, classTup);
19011 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19012
19013
19014 /* Update dependency on schema if caller said so */
19015 if (hasDependEntry &&
19016 changeDependencyFor(RelationRelationId,
19017 relOid,
19018 NamespaceRelationId,
19019 oldNspOid,
19020 newNspOid) != 1)
19021 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19022 NameStr(classForm->relname));
19023 }
19024 else
19025 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19026 if (!already_done)
19027 {
19028 add_exact_object_address(&thisobj, objsMoved);
19029
19030 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19031 }
19032
19033 heap_freetuple(classTup);
19034}
#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 19088 of file tablecmds.c.

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

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

Referenced by ProcessUtilitySlow().

◆ AlterTableMoveAll()

Oid AlterTableMoveAll ( AlterTableMoveAllStmt stmt)

Definition at line 16897 of file tablecmds.c.

16898{
16899 List *relations = NIL;
16900 ListCell *l;
16901 ScanKeyData key[1];
16902 Relation rel;
16903 TableScanDesc scan;
16904 HeapTuple tuple;
16905 Oid orig_tablespaceoid;
16906 Oid new_tablespaceoid;
16907 List *role_oids = roleSpecsToIds(stmt->roles);
16908
16909 /* Ensure we were not asked to move something we can't */
16910 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16911 stmt->objtype != OBJECT_MATVIEW)
16912 ereport(ERROR,
16913 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16914 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16915
16916 /* Get the orig and new tablespace OIDs */
16917 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16918 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16919
16920 /* Can't move shared relations in to or out of pg_global */
16921 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16922 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16923 new_tablespaceoid == GLOBALTABLESPACE_OID)
16924 ereport(ERROR,
16925 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16926 errmsg("cannot move relations in to or out of pg_global tablespace")));
16927
16928 /*
16929 * Must have CREATE rights on the new tablespace, unless it is the
16930 * database default tablespace (which all users implicitly have CREATE
16931 * rights on).
16932 */
16933 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16934 {
16935 AclResult aclresult;
16936
16937 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16938 ACL_CREATE);
16939 if (aclresult != ACLCHECK_OK)
16941 get_tablespace_name(new_tablespaceoid));
16942 }
16943
16944 /*
16945 * Now that the checks are done, check if we should set either to
16946 * InvalidOid because it is our database's default tablespace.
16947 */
16948 if (orig_tablespaceoid == MyDatabaseTableSpace)
16949 orig_tablespaceoid = InvalidOid;
16950
16951 if (new_tablespaceoid == MyDatabaseTableSpace)
16952 new_tablespaceoid = InvalidOid;
16953
16954 /* no-op */
16955 if (orig_tablespaceoid == new_tablespaceoid)
16956 return new_tablespaceoid;
16957
16958 /*
16959 * Walk the list of objects in the tablespace and move them. This will
16960 * only find objects in our database, of course.
16961 */
16962 ScanKeyInit(&key[0],
16963 Anum_pg_class_reltablespace,
16964 BTEqualStrategyNumber, F_OIDEQ,
16965 ObjectIdGetDatum(orig_tablespaceoid));
16966
16967 rel = table_open(RelationRelationId, AccessShareLock);
16968 scan = table_beginscan_catalog(rel, 1, key);
16969 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
16970 {
16971 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
16972 Oid relOid = relForm->oid;
16973
16974 /*
16975 * Do not move objects in pg_catalog as part of this, if an admin
16976 * really wishes to do so, they can issue the individual ALTER
16977 * commands directly.
16978 *
16979 * Also, explicitly avoid any shared tables, temp tables, or TOAST
16980 * (TOAST will be moved with the main table).
16981 */
16982 if (IsCatalogNamespace(relForm->relnamespace) ||
16983 relForm->relisshared ||
16984 isAnyTempNamespace(relForm->relnamespace) ||
16985 IsToastNamespace(relForm->relnamespace))
16986 continue;
16987
16988 /* Only move the object type requested */
16989 if ((stmt->objtype == OBJECT_TABLE &&
16990 relForm->relkind != RELKIND_RELATION &&
16991 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
16992 (stmt->objtype == OBJECT_INDEX &&
16993 relForm->relkind != RELKIND_INDEX &&
16994 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
16995 (stmt->objtype == OBJECT_MATVIEW &&
16996 relForm->relkind != RELKIND_MATVIEW))
16997 continue;
16998
16999 /* Check if we are only moving objects owned by certain roles */
17000 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17001 continue;
17002
17003 /*
17004 * Handle permissions-checking here since we are locking the tables
17005 * and also to avoid doing a bunch of work only to fail part-way. Note
17006 * that permissions will also be checked by AlterTableInternal().
17007 *
17008 * Caller must be considered an owner on the table to move it.
17009 */
17010 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17012 NameStr(relForm->relname));
17013
17014 if (stmt->nowait &&
17016 ereport(ERROR,
17017 (errcode(ERRCODE_OBJECT_IN_USE),
17018 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17019 get_namespace_name(relForm->relnamespace),
17020 NameStr(relForm->relname))));
17021 else
17023
17024 /* Add to our list of objects to move */
17025 relations = lappend_oid(relations, relOid);
17026 }
17027
17028 table_endscan(scan);
17030
17031 if (relations == NIL)
17033 (errcode(ERRCODE_NO_DATA_FOUND),
17034 errmsg("no matching relations in tablespace \"%s\" found",
17035 orig_tablespaceoid == InvalidOid ? "(database default)" :
17036 get_tablespace_name(orig_tablespaceoid))));
17037
17038 /* Everything is locked, loop through and move all of the relations. */
17039 foreach(l, relations)
17040 {
17041 List *cmds = NIL;
17043
17045 cmd->name = stmt->new_tablespacename;
17046
17047 cmds = lappend(cmds, cmd);
17048
17050 /* OID is set by AlterTableInternal */
17051 AlterTableInternal(lfirst_oid(l), cmds, false);
17053 }
17054
17055 return new_tablespaceoid;
17056}
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:96
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 18858 of file tablecmds.c.

18859{
18860 Relation rel;
18861 Oid relid;
18862 Oid oldNspOid;
18863 Oid nspOid;
18864 RangeVar *newrv;
18865 ObjectAddresses *objsMoved;
18866 ObjectAddress myself;
18867
18869 stmt->missing_ok ? RVR_MISSING_OK : 0,
18871 stmt);
18872
18873 if (!OidIsValid(relid))
18874 {
18876 (errmsg("relation \"%s\" does not exist, skipping",
18877 stmt->relation->relname)));
18878 return InvalidObjectAddress;
18879 }
18880
18881 rel = relation_open(relid, NoLock);
18882
18883 oldNspOid = RelationGetNamespace(rel);
18884
18885 /* If it's an owned sequence, disallow moving it by itself. */
18886 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18887 {
18888 Oid tableId;
18889 int32 colId;
18890
18891 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18892 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18893 ereport(ERROR,
18894 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18895 errmsg("cannot move an owned sequence into another schema"),
18896 errdetail("Sequence \"%s\" is linked to table \"%s\".",
18898 get_rel_name(tableId))));
18899 }
18900
18901 /* Get and lock schema OID and check its permissions. */
18902 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18903 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18904
18905 /* common checks on switching namespaces */
18906 CheckSetNamespace(oldNspOid, nspOid);
18907
18908 objsMoved = new_object_addresses();
18909 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18910 free_object_addresses(objsMoved);
18911
18912 ObjectAddressSet(myself, RelationRelationId, relid);
18913
18914 if (oldschema)
18915 *oldschema = oldNspOid;
18916
18917 /* close rel, but keep lock until commit */
18918 relation_close(rel, NoLock);
18919
18920 return myself;
18921}
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:18929

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 18929 of file tablecmds.c.

18931{
18932 Relation classRel;
18933
18934 Assert(objsMoved != NULL);
18935
18936 /* OK, modify the pg_class row and pg_depend entry */
18937 classRel = table_open(RelationRelationId, RowExclusiveLock);
18938
18939 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18940 nspOid, true, objsMoved);
18941
18942 /* Fix the table's row type too, if it has one */
18943 if (OidIsValid(rel->rd_rel->reltype))
18944 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18945 false, /* isImplicitArray */
18946 false, /* ignoreDependent */
18947 false, /* errorOnTableType */
18948 objsMoved);
18949
18950 /* Fix other dependent stuff */
18951 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
18952 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
18953 objsMoved, AccessExclusiveLock);
18954 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
18955 false, objsMoved);
18956
18957 table_close(classRel, RowExclusiveLock);
18958}
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:19088
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19043
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:130
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
List * fk_attrs
Definition: parsenodes.h:2854
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:13574
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:13423
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
Definition: tablecmds.c:13603
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:13320
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
Definition: tablecmds.c:13265
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 14614 of file tablecmds.c.

14615{
14616 Assert(expr != NULL);
14617
14618 for (;;)
14619 {
14620 /* only one varno, so no need to check that */
14621 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14622 return false;
14623 else if (IsA(expr, RelabelType))
14624 expr = (Node *) ((RelabelType *) expr)->arg;
14625 else if (IsA(expr, CoerceToDomain))
14626 {
14627 CoerceToDomain *d = (CoerceToDomain *) expr;
14628
14630 return true;
14631 expr = (Node *) d->arg;
14632 }
14633 else if (IsA(expr, FuncExpr))
14634 {
14635 FuncExpr *f = (FuncExpr *) expr;
14636
14637 switch (f->funcid)
14638 {
14639 case F_TIMESTAMPTZ_TIMESTAMP:
14640 case F_TIMESTAMP_TIMESTAMPTZ:
14642 return true;
14643 else
14644 expr = linitial(f->args);
14645 break;
14646 default:
14647 return true;
14648 }
14649 }
14650 else
14651 return true;
14652 }
14653}
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 21885 of file tablecmds.c.

21886{
21887 List *constraints;
21888 ListCell *cell;
21889
21890 constraints = GetParentedForeignKeyRefs(partition);
21891
21892 foreach(cell, constraints)
21893 {
21894 Oid constrOid = lfirst_oid(cell);
21895 HeapTuple tuple;
21896 Form_pg_constraint constrForm;
21897 Relation rel;
21898 Trigger trig = {0};
21899
21900 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21901 if (!HeapTupleIsValid(tuple))
21902 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21903 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21904
21905 Assert(OidIsValid(constrForm->conparentid));
21906 Assert(constrForm->confrelid == RelationGetRelid(partition));
21907
21908 /* prevent data changes into the referencing table until commit */
21909 rel = table_open(constrForm->conrelid, ShareLock);
21910
21911 trig.tgoid = InvalidOid;
21912 trig.tgname = NameStr(constrForm->conname);
21914 trig.tgisinternal = true;
21915 trig.tgconstrrelid = RelationGetRelid(partition);
21916 trig.tgconstrindid = constrForm->conindid;
21917 trig.tgconstraint = constrForm->oid;
21918 trig.tgdeferrable = false;
21919 trig.tginitdeferred = false;
21920 /* we needn't fill in remaining fields */
21921
21922 RI_PartitionRemove_Check(&trig, rel, partition);
21923
21924 ReleaseSysCache(tuple);
21925
21926 table_close(rel, NoLock);
21927 }
21928}
#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:21832
#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 19371 of file tablecmds.c.

19373{
19374 ListCell *cur_item;
19375
19376 foreach(cur_item, on_commits)
19377 {
19378 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19379
19380 if (!isCommit && oc->creating_subid == mySubid)
19381 {
19382 /* cur_item must be removed */
19384 pfree(oc);
19385 }
19386 else
19387 {
19388 /* cur_item must be preserved */
19389 if (oc->creating_subid == mySubid)
19390 oc->creating_subid = parentSubid;
19391 if (oc->deleting_subid == mySubid)
19392 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19393 }
19394 }
19395}
#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 19339 of file tablecmds.c.

19340{
19341 ListCell *cur_item;
19342
19343 foreach(cur_item, on_commits)
19344 {
19345 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19346
19347 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19349 {
19350 /* cur_item must be removed */
19352 pfree(oc);
19353 }
19354 else
19355 {
19356 /* cur_item must be preserved */
19359 }
19360 }
19361}

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:192
EState * CreateExecutorState(void)
Definition: execUtils.c:88
#define GetPerTupleExprContext(estate)
Definition: executor.h:650
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:387
#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:6691
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 char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition: tablecmds.c:9835
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:208
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 17173 of file tablecmds.c.

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

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

15871{
15872 Relation ftrel;
15873 Relation attrel;
15874 ForeignServer *server;
15875 ForeignDataWrapper *fdw;
15876 HeapTuple tuple;
15877 HeapTuple newtuple;
15878 bool isnull;
15879 Datum repl_val[Natts_pg_attribute];
15880 bool repl_null[Natts_pg_attribute];
15881 bool repl_repl[Natts_pg_attribute];
15882 Datum datum;
15883 Form_pg_foreign_table fttableform;
15884 Form_pg_attribute atttableform;
15886 ObjectAddress address;
15887
15888 if (options == NIL)
15889 return InvalidObjectAddress;
15890
15891 /* First, determine FDW validator associated to the foreign table. */
15892 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15893 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15894 if (!HeapTupleIsValid(tuple))
15895 ereport(ERROR,
15896 (errcode(ERRCODE_UNDEFINED_OBJECT),
15897 errmsg("foreign table \"%s\" does not exist",
15899 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15900 server = GetForeignServer(fttableform->ftserver);
15901 fdw = GetForeignDataWrapper(server->fdwid);
15902
15904 ReleaseSysCache(tuple);
15905
15906 attrel = table_open(AttributeRelationId, RowExclusiveLock);
15907 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15908 if (!HeapTupleIsValid(tuple))
15909 ereport(ERROR,
15910 (errcode(ERRCODE_UNDEFINED_COLUMN),
15911 errmsg("column \"%s\" of relation \"%s\" does not exist",
15912 colName, RelationGetRelationName(rel))));
15913
15914 /* Prevent them from altering a system attribute */
15915 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15916 attnum = atttableform->attnum;
15917 if (attnum <= 0)
15918 ereport(ERROR,
15919 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15920 errmsg("cannot alter system column \"%s\"", colName)));
15921
15922
15923 /* Initialize buffers for new tuple values */
15924 memset(repl_val, 0, sizeof(repl_val));
15925 memset(repl_null, false, sizeof(repl_null));
15926 memset(repl_repl, false, sizeof(repl_repl));
15927
15928 /* Extract the current options */
15929 datum = SysCacheGetAttr(ATTNAME,
15930 tuple,
15931 Anum_pg_attribute_attfdwoptions,
15932 &isnull);
15933 if (isnull)
15934 datum = PointerGetDatum(NULL);
15935
15936 /* Transform the options */
15937 datum = transformGenericOptions(AttributeRelationId,
15938 datum,
15939 options,
15940 fdw->fdwvalidator);
15941
15942 if (PointerIsValid(DatumGetPointer(datum)))
15943 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
15944 else
15945 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
15946
15947 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
15948
15949 /* Everything looks good - update the tuple */
15950
15951 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
15952 repl_val, repl_null, repl_repl);
15953
15954 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
15955
15956 InvokeObjectPostAlterHook(RelationRelationId,
15957 RelationGetRelid(rel),
15958 atttableform->attnum);
15959 ObjectAddressSubSet(address, RelationRelationId,
15960 RelationGetRelid(rel), attnum);
15961
15962 ReleaseSysCache(tuple);
15963
15965
15966 heap_freetuple(newtuple);
15967
15968 return address;
15969}
#define PointerIsValid(pointer)
Definition: c.h:734
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition: foreign.c:38
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:112
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 14661 of file tablecmds.c.

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

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

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 12303 of file tablecmds.c.

12307{
12308 Form_pg_constraint currcon;
12309 bool changed = false;
12310 List *otherrelids = NIL;
12311
12312 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12313
12314 /*
12315 * Do the catalog work for the enforceability or deferrability change,
12316 * recurse if necessary.
12317 *
12318 * Note that even if deferrability is requested to be altered along with
12319 * enforceability, we don't need to explicitly update multiple entries in
12320 * pg_trigger related to deferrability.
12321 *
12322 * Modifying enforceability involves either creating or dropping the
12323 * trigger, during which the deferrability setting will be adjusted
12324 * automatically.
12325 */
12326 if (cmdcon->alterEnforceability &&
12327 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12328 currcon->conrelid, currcon->confrelid,
12329 contuple, lockmode, InvalidOid,
12331 changed = true;
12332
12333 else if (cmdcon->alterDeferrability &&
12334 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12335 contuple, recurse, &otherrelids,
12336 lockmode))
12337 {
12338 /*
12339 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12340 * the relations having the constraint itself; here we also invalidate
12341 * for relations that have any triggers that are part of the
12342 * constraint.
12343 */
12344 foreach_oid(relid, otherrelids)
12346
12347 changed = true;
12348 }
12349
12350 /*
12351 * Do the catalog work for the inheritability change.
12352 */
12353 if (cmdcon->alterInheritability &&
12354 ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12355 lockmode))
12356 changed = true;
12357
12358 return changed;
12359}
#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:12574

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 12517 of file tablecmds.c.

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

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 12374 of file tablecmds.c.

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

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 12574 of file tablecmds.c.

12577{
12578 Form_pg_constraint currcon;
12579 AttrNumber colNum;
12580 char *colName;
12581 List *children;
12582
12583 Assert(cmdcon->alterInheritability);
12584
12585 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12586
12587 /* The current implementation only works for NOT NULL constraints */
12588 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12589
12590 /*
12591 * If called to modify a constraint that's already in the desired state,
12592 * silently do nothing.
12593 */
12594 if (cmdcon->noinherit == currcon->connoinherit)
12595 return false;
12596
12597 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12599
12600 /* Fetch the column number and name */
12601 colNum = extractNotNullColumn(contuple);
12602 colName = get_attname(currcon->conrelid, colNum, false);
12603
12604 /*
12605 * Propagate the change to children. For this subcommand type we don't
12606 * recursively affect children, just the immediate level.
12607 */
12609 lockmode);
12610 foreach_oid(childoid, children)
12611 {
12612 ObjectAddress addr;
12613
12614 if (cmdcon->noinherit)
12615 {
12616 HeapTuple childtup;
12617 Form_pg_constraint childcon;
12618
12619 childtup = findNotNullConstraint(childoid, colName);
12620 if (!childtup)
12621 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12622 colName, childoid);
12623 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12624 Assert(childcon->coninhcount > 0);
12625 childcon->coninhcount--;
12626 childcon->conislocal = true;
12627 CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12628 heap_freetuple(childtup);
12629 }
12630 else
12631 {
12632 Relation childrel = table_open(childoid, NoLock);
12633
12634 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12635 colName, true, true, lockmode);
12636 if (OidIsValid(addr.objectId))
12638 table_close(childrel, NoLock);
12639 }
12640 }
12641
12642 return true;
12643}
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 20152 of file tablecmds.c.

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

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 21523 of file tablecmds.c.

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

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

16360{
16361 Oid indexOid;
16362 ObjectAddress address;
16363
16364 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16365
16366 if (!OidIsValid(indexOid))
16367 ereport(ERROR,
16368 (errcode(ERRCODE_UNDEFINED_OBJECT),
16369 errmsg("index \"%s\" for table \"%s\" does not exist",
16370 indexName, RelationGetRelationName(rel))));
16371
16372 /* Check index is valid to cluster on */
16373 check_index_is_clusterable(rel, indexOid, lockmode);
16374
16375 /* And do the work */
16376 mark_index_clustered(rel, indexOid, false);
16377
16378 ObjectAddressSet(address,
16379 RelationRelationId, indexOid);
16380
16381 return address;
16382}
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:15867
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17173
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:18270
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12160
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:12867
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:20814
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:18546
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:17116
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:18516
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:16557
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:18575
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:14661
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:16391
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:18128
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17737
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:18402
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:21319
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:20152
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
Definition: tablecmds.c:16437
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:13950
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:18656
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:16858
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:21523
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:16359
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:17134
#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 20814 of file tablecmds.c.

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

21320{
21321 Relation partRel;
21322 ObjectAddress address;
21323 Snapshot snap = GetActiveSnapshot();
21324
21326
21327 /*
21328 * Wait until existing snapshots are gone. This is important if the
21329 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21330 * user could immediately run DETACH FINALIZE without actually waiting for
21331 * existing transactions. We must not complete the detach action until
21332 * all such queries are complete (otherwise we would present them with an
21333 * inconsistent view of catalogs).
21334 */
21335 WaitForOlderSnapshots(snap->xmin, false);
21336
21337 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21338
21339 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21340
21341 table_close(partRel, NoLock);
21342
21343 return address;
21344}
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 16391 of file tablecmds.c.

16392{
16393 mark_index_clustered(rel, InvalidOid, false);
16394}

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 13950 of file tablecmds.c.

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

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 17737 of file tablecmds.c.

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

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 18270 of file tablecmds.c.

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

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 17134 of file tablecmds.c.

17136{
17137 EnableDisableRule(rel, rulename, fires_when);
17138
17139 InvokeObjectPostAlterHook(RelationRelationId,
17140 RelationGetRelid(rel), 0);
17141}
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 17116 of file tablecmds.c.

17119{
17120 EnableDisableTrigger(rel, trigname, InvalidOid,
17121 fires_when, skip_system, recurse,
17122 lockmode);
17123
17124 InvokeObjectPostAlterHook(RelationRelationId,
17125 RelationGetRelid(rel), 0);
17126}
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 18546 of file tablecmds.c.

18547{
18548 Relation pg_class;
18549 Oid relid;
18550 HeapTuple tuple;
18551
18552 relid = RelationGetRelid(rel);
18553
18554 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18555
18556 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18557
18558 if (!HeapTupleIsValid(tuple))
18559 elog(ERROR, "cache lookup failed for relation %u", relid);
18560
18561 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18562 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18563
18564 InvokeObjectPostAlterHook(RelationRelationId,
18565 RelationGetRelid(rel), 0);
18566
18567 table_close(pg_class, RowExclusiveLock);
18568 heap_freetuple(tuple);
18569}

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 18575 of file tablecmds.c.

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

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 18402 of file tablecmds.c.

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

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 16437 of file tablecmds.c.

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

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 18656 of file tablecmds.c.

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

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

18517{
18518 Relation pg_class;
18519 Oid relid;
18520 HeapTuple tuple;
18521
18522 relid = RelationGetRelid(rel);
18523
18524 /* Pull the record for this relation and update it */
18525 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18526
18527 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18528
18529 if (!HeapTupleIsValid(tuple))
18530 elog(ERROR, "cache lookup failed for relation %u", relid);
18531
18532 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18533 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18534
18535 InvokeObjectPostAlterHook(RelationRelationId,
18536 RelationGetRelid(rel), 0);
18537
18538 table_close(pg_class, RowExclusiveLock);
18539 heap_freetuple(tuple);
18540}

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:21972

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 16765 of file tablecmds.c.

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

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 16858 of file tablecmds.c.

16859{
16860 /*
16861 * Shouldn't be called on relations having storage; these are processed in
16862 * phase 3.
16863 */
16864 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16865
16866 /* check if relation can be moved to its new tablespace */
16867 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16868 {
16869 InvokeObjectPostAlterHook(RelationRelationId,
16870 RelationGetRelid(rel),
16871 0);
16872 return;
16873 }
16874
16875 /* Update can be done, so change reltablespace */
16876 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16877
16878 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16879
16880 /* Make sure the reltablespace change is visible */
16882}

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 12867 of file tablecmds.c.

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

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 15371 of file tablecmds.c.

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

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 15540 of file tablecmds.c.

15542{
15543 List *raw_parsetree_list;
15544 List *querytree_list;
15545 ListCell *list_item;
15546 Relation rel;
15547
15548 /*
15549 * We expect that we will get only ALTER TABLE and CREATE INDEX
15550 * statements. Hence, there is no need to pass them through
15551 * parse_analyze_*() or the rewriter, but instead we need to pass them
15552 * through parse_utilcmd.c to make them ready for execution.
15553 */
15554 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15555 querytree_list = NIL;
15556 foreach(list_item, raw_parsetree_list)
15557 {
15558 RawStmt *rs = lfirst_node(RawStmt, list_item);
15559 Node *stmt = rs->stmt;
15560
15561 if (IsA(stmt, IndexStmt))
15562 querytree_list = lappend(querytree_list,
15563 transformIndexStmt(oldRelId,
15564 (IndexStmt *) stmt,
15565 cmd));
15566 else if (IsA(stmt, AlterTableStmt))
15567 {
15568 List *beforeStmts;
15569 List *afterStmts;
15570
15571 stmt = (Node *) transformAlterTableStmt(oldRelId,
15572 (AlterTableStmt *) stmt,
15573 cmd,
15574 &beforeStmts,
15575 &afterStmts);
15576 querytree_list = list_concat(querytree_list, beforeStmts);
15577 querytree_list = lappend(querytree_list, stmt);
15578 querytree_list = list_concat(querytree_list, afterStmts);
15579 }
15580 else if (IsA(stmt, CreateStatsStmt))
15581 querytree_list = lappend(querytree_list,
15582 transformStatsStmt(oldRelId,
15584 cmd));
15585 else
15586 querytree_list = lappend(querytree_list, stmt);
15587 }
15588
15589 /* Caller should already have acquired whatever lock we need. */
15590 rel = relation_open(oldRelId, NoLock);
15591
15592 /*
15593 * Attach each generated command to the proper place in the work queue.
15594 * Note this could result in creation of entirely new work-queue entries.
15595 *
15596 * Also note that we have to tweak the command subtypes, because it turns
15597 * out that re-creation of indexes and constraints has to act a bit
15598 * differently from initial creation.
15599 */
15600 foreach(list_item, querytree_list)
15601 {
15602 Node *stm = (Node *) lfirst(list_item);
15603 AlteredTableInfo *tab;
15604
15605 tab = ATGetQueueEntry(wqueue, rel);
15606
15607 if (IsA(stm, IndexStmt))
15608 {
15609 IndexStmt *stmt = (IndexStmt *) stm;
15610 AlterTableCmd *newcmd;
15611
15612 if (!rewrite)
15613 TryReuseIndex(oldId, stmt);
15614 stmt->reset_default_tblspc = true;
15615 /* keep the index's comment */
15616 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15617
15618 newcmd = makeNode(AlterTableCmd);
15619 newcmd->subtype = AT_ReAddIndex;
15620 newcmd->def = (Node *) stmt;
15622 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15623 }
15624 else if (IsA(stm, AlterTableStmt))
15625 {
15627 ListCell *lcmd;
15628
15629 foreach(lcmd, stmt->cmds)
15630 {
15632
15633 if (cmd->subtype == AT_AddIndex)
15634 {
15635 IndexStmt *indstmt;
15636 Oid indoid;
15637
15638 indstmt = castNode(IndexStmt, cmd->def);
15639 indoid = get_constraint_index(oldId);
15640
15641 if (!rewrite)
15642 TryReuseIndex(indoid, indstmt);
15643 /* keep any comment on the index */
15644 indstmt->idxcomment = GetComment(indoid,
15645 RelationRelationId, 0);
15646 indstmt->reset_default_tblspc = true;
15647
15648 cmd->subtype = AT_ReAddIndex;
15650 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15651
15652 /* recreate any comment on the constraint */
15655 oldId,
15656 rel,
15657 NIL,
15658 indstmt->idxname);
15659 }
15660 else if (cmd->subtype == AT_AddConstraint)
15661 {
15662 Constraint *con = castNode(Constraint, cmd->def);
15663
15664 con->old_pktable_oid = refRelId;
15665 /* rewriting neither side of a FK */
15666 if (con->contype == CONSTR_FOREIGN &&
15667 !rewrite && tab->rewrite == 0)
15668 TryReuseForeignKey(oldId, con);
15669 con->reset_default_tblspc = true;
15673
15674 /*
15675 * Recreate any comment on the constraint. If we have
15676 * recreated a primary key, then transformTableConstraint
15677 * has added an unnamed not-null constraint here; skip
15678 * this in that case.
15679 */
15680 if (con->conname)
15683 oldId,
15684 rel,
15685 NIL,
15686 con->conname);
15687 else
15688 Assert(con->contype == CONSTR_NOTNULL);
15689 }
15690 else
15691 elog(ERROR, "unexpected statement subtype: %d",
15692 (int) cmd->subtype);
15693 }
15694 }
15695 else if (IsA(stm, AlterDomainStmt))
15696 {
15698
15699 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
15700 {
15701 Constraint *con = castNode(Constraint, stmt->def);
15703
15705 cmd->def = (Node *) stmt;
15708
15709 /* recreate any comment on the constraint */
15712 oldId,
15713 NULL,
15714 stmt->typeName,
15715 con->conname);
15716 }
15717 else
15718 elog(ERROR, "unexpected statement subtype: %d",
15719 (int) stmt->subtype);
15720 }
15721 else if (IsA(stm, CreateStatsStmt))
15722 {
15724 AlterTableCmd *newcmd;
15725
15726 /* keep the statistics object's comment */
15727 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15728
15729 newcmd = makeNode(AlterTableCmd);
15730 newcmd->subtype = AT_ReAddStatistics;
15731 newcmd->def = (Node *) stmt;
15732 tab->subcmds[AT_PASS_MISC] =
15733 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15734 }
15735 else
15736 elog(ERROR, "unexpected statement type: %d",
15737 (int) nodeTag(stm));
15738 }
15739
15740 relation_close(rel, NoLock);
15741}
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:15798
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:15827
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:15754

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 17151 of file tablecmds.c.

17152{
17153 if (child_rel->rd_rel->reloftype)
17154 ereport(ERROR,
17155 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17156 errmsg("cannot change inheritance of typed table")));
17157
17158 if (child_rel->rd_rel->relispartition)
17159 ereport(ERROR,
17160 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17161 errmsg("cannot change inheritance of a partition")));
17162
17163 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17164 ereport(ERROR,
17165 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17166 errmsg("cannot change inheritance of partitioned table")));
17167}

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 14311 of file tablecmds.c.

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

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

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:16527
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:18733
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:17151
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:14311
#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:16403

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 16403 of file tablecmds.c.

16404{
16405 Oid amoid;
16406
16407 /*
16408 * Look up the access method name and check that it differs from the
16409 * table's current AM. If DEFAULT was specified for a partitioned table
16410 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16411 */
16412 if (amname != NULL)
16413 amoid = get_table_am_oid(amname, false);
16414 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16415 amoid = InvalidOid;
16416 else
16418
16419 /* if it's a match, phase 3 doesn't need to do anything */
16420 if (rel->rd_rel->relam == amoid)
16421 return;
16422
16423 /* Save info for Phase 3 to do the real work */
16425 tab->newAccessMethod = amoid;
16426 tab->chgAccessMethod = true;
16427}
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 16527 of file tablecmds.c.

16528{
16529 Oid tablespaceId;
16530
16531 /* Check that the tablespace exists */
16532 tablespaceId = get_tablespace_oid(tablespacename, false);
16533
16534 /* Check permissions except when moving to database's default */
16535 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16536 {
16537 AclResult aclresult;
16538
16539 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16540 if (aclresult != ACLCHECK_OK)
16541 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16542 }
16543
16544 /* Save info for Phase 3 to do the real work */
16545 if (OidIsValid(tab->newTableSpace))
16546 ereport(ERROR,
16547 (errcode(ERRCODE_SYNTAX_ERROR),
16548 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16549
16550 tab->newTableSpace = tablespaceId;
16551}

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:15371
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:1225
AttrNumber ExecRelGenVirtualNotNull(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, List *notnull_virtual_attrs)
Definition: execMain.c:2066
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:644
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:655
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:122
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:6066
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:716
MemoryContext es_query_cxt
Definition: execnodes.h:706
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:267
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:13632
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 20475 of file tablecmds.c.

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

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

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

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 16290 of file tablecmds.c.

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

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 13603 of file tablecmds.c.

13604{
13605 Oid roleid = GetUserId();
13606 AclResult aclresult;
13607 int i;
13608
13609 /* Okay if we have relation-level REFERENCES permission */
13610 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13612 if (aclresult == ACLCHECK_OK)
13613 return;
13614 /* Else we must have REFERENCES on each column */
13615 for (i = 0; i < natts; i++)
13616 {
13617 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13618 roleid, ACL_REFERENCES);
13619 if (aclresult != ACLCHECK_OK)
13620 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13622 }
13623}
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:6009

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 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 /* We need the same lock level that CreateTrigger will acquire */
11298 fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11299
11300 indexOid = constrForm->conindid;
11302 &numfks,
11303 conkey,
11304 confkey,
11305 conpfeqop,
11306 conppeqop,
11307 conffeqop,
11308 &numfkdelsetcols,
11309 confdelsetcols);
11310
11311 for (int i = 0; i < numfks; i++)
11312 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11313
11314 fkconstraint = makeNode(Constraint);
11315 fkconstraint->contype = CONSTRAINT_FOREIGN;
11316 fkconstraint->conname = NameStr(constrForm->conname);
11317 fkconstraint->deferrable = constrForm->condeferrable;
11318 fkconstraint->initdeferred = constrForm->condeferred;
11319 fkconstraint->location = -1;
11320 fkconstraint->pktable = NULL;
11321 /* ->fk_attrs determined below */
11322 fkconstraint->pk_attrs = NIL;
11323 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11324 fkconstraint->fk_upd_action = constrForm->confupdtype;
11325 fkconstraint->fk_del_action = constrForm->confdeltype;
11326 fkconstraint->fk_del_set_cols = NIL;
11327 fkconstraint->old_conpfeqop = NIL;
11328 fkconstraint->old_pktable_oid = InvalidOid;
11329 fkconstraint->is_enforced = constrForm->conenforced;
11330 fkconstraint->skip_validation = false;
11331 fkconstraint->initially_valid = constrForm->convalidated;
11332
11333 /* set up colnames that are used to generate the constraint name */
11334 for (int i = 0; i < numfks; i++)
11335 {
11337
11338 att = TupleDescAttr(RelationGetDescr(fkRel),
11339 conkey[i] - 1);
11340 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11341 makeString(NameStr(att->attname)));
11342 }
11343
11344 /*
11345 * Add the new foreign key constraint pointing to the new partition.
11346 * Because this new partition appears in the referenced side of the
11347 * constraint, we don't need to set up for Phase 3 check.
11348 */
11349 partIndexId = index_get_partition(partitionRel, indexOid);
11350 if (!OidIsValid(partIndexId))
11351 elog(ERROR, "index for %u not found in partition %s",
11352 indexOid, RelationGetRelationName(partitionRel));
11353
11354 /*
11355 * Get the "action" triggers belonging to the constraint to pass as
11356 * parent OIDs for similar triggers that will be created on the
11357 * partition in addFkRecurseReferenced().
11358 */
11359 if (constrForm->conenforced)
11360 GetForeignKeyActionTriggers(trigrel, constrOid,
11361 constrForm->confrelid, constrForm->conrelid,
11362 &deleteTriggerOid, &updateTriggerOid);
11363
11364 /* Add this constraint ... */
11366 fkconstraint->conname, fkconstraint, fkRel,
11367 partitionRel, partIndexId, constrOid,
11368 numfks, mapped_confkey,
11369 conkey, conpfeqop, conppeqop, conffeqop,
11370 numfkdelsetcols, confdelsetcols, false,
11371 constrForm->conperiod);
11372 /* ... and recurse */
11373 addFkRecurseReferenced(fkconstraint,
11374 fkRel,
11375 partitionRel,
11376 partIndexId,
11377 address.objectId,
11378 numfks,
11379 mapped_confkey,
11380 conkey,
11381 conpfeqop,
11382 conppeqop,
11383 conffeqop,
11384 numfkdelsetcols,
11385 confdelsetcols,
11386 true,
11387 deleteTriggerOid,
11388 updateTriggerOid,
11389 constrForm->conperiod);
11390
11391 table_close(fkRel, NoLock);
11392 ReleaseSysCache(tuple);
11393 }
11394
11395 table_close(trigrel, RowExclusiveLock);
11396}
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:12033

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 11412 of file tablecmds.c.

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

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 11182 of file tablecmds.c.

11184{
11185 /* This only works for declarative partitioning */
11186 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11187
11188 /*
11189 * First, clone constraints where the parent is on the referencing side.
11190 */
11191 CloneFkReferencing(wqueue, parentRel, partitionRel);
11192
11193 /*
11194 * Clone constraints for which the parent is on the referenced side.
11195 */
11196 CloneFkReferenced(parentRel, partitionRel);
11197}
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11412
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 20657 of file tablecmds.c.

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

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

20017{
20018 List *existConstraint = list_copy(provenConstraint);
20019 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20020 int num_check,
20021 i;
20022
20023 num_check = (constr != NULL) ? constr->num_check : 0;
20024 for (i = 0; i < num_check; i++)
20025 {
20026 Node *cexpr;
20027
20028 /*
20029 * If this constraint hasn't been fully validated yet, we must ignore
20030 * it here.
20031 */
20032 if (!constr->check[i].ccvalid)
20033 continue;
20034
20035 /*
20036 * NOT ENFORCED constraints are always marked as invalid, which should
20037 * have been ignored.
20038 */
20039 Assert(constr->check[i].ccenforced);
20040
20041 cexpr = stringToNode(constr->check[i].ccbin);
20042
20043 /*
20044 * Run each expression through const-simplification and
20045 * canonicalization. It is necessary, because we will be comparing it
20046 * to similarly-processed partition constraint expressions, and may
20047 * fail to detect valid matches without this.
20048 */
20049 cexpr = eval_const_expressions(NULL, cexpr);
20050 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20051
20052 existConstraint = list_concat(existConstraint,
20053 make_ands_implicit((Expr *) cexpr));
20054 }
20055
20056 /*
20057 * Try to make the proof. Since we are comparing CHECK constraints, we
20058 * need to use weak implication, i.e., we assume existConstraint is
20059 * not-false and try to prove the same for testConstraint.
20060 *
20061 * Note that predicate_implied_by assumes its first argument is known
20062 * immutable. That should always be true for both NOT NULL and partition
20063 * constraints, so we don't test it here.
20064 */
20065 return predicate_implied_by(testConstraint, existConstraint, true);
20066}
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 17383 of file tablecmds.c.

17384{
17387
17388 if (acon->condeferrable != bcon->condeferrable ||
17389 acon->condeferred != bcon->condeferred ||
17390 strcmp(decompile_conbin(a, tupleDesc),
17391 decompile_conbin(b, tupleDesc)) != 0)
17392 return false;
17393 else
17394 return true;
17395}
int b
Definition: isn.c:74
int a
Definition: isn.c:73
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:17355

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 13732 of file tablecmds.c.

13735{
13736 ObjectAddress trigAddress;
13737 CreateTrigStmt *fk_trigger;
13738
13739 /*
13740 * Note: for a self-referential FK (referencing and referenced tables are
13741 * the same), it is important that the ON UPDATE action fires before the
13742 * CHECK action, since both triggers will fire on the same row during an
13743 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13744 * state of the row. Triggers fire in name order, so we ensure this by
13745 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13746 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13747 */
13748 fk_trigger = makeNode(CreateTrigStmt);
13749 fk_trigger->replace = false;
13750 fk_trigger->isconstraint = true;
13751 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13752 fk_trigger->relation = NULL;
13753
13754 /* Either ON INSERT or ON UPDATE */
13755 if (on_insert)
13756 {
13757 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13758 fk_trigger->events = TRIGGER_TYPE_INSERT;
13759 }
13760 else
13761 {
13762 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13763 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13764 }
13765
13766 fk_trigger->args = NIL;
13767 fk_trigger->row = true;
13768 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13769 fk_trigger->columns = NIL;
13770 fk_trigger->whenClause = NULL;
13771 fk_trigger->transitionRels = NIL;
13772 fk_trigger->deferrable = fkconstraint->deferrable;
13773 fk_trigger->initdeferred = fkconstraint->initdeferred;
13774 fk_trigger->constrrel = NULL;
13775
13776 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13777 constraintOid, indexOid, InvalidOid,
13778 parentTrigOid, NULL, true, false);
13779
13780 /* Make changes-so-far visible */
13782
13783 return trigAddress.objectId;
13784}
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 13795 of file tablecmds.c.

13799{
13800 CreateTrigStmt *fk_trigger;
13801 ObjectAddress trigAddress;
13802
13803 /*
13804 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13805 * DELETE action on the referenced table.
13806 */
13807 fk_trigger = makeNode(CreateTrigStmt);
13808 fk_trigger->replace = false;
13809 fk_trigger->isconstraint = true;
13810 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13811 fk_trigger->relation = NULL;
13812 fk_trigger->args = NIL;
13813 fk_trigger->row = true;
13814 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13815 fk_trigger->events = TRIGGER_TYPE_DELETE;
13816 fk_trigger->columns = NIL;
13817 fk_trigger->whenClause = NULL;
13818 fk_trigger->transitionRels = NIL;
13819 fk_trigger->constrrel = NULL;
13820
13821 switch (fkconstraint->fk_del_action)
13822 {
13824 fk_trigger->deferrable = fkconstraint->deferrable;
13825 fk_trigger->initdeferred = fkconstraint->initdeferred;
13826 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13827 break;
13829 fk_trigger->deferrable = false;
13830 fk_trigger->initdeferred = false;
13831 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13832 break;
13834 fk_trigger->deferrable = false;
13835 fk_trigger->initdeferred = false;
13836 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13837 break;
13839 fk_trigger->deferrable = false;
13840 fk_trigger->initdeferred = false;
13841 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13842 break;
13844 fk_trigger->deferrable = false;
13845 fk_trigger->initdeferred = false;
13846 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13847 break;
13848 default:
13849 elog(ERROR, "unrecognized FK action type: %d",
13850 (int) fkconstraint->fk_del_action);
13851 break;
13852 }
13853
13854 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13855 constraintOid, indexOid, InvalidOid,
13856 parentDelTrigger, NULL, true, false);
13857 if (deleteTrigOid)
13858 *deleteTrigOid = trigAddress.objectId;
13859
13860 /* Make changes-so-far visible */
13862
13863 /*
13864 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13865 * UPDATE action on the referenced table.
13866 */
13867 fk_trigger = makeNode(CreateTrigStmt);
13868 fk_trigger->replace = false;
13869 fk_trigger->isconstraint = true;
13870 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13871 fk_trigger->relation = NULL;
13872 fk_trigger->args = NIL;
13873 fk_trigger->row = true;
13874 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13875 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13876 fk_trigger->columns = NIL;
13877 fk_trigger->whenClause = NULL;
13878 fk_trigger->transitionRels = NIL;
13879 fk_trigger->constrrel = NULL;
13880
13881 switch (fkconstraint->fk_upd_action)
13882 {
13884 fk_trigger->deferrable = fkconstraint->deferrable;
13885 fk_trigger->initdeferred = fkconstraint->initdeferred;
13886 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13887 break;
13889 fk_trigger->deferrable = false;
13890 fk_trigger->initdeferred = false;
13891 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13892 break;
13894 fk_trigger->deferrable = false;
13895 fk_trigger->initdeferred = false;
13896 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13897 break;
13899 fk_trigger->deferrable = false;
13900 fk_trigger->initdeferred = false;
13901 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13902 break;
13904 fk_trigger->deferrable = false;
13905 fk_trigger->initdeferred = false;
13906 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13907 break;
13908 default:
13909 elog(ERROR, "unrecognized FK action type: %d",
13910 (int) fkconstraint->fk_upd_action);
13911 break;
13912 }
13913
13914 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13915 constraintOid, indexOid, InvalidOid,
13916 parentUpdTrigger, NULL, true, false);
13917 if (updateTrigOid)
13918 *updateTrigOid = trigAddress.objectId;
13919}
#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 13930 of file tablecmds.c.

13935{
13936 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13937 constraintOid, indexOid,
13938 parentInsTrigger, true);
13939 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13940 constraintOid, indexOid,
13941 parentUpdTrigger, false);
13942}
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:13732

References CreateFKCheckTrigger().

Referenced by addFkRecurseReferencing(), and ATExecAlterConstrEnforceability().

◆ CreateInheritance()

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

Definition at line 17286 of file tablecmds.c.

17287{
17288 Relation catalogRelation;
17289 SysScanDesc scan;
17291 HeapTuple inheritsTuple;
17292 int32 inhseqno;
17293
17294 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17295 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17296
17297 /*
17298 * Check for duplicates in the list of parents, and determine the highest
17299 * inhseqno already present; we'll use the next one for the new parent.
17300 * Also, if proposed child is a partition, it cannot already be
17301 * inheriting.
17302 *
17303 * Note: we do not reject the case where the child already inherits from
17304 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17305 */
17307 Anum_pg_inherits_inhrelid,
17308 BTEqualStrategyNumber, F_OIDEQ,
17310 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17311 true, NULL, 1, &key);
17312
17313 /* inhseqno sequences start at 1 */
17314 inhseqno = 0;
17315 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17316 {
17317 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17318
17319 if (inh->inhparent == RelationGetRelid(parent_rel))
17320 ereport(ERROR,
17321 (errcode(ERRCODE_DUPLICATE_TABLE),
17322 errmsg("relation \"%s\" would be inherited from more than once",
17323 RelationGetRelationName(parent_rel))));
17324
17325 if (inh->inhseqno > inhseqno)
17326 inhseqno = inh->inhseqno;
17327 }
17328 systable_endscan(scan);
17329
17330 /* Match up the columns and bump attinhcount as needed */
17331 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17332
17333 /* Match up the constraints and bump coninhcount as needed */
17334 MergeConstraintsIntoExisting(child_rel, parent_rel);
17335
17336 /*
17337 * OK, it looks valid. Make the catalog entries that show inheritance.
17338 */
17340 RelationGetRelid(parent_rel),
17341 inhseqno + 1,
17342 catalogRelation,
17343 parent_rel->rd_rel->relkind ==
17344 RELKIND_PARTITIONED_TABLE);
17345
17346 /* Now we're done with pg_inherits */
17347 table_close(catalogRelation, RowExclusiveLock);
17348}
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:17412
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:17550

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 17355 of file tablecmds.c.

17356{
17358 bool isnull;
17359 Datum attr;
17360 Datum expr;
17361
17362 con = (Form_pg_constraint) GETSTRUCT(contup);
17363 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17364 if (isnull)
17365 elog(ERROR, "null conbin for constraint %u", con->oid);
17366
17367 expr = DirectFunctionCall2(pg_get_expr, attr,
17368 ObjectIdGetDatum(con->conrelid));
17369 return TextDatumGetCString(expr);
17370}
#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:19697
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:19639
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 21354 of file tablecmds.c.

21355{
21356 List *constraintExpr;
21357
21358 constraintExpr = RelationGetPartitionQual(partRel);
21359 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
21360
21361 /*
21362 * Avoid adding a new constraint if the needed constraint is implied by an
21363 * existing constraint
21364 */
21365 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
21366 {
21367 AlteredTableInfo *tab;
21368 Constraint *n;
21369
21370 tab = ATGetQueueEntry(wqueue, partRel);
21371
21372 /* Add constraint on partition, equivalent to the partition constraint */
21373 n = makeNode(Constraint);
21374 n->contype = CONSTR_CHECK;
21375 n->conname = NULL;
21376 n->location = -1;
21377 n->is_no_inherit = false;
21378 n->raw_expr = NULL;
21379 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
21380 n->is_enforced = true;
21381 n->initially_valid = true;
21382 n->skip_validation = true;
21383 /* It's a re-add, since it nominally already exists */
21384 ATAddCheckNNConstraint(wqueue, tab, partRel, n,
21385 true, false, true, ShareUpdateExclusiveLock);
21386 }
21387}
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:19961

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 20985 of file tablecmds.c.

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

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 18076 of file tablecmds.c.

18078{
18079 Relation catalogRelation;
18080 SysScanDesc scan;
18081 ScanKeyData key[3];
18082 HeapTuple depTuple;
18083
18084 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18085
18086 ScanKeyInit(&key[0],
18087 Anum_pg_depend_classid,
18088 BTEqualStrategyNumber, F_OIDEQ,
18089 ObjectIdGetDatum(RelationRelationId));
18090 ScanKeyInit(&key[1],
18091 Anum_pg_depend_objid,
18092 BTEqualStrategyNumber, F_OIDEQ,
18093 ObjectIdGetDatum(relid));
18094 ScanKeyInit(&key[2],
18095 Anum_pg_depend_objsubid,
18096 BTEqualStrategyNumber, F_INT4EQ,
18097 Int32GetDatum(0));
18098
18099 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18100 NULL, 3, key);
18101
18102 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18103 {
18104 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18105
18106 if (dep->refclassid == refclassid &&
18107 dep->refobjid == refobjid &&
18108 dep->refobjsubid == 0 &&
18109 dep->deptype == deptype)
18110 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18111 }
18112
18113 systable_endscan(scan);
18114 table_close(catalogRelation, RowExclusiveLock);
18115}

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 21396 of file tablecmds.c.

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

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 14015 of file tablecmds.c.

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

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

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:19442
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:956
void hash_destroy(HTAB *hashp)
Definition: dynahash.c:866
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1421
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:1386
#define PG_TRY(...)
Definition: elog.h:371
#define PG_END_TRY(...)
Definition: elog.h:396
#define PG_FINALLY(...)
Definition: elog.h:388
struct ResultRelInfo ResultRelInfo
FdwRoutine * GetFdwRoutineByServerId(Oid serverid)
Definition: foreign.c:378
Oid GetForeignServerIdByRelId(Oid relid)
Definition: foreign.c:356
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:684
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:474
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:5073
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 13574 of file tablecmds.c.

13575{
13576 CoercionPathType ret;
13577
13578 if (targetTypeId == sourceTypeId)
13579 {
13581 *funcid = InvalidOid;
13582 }
13583 else
13584 {
13585 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13586 COERCION_IMPLICIT, funcid);
13587 if (ret == COERCION_PATH_NONE)
13588 /* A previously-relied-upon cast is now gone. */
13589 elog(ERROR, "could not find cast from %u to %u",
13590 sourceTypeId, targetTypeId);
13591 }
13592
13593 return ret;
13594}
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 21934 of file tablecmds.c.

21935{
21936 char cmethod;
21937
21938 if (compression == NULL || strcmp(compression, "default") == 0)
21940
21941 /*
21942 * To specify a nondefault method, the column data type must be toastable.
21943 * Note this says nothing about whether the column's attstorage setting
21944 * permits compression; we intentionally allow attstorage and
21945 * attcompression to be independent. But with a non-toastable type,
21946 * attstorage could not be set to a value that would permit compression.
21947 *
21948 * We don't actually need to enforce this, since nothing bad would happen
21949 * if attcompression were non-default; it would never be consulted. But
21950 * it seems more user-friendly to complain about a certainly-useless
21951 * attempt to set the property.
21952 */
21953 if (!TypeIsToastable(atttypid))
21954 ereport(ERROR,
21955 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21956 errmsg("column data type %s does not support compression",
21957 format_type_be(atttypid))));
21958
21959 cmethod = CompressionNameToMethod(compression);
21960 if (!CompressionMethodIsValid(cmethod))
21961 ereport(ERROR,
21962 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21963 errmsg("invalid compression method \"%s\"", compression)));
21964
21965 return cmethod;
21966}
#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 21972 of file tablecmds.c.

21973{
21974 char cstorage = 0;
21975
21976 if (pg_strcasecmp(storagemode, "plain") == 0)
21977 cstorage = TYPSTORAGE_PLAIN;
21978 else if (pg_strcasecmp(storagemode, "external") == 0)
21979 cstorage = TYPSTORAGE_EXTERNAL;
21980 else if (pg_strcasecmp(storagemode, "extended") == 0)
21981 cstorage = TYPSTORAGE_EXTENDED;
21982 else if (pg_strcasecmp(storagemode, "main") == 0)
21983 cstorage = TYPSTORAGE_MAIN;
21984 else if (pg_strcasecmp(storagemode, "default") == 0)
21985 cstorage = get_typstorage(atttypid);
21986 else
21987 ereport(ERROR,
21988 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21989 errmsg("invalid storage type \"%s\"",
21990 storagemode)));
21991
21992 /*
21993 * safety check: do not allow toasted storage modes unless column datatype
21994 * is TOAST-aware.
21995 */
21996 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
21997 ereport(ERROR,
21998 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21999 errmsg("column data type %s can only have storage PLAIN",
22000 format_type_be(atttypid))));
22001
22002 return cstorage;
22003}
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 12033 of file tablecmds.c.

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

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 12094 of file tablecmds.c.

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

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 21832 of file tablecmds.c.

21833{
21834 Relation pg_constraint;
21835 HeapTuple tuple;
21836 SysScanDesc scan;
21837 ScanKeyData key[2];
21838 List *constraints = NIL;
21839
21840 /*
21841 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21842 * scan.
21843 */
21844 if (RelationGetIndexList(partition) == NIL ||
21847 return NIL;
21848
21849 /* Search for constraints referencing this table */
21850 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21851 ScanKeyInit(&key[0],
21852 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21853 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21854 ScanKeyInit(&key[1],
21855 Anum_pg_constraint_contype, BTEqualStrategyNumber,
21856 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21857
21858 /* XXX This is a seqscan, as we don't have a usable index */
21859 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21860 while ((tuple = systable_getnext(scan)) != NULL)
21861 {
21862 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21863
21864 /*
21865 * We only need to process constraints that are part of larger ones.
21866 */
21867 if (!OidIsValid(constrForm->conparentid))
21868 continue;
21869
21870 constraints = lappend_oid(constraints, constrForm->oid);
21871 }
21872
21873 systable_endscan(scan);
21874 table_close(pg_constraint, AccessShareLock);
21875
21876 return constraints;
21877}
#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 17059 of file tablecmds.c.

17060{
17061 SMgrRelation dstrel;
17062
17063 /*
17064 * Since we copy the file directly without looking at the shared buffers,
17065 * we'd better first flush out any pages of the source relation that are
17066 * in shared buffers. We assume no new changes will be made while we are
17067 * holding exclusive lock on the rel.
17068 */
17070
17071 /*
17072 * Create and copy all forks of the relation, and schedule unlinking of
17073 * old physical files.
17074 *
17075 * NOTE: any conflict in relfilenumber value will be caught in
17076 * RelationCreateStorage().
17077 */
17078 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17079
17080 /* copy main fork */
17082 rel->rd_rel->relpersistence);
17083
17084 /* copy those extra forks that exist */
17085 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17086 forkNum <= MAX_FORKNUM; forkNum++)
17087 {
17088 if (smgrexists(RelationGetSmgr(rel), forkNum))
17089 {
17090 smgrcreate(dstrel, forkNum, false);
17091
17092 /*
17093 * WAL log creation if the relation is persistent, or this is the
17094 * init fork of an unlogged relation.
17095 */
17096 if (RelationIsPermanent(rel) ||
17097 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17098 forkNum == INIT_FORKNUM))
17099 log_smgrcreate(&newrlocator, forkNum);
17100 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17101 rel->rd_rel->relpersistence);
17102 }
17103 }
17104
17105 /* drop old relation, and close new one */
17107 smgrclose(dstrel);
17108}
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:4941
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 17779 of file tablecmds.c.

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

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

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 17550 of file tablecmds.c.

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

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:20016

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 19961 of file tablecmds.c.

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

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 19232 of file tablecmds.c.

19233{
19234 ListCell *l;
19235 List *oids_to_truncate = NIL;
19236 List *oids_to_drop = NIL;
19237
19238 foreach(l, on_commits)
19239 {
19240 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19241
19242 /* Ignore entry if already dropped in this xact */
19244 continue;
19245
19246 switch (oc->oncommit)
19247 {
19248 case ONCOMMIT_NOOP:
19250 /* Do nothing (there shouldn't be such entries, actually) */
19251 break;
19253
19254 /*
19255 * If this transaction hasn't accessed any temporary
19256 * relations, we can skip truncating ON COMMIT DELETE ROWS
19257 * tables, as they must still be empty.
19258 */
19260 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19261 break;
19262 case ONCOMMIT_DROP:
19263 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19264 break;
19265 }
19266 }
19267
19268 /*
19269 * Truncate relations before dropping so that all dependencies between
19270 * relations are removed after they are worked on. Doing it like this
19271 * might be a waste as it is possible that a relation being truncated will
19272 * be dropped anyway due to its parent being dropped, but this makes the
19273 * code more robust because of not having to re-check that the relation
19274 * exists at truncation time.
19275 */
19276 if (oids_to_truncate != NIL)
19277 heap_truncate(oids_to_truncate);
19278
19279 if (oids_to_drop != NIL)
19280 {
19281 ObjectAddresses *targetObjects = new_object_addresses();
19282
19283 foreach(l, oids_to_drop)
19284 {
19285 ObjectAddress object;
19286
19287 object.classId = RelationRelationId;
19288 object.objectId = lfirst_oid(l);
19289 object.objectSubId = 0;
19290
19291 Assert(!object_address_present(&object, targetObjects));
19292
19293 add_exact_object_address(&object, targetObjects);
19294 }
19295
19296 /*
19297 * Object deletion might involve toast table access (to clean up
19298 * toasted catalog entries), so ensure we have a valid snapshot.
19299 */
19301
19302 /*
19303 * Since this is an automatic drop, rather than one directly initiated
19304 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19305 */
19308
19310
19311#ifdef USE_ASSERT_CHECKING
19312
19313 /*
19314 * Note that table deletion will call remove_on_commit_action, so the
19315 * entry should get marked as deleted.
19316 */
19317 foreach(l, on_commits)
19318 {
19319 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19320
19321 if (oc->oncommit != ONCOMMIT_DROP)
19322 continue;
19323
19325 }
19326#endif
19327 }
19328}
#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 13055 of file tablecmds.c.

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

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

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 13158 of file tablecmds.c.

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

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 20079 of file tablecmds.c.

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

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 19498 of file tablecmds.c.

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

21471{
21473 Form_pg_class classform;
21474 HeapTuple tuple;
21475
21476 state = (struct AttachIndexCallbackState *) arg;
21477
21478 if (!state->lockedParentTbl)
21479 {
21480 LockRelationOid(state->parentTblOid, AccessShareLock);
21481 state->lockedParentTbl = true;
21482 }
21483
21484 /*
21485 * If we previously locked some other heap, and the name we're looking up
21486 * no longer refers to an index on that relation, release the now-useless
21487 * lock. XXX maybe we should do *after* we verify whether the index does
21488 * not actually belong to the same relation ...
21489 */
21490 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21491 {
21492 UnlockRelationOid(state->partitionOid, AccessShareLock);
21493 state->partitionOid = InvalidOid;
21494 }
21495
21496 /* Didn't find a relation, so no need for locking or permission checks. */
21497 if (!OidIsValid(relOid))
21498 return;
21499
21500 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21501 if (!HeapTupleIsValid(tuple))
21502 return; /* concurrently dropped, so nothing to do */
21503 classform = (Form_pg_class) GETSTRUCT(tuple);
21504 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21505 classform->relkind != RELKIND_INDEX)
21506 ereport(ERROR,
21507 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21508 errmsg("\"%s\" is not an index", rv->relname)));
21509 ReleaseSysCache(tuple);
21510
21511 /*
21512 * Since we need only examine the heap's tupledesc, an access share lock
21513 * on it (preventing any DDL) is sufficient.
21514 */
21515 state->partitionOid = IndexGetRelation(relOid, false);
21516 LockRelationOid(state->partitionOid, AccessShareLock);
21517}
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 19442 of file tablecmds.c.

19444{
19445 HeapTuple tuple;
19446
19447 /* Nothing to do if the relation was not found. */
19448 if (!OidIsValid(relId))
19449 return;
19450
19451 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19452 if (!HeapTupleIsValid(tuple)) /* should not happen */
19453 elog(ERROR, "cache lookup failed for relation %u", relId);
19454
19457
19458 ReleaseSysCache(tuple);
19459}

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 19406 of file tablecmds.c.

19408{
19409 char relkind;
19410 AclResult aclresult;
19411
19412 /* Nothing to do if the relation was not found. */
19413 if (!OidIsValid(relId))
19414 return;
19415
19416 /*
19417 * If the relation does exist, check whether it's an index. But note that
19418 * the relation might have been dropped between the time we did the name
19419 * lookup and now. In that case, there's nothing to do.
19420 */
19421 relkind = get_rel_relkind(relId);
19422 if (!relkind)
19423 return;
19424 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19425 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19426 ereport(ERROR,
19427 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19428 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19429
19430 /* Check permissions */
19431 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19432 if (aclresult != ACLCHECK_OK)
19433 aclcheck_error(aclresult,
19435 relation->relname);
19436}
#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 19466 of file tablecmds.c.

19468{
19469 HeapTuple tuple;
19470
19471 /* Nothing to do if the relation was not found. */
19472 if (!OidIsValid(relId))
19473 return;
19474
19475 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19476 if (!HeapTupleIsValid(tuple)) /* should not happen */
19477 elog(ERROR, "cache lookup failed for relation %u", relId);
19478
19479 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19481 relation->relname);
19482
19483 if (!allowSystemTableMods &&
19484 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19485 ereport(ERROR,
19486 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19487 errmsg("permission denied: \"%s\" is a system catalog",
19488 relation->relname)));
19489
19490 ReleaseSysCache(tuple);
19491}

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 15754 of file tablecmds.c.

15757{
15758 CommentStmt *cmd;
15759 char *comment_str;
15760 AlterTableCmd *newcmd;
15761
15762 /* Look for comment for object wanted, and leave if none */
15763 comment_str = GetComment(objid, ConstraintRelationId, 0);
15764 if (comment_str == NULL)
15765 return;
15766
15767 /* Build CommentStmt node, copying all input data for safety */
15768 cmd = makeNode(CommentStmt);
15769 if (rel)
15770 {
15772 cmd->object = (Node *)
15775 makeString(pstrdup(conname)));
15776 }
15777 else
15778 {
15780 cmd->object = (Node *)
15782 makeString(pstrdup(conname)));
15783 }
15784 cmd->comment = comment_str;
15785
15786 /* Append it to list of commands */
15787 newcmd = makeNode(AlterTableCmd);
15788 newcmd->subtype = AT_ReAddComment;
15789 newcmd->def = (Node *) cmd;
15790 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15791}
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 21685 of file tablecmds.c.

21686{
21687 Oid existingIdx;
21688
21689 existingIdx = index_get_partition(partitionTbl,
21690 RelationGetRelid(parentIdx));
21691 if (OidIsValid(existingIdx))
21692 ereport(ERROR,
21693 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21694 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21695 RelationGetRelationName(partIdx),
21696 RelationGetRelationName(parentIdx)),
21697 errdetail("Another index is already attached for partition \"%s\".",
21698 RelationGetRelationName(partitionTbl))));
21699}

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 19173 of file tablecmds.c.

19174{
19175 OnCommitItem *oc;
19176 MemoryContext oldcxt;
19177
19178 /*
19179 * We needn't bother registering the relation unless there is an ON COMMIT
19180 * action we need to take.
19181 */
19183 return;
19184
19186
19187 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
19188 oc->relid = relid;
19189 oc->oncommit = action;
19192
19193 /*
19194 * We use lcons() here so that ON COMMIT actions are processed in reverse
19195 * order of registration. That might not be essential but it seems
19196 * reasonable.
19197 */
19199
19200 MemoryContextSwitchTo(oldcxt);
19201}
List * lcons(void *datum, List *list)
Definition: list.c:495
MemoryContext CacheMemoryContext
Definition: mcxt.c:152

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 18314 of file tablecmds.c.

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

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

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 15219 of file tablecmds.c.

15220{
15221 if (!get_index_isclustered(indoid))
15222 return;
15223
15224 if (tab->clusterOnIndex)
15225 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15226
15227 tab->clusterOnIndex = get_rel_name(indoid);
15228}
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 15235 of file tablecmds.c.

15236{
15237 /*
15238 * This de-duplication check is critical for two independent reasons: we
15239 * mustn't try to recreate the same constraint twice, and if a constraint
15240 * depends on more than one column whose type is to be altered, we must
15241 * capture its definition string before applying any of the column type
15242 * changes. ruleutils.c will get confused if we ask again later.
15243 */
15244 if (!list_member_oid(tab->changedConstraintOids, conoid))
15245 {
15246 /* OK, capture the constraint's existing definition string */
15247 char *defstring = pg_get_constraintdef_command(conoid);
15248 Oid indoid;
15249
15250 /*
15251 * It is critical to create not-null constraints ahead of primary key
15252 * indexes; otherwise, the not-null constraint would be created by the
15253 * primary key, and the constraint name would be wrong.
15254 */
15255 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15256 {
15257 tab->changedConstraintOids = lcons_oid(conoid,
15259 tab->changedConstraintDefs = lcons(defstring,
15261 }
15262 else
15263 {
15264
15266 conoid);
15268 defstring);
15269 }
15270
15271 /*
15272 * For the index of a constraint, if any, remember if it is used for
15273 * the table's replica identity or if it is a clustered index, so that
15274 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15275 * those properties.
15276 */
15277 indoid = get_constraint_index(conoid);
15278 if (OidIsValid(indoid))
15279 {
15281 RememberClusterOnForRebuilding(indoid, tab);
15282 }
15283 }
15284}
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:15204
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15219

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 15291 of file tablecmds.c.

15292{
15293 /*
15294 * This de-duplication check is critical for two independent reasons: we
15295 * mustn't try to recreate the same index twice, and if an index depends
15296 * on more than one column whose type is to be altered, we must capture
15297 * its definition string before applying any of the column type changes.
15298 * ruleutils.c will get confused if we ask again later.
15299 */
15300 if (!list_member_oid(tab->changedIndexOids, indoid))
15301 {
15302 /*
15303 * Before adding it as an index-to-rebuild, we'd better see if it
15304 * belongs to a constraint, and if so rebuild the constraint instead.
15305 * Typically this check fails, because constraint indexes normally
15306 * have only dependencies on their constraint. But it's possible for
15307 * such an index to also have direct dependencies on table columns,
15308 * for example with a partial exclusion constraint.
15309 */
15310 Oid conoid = get_index_constraint(indoid);
15311
15312 if (OidIsValid(conoid))
15313 {
15315 }
15316 else
15317 {
15318 /* OK, capture the index's existing definition string */
15319 char *defstring = pg_get_indexdef_string(indoid);
15320
15322 indoid);
15324 defstring);
15325
15326 /*
15327 * Remember if this index is used for the table's replica identity
15328 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15329 * can queue up commands necessary to restore those properties.
15330 */
15332 RememberClusterOnForRebuilding(indoid, tab);
15333 }
15334 }
15335}
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 15204 of file tablecmds.c.

15205{
15206 if (!get_index_isreplident(indoid))
15207 return;
15208
15209 if (tab->replicaIdentityIndex)
15210 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15211
15212 tab->replicaIdentityIndex = get_rel_name(indoid);
15213}
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 15342 of file tablecmds.c.

15343{
15344 /*
15345 * This de-duplication check is critical for two independent reasons: we
15346 * mustn't try to recreate the same statistics object twice, and if the
15347 * statistics object depends on more than one column whose type is to be
15348 * altered, we must capture its definition string before applying any of
15349 * the type changes. ruleutils.c will get confused if we ask again later.
15350 */
15351 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15352 {
15353 /* OK, capture the statistics object's existing definition string */
15354 char *defstring = pg_get_statisticsobjdef_string(stxoid);
15355
15357 stxoid);
15359 defstring);
15360 }
15361}
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 19209 of file tablecmds.c.

19210{
19211 ListCell *l;
19212
19213 foreach(l, on_commits)
19214 {
19215 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19216
19217 if (oc->relid == relid)
19218 {
19220 break;
19221 }
19222 }
19223}

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 17862 of file tablecmds.c.

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

11885{
11886 ObjectAddresses *objs;
11887 HeapTuple consttup;
11889 SysScanDesc scan;
11890 HeapTuple trigtup;
11891
11893 Anum_pg_constraint_conrelid,
11894 BTEqualStrategyNumber, F_OIDEQ,
11895 ObjectIdGetDatum(conrelid));
11896
11897 scan = systable_beginscan(conrel,
11898 ConstraintRelidTypidNameIndexId,
11899 true, NULL, 1, &key);
11900 objs = new_object_addresses();
11901 while ((consttup = systable_getnext(scan)) != NULL)
11902 {
11903 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11904
11905 if (conform->conparentid != conoid)
11906 continue;
11907 else
11908 {
11909 ObjectAddress addr;
11910 SysScanDesc scan2;
11911 ScanKeyData key2;
11913
11914 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11915 add_exact_object_address(&addr, objs);
11916
11917 /*
11918 * First we must delete the dependency record that binds the
11919 * constraint records together.
11920 */
11921 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11922 conform->oid,
11924 ConstraintRelationId,
11925 conoid);
11926 Assert(n == 1); /* actually only one is expected */
11927
11928 /*
11929 * Now search for the triggers for this constraint and set them up
11930 * for deletion too
11931 */
11932 ScanKeyInit(&key2,
11933 Anum_pg_trigger_tgconstraint,
11934 BTEqualStrategyNumber, F_OIDEQ,
11935 ObjectIdGetDatum(conform->oid));
11936 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11937 true, NULL, 1, &key2);
11938 while ((trigtup = systable_getnext(scan2)) != NULL)
11939 {
11940 ObjectAddressSet(addr, TriggerRelationId,
11941 ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11942 add_exact_object_address(&addr, objs);
11943 }
11944 systable_endscan(scan2);
11945 }
11946 }
11947 /* make the dependency deletions visible */
11951 systable_endscan(scan);
11952}
#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 13265 of file tablecmds.c.

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

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 13423 of file tablecmds.c.

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

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

19640{
19641 PartitionSpec *newspec;
19642 ParseState *pstate;
19643 ParseNamespaceItem *nsitem;
19644 ListCell *l;
19645
19646 newspec = makeNode(PartitionSpec);
19647
19648 newspec->strategy = partspec->strategy;
19649 newspec->partParams = NIL;
19650 newspec->location = partspec->location;
19651
19652 /* Check valid number of columns for strategy */
19653 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19654 list_length(partspec->partParams) != 1)
19655 ereport(ERROR,
19656 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19657 errmsg("cannot use \"list\" partition strategy with more than one column")));
19658
19659 /*
19660 * Create a dummy ParseState and insert the target relation as its sole
19661 * rangetable entry. We need a ParseState for transformExpr.
19662 */
19663 pstate = make_parsestate(NULL);
19664 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19665 NULL, false, true);
19666 addNSItemToQuery(pstate, nsitem, true, true, true);
19667
19668 /* take care of any partition expressions */
19669 foreach(l, partspec->partParams)
19670 {
19672
19673 if (pelem->expr)
19674 {
19675 /* Copy, to avoid scribbling on the input */
19676 pelem = copyObject(pelem);
19677
19678 /* Now do parse transformation of the expression */
19679 pelem->expr = transformExpr(pstate, pelem->expr,
19681
19682 /* we have to fix its collations too */
19683 assign_expr_collations(pstate, pelem->expr);
19684 }
19685
19686 newspec->partParams = lappend(newspec->partParams, pelem);
19687 }
19688
19689 return newspec;
19690}
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:121
#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 11659 of file tablecmds.c.

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

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 15827 of file tablecmds.c.

15828{
15829 HeapTuple tup;
15830 Datum adatum;
15831 ArrayType *arr;
15832 Oid *rawarr;
15833 int numkeys;
15834 int i;
15835
15836 Assert(con->contype == CONSTR_FOREIGN);
15837 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15838
15839 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15840 if (!HeapTupleIsValid(tup)) /* should not happen */
15841 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15842
15843 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15844 Anum_pg_constraint_conpfeqop);
15845 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15846 numkeys = ARR_DIMS(arr)[0];
15847 /* test follows the one in ri_FetchConstraintInfo() */
15848 if (ARR_NDIM(arr) != 1 ||
15849 ARR_HASNULL(arr) ||
15850 ARR_ELEMTYPE(arr) != OIDOID)
15851 elog(ERROR, "conpfeqop is not a 1-D Oid array");
15852 rawarr = (Oid *) ARR_DATA_PTR(arr);
15853
15854 /* stash a List of the operator Oids in our Constraint node */
15855 for (i = 0; i < numkeys; i++)
15856 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15857
15858 ReleaseSysCache(tup);
15859}
#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 15798 of file tablecmds.c.

15799{
15800 if (CheckIndexCompatible(oldId,
15801 stmt->accessMethod,
15802 stmt->indexParams,
15803 stmt->excludeOpNames,
15804 stmt->iswithoutoverlaps))
15805 {
15806 Relation irel = index_open(oldId, NoLock);
15807
15808 /* If it's a partitioned index, there is no storage to share. */
15809 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15810 {
15811 stmt->oldNumber = irel->rd_locator.relNumber;
15812 stmt->oldCreateSubid = irel->rd_createSubid;
15813 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15814 }
15815 index_close(irel, NoLock);
15816 }
15817}
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 13632 of file tablecmds.c.

13638{
13639 TupleTableSlot *slot;
13640 TableScanDesc scan;
13641 Trigger trig = {0};
13642 Snapshot snapshot;
13643 MemoryContext oldcxt;
13644 MemoryContext perTupCxt;
13645
13647 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13648
13649 /*
13650 * Build a trigger call structure; we'll need it either way.
13651 */
13652 trig.tgoid = InvalidOid;
13653 trig.tgname = conname;
13655 trig.tgisinternal = true;
13656 trig.tgconstrrelid = RelationGetRelid(pkrel);
13657 trig.tgconstrindid = pkindOid;
13658 trig.tgconstraint = constraintOid;
13659 trig.tgdeferrable = false;
13660 trig.tginitdeferred = false;
13661 /* we needn't fill in remaining fields */
13662
13663 /*
13664 * See if we can do it with a single LEFT JOIN query. A false result
13665 * indicates we must proceed with the fire-the-trigger method. We can't do
13666 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13667 * left joins.
13668 */
13669 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13670 return;
13671
13672 /*
13673 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13674 * if that tuple had just been inserted. If any of those fail, it should
13675 * ereport(ERROR) and that's that.
13676 */
13677 snapshot = RegisterSnapshot(GetLatestSnapshot());
13678 slot = table_slot_create(rel, NULL);
13679 scan = table_beginscan(rel, snapshot, 0, NULL);
13680
13682 "validateForeignKeyConstraint",
13684 oldcxt = MemoryContextSwitchTo(perTupCxt);
13685
13686 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13687 {
13688 LOCAL_FCINFO(fcinfo, 0);
13689 TriggerData trigdata = {0};
13690
13692
13693 /*
13694 * Make a call to the trigger function
13695 *
13696 * No parameters are passed, but we do set a context
13697 */
13698 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13699
13700 /*
13701 * We assume RI_FKey_check_ins won't look at flinfo...
13702 */
13703 trigdata.type = T_TriggerData;
13705 trigdata.tg_relation = rel;
13706 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13707 trigdata.tg_trigslot = slot;
13708 trigdata.tg_trigger = &trig;
13709
13710 fcinfo->context = (Node *) &trigdata;
13711
13712 RI_FKey_check_ins(fcinfo);
13713
13714 MemoryContextReset(perTupCxt);
13715 }
13716
13717 MemoryContextSwitchTo(oldcxt);
13718 MemoryContextDelete(perTupCxt);
13719 table_endscan(scan);
13720 UnregisterSnapshot(snapshot);
13722}
#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 21708 of file tablecmds.c.

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

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 21810 of file tablecmds.c.

21811{
21812 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21813 {
21815 iinfo->ii_IndexAttrNumbers[i] - 1);
21816
21817 if (!att->attnotnull)
21818 ereport(ERROR,
21819 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21820 errmsg("invalid primary key definition"),
21821 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21822 NameStr(att->attname),
21823 RelationGetRelationName(partition)));
21824 }
21825}
int ii_NumIndexKeyAttrs
Definition: execnodes.h:196
AttrNumber ii_IndexAttrNumbers[INDEX_MAX_KEYS]
Definition: execnodes.h:197

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