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, List **otherrelids, LOCKMODE lockmode)
 
static void AlterConstrTriggerDeferrability (Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
 
static void ATExecAlterChildConstr (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
 
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 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, LOCKMODE lockmode)
 
static ObjectAddress ATExecSetNotNull (List **wqueue, Relation rel, char *constrname, 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 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 void validateFkOnDeleteSetColumns (int numfks, const int16 *fkattnums, int numfksetcols, const 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 (Relation rel, 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 partIdx)
 
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 7618 of file tablecmds.c.

7619{
7620 ObjectAddress myself,
7621 referenced;
7622
7623 /* We know the default collation is pinned, so don't bother recording it */
7624 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7625 {
7626 myself.classId = RelationRelationId;
7627 myself.objectId = relid;
7628 myself.objectSubId = attnum;
7629 referenced.classId = CollationRelationId;
7630 referenced.objectId = collid;
7631 referenced.objectSubId = 0;
7632 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7633 }
7634}
#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 7600 of file tablecmds.c.

7601{
7602 ObjectAddress myself,
7603 referenced;
7604
7605 myself.classId = RelationRelationId;
7606 myself.objectId = relid;
7607 myself.objectSubId = attnum;
7608 referenced.classId = TypeRelationId;
7609 referenced.objectId = typid;
7610 referenced.objectSubId = 0;
7611 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7612}

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

10494{
10495 ObjectAddress address;
10496 Oid constrOid;
10497 char *conname;
10498 bool conislocal;
10499 int16 coninhcount;
10500 bool connoinherit;
10501
10502 /*
10503 * Verify relkind for each referenced partition. At the top level, this
10504 * is redundant with a previous check, but we need it when recursing.
10505 */
10506 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10507 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10508 ereport(ERROR,
10509 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10510 errmsg("referenced relation \"%s\" is not a table",
10511 RelationGetRelationName(pkrel))));
10512
10513 /*
10514 * Caller supplies us with a constraint name; however, it may be used in
10515 * this partition, so come up with a different one in that case.
10516 */
10518 RelationGetRelid(rel),
10519 constraintname))
10522 "fkey",
10524 else
10525 conname = constraintname;
10526
10527 if (fkconstraint->conname == NULL)
10528 fkconstraint->conname = pstrdup(conname);
10529
10530 if (OidIsValid(parentConstr))
10531 {
10532 conislocal = false;
10533 coninhcount = 1;
10534 connoinherit = false;
10535 }
10536 else
10537 {
10538 conislocal = true;
10539 coninhcount = 0;
10540
10541 /*
10542 * always inherit for partitioned tables, never for legacy inheritance
10543 */
10544 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10545 }
10546
10547 /*
10548 * Record the FK constraint in pg_constraint.
10549 */
10550 constrOid = CreateConstraintEntry(conname,
10552 CONSTRAINT_FOREIGN,
10553 fkconstraint->deferrable,
10554 fkconstraint->initdeferred,
10555 true, /* Is Enforced */
10556 fkconstraint->initially_valid,
10557 parentConstr,
10558 RelationGetRelid(rel),
10559 fkattnum,
10560 numfks,
10561 numfks,
10562 InvalidOid, /* not a domain constraint */
10563 indexOid,
10564 RelationGetRelid(pkrel),
10565 pkattnum,
10566 pfeqoperators,
10567 ppeqoperators,
10568 ffeqoperators,
10569 numfks,
10570 fkconstraint->fk_upd_action,
10571 fkconstraint->fk_del_action,
10572 fkdelsetcols,
10573 numfkdelsetcols,
10574 fkconstraint->fk_matchtype,
10575 NULL, /* no exclusion constraint */
10576 NULL, /* no check constraint */
10577 NULL,
10578 conislocal, /* islocal */
10579 coninhcount, /* inhcount */
10580 connoinherit, /* conNoInherit */
10581 with_period, /* conPeriod */
10582 is_internal); /* is_internal */
10583
10584 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10585
10586 /*
10587 * In partitioning cases, create the dependency entries for this
10588 * constraint. (For non-partitioned cases, relevant entries were created
10589 * by CreateConstraintEntry.)
10590 *
10591 * On the referenced side, we need the constraint to have an internal
10592 * dependency on its parent constraint; this means that this constraint
10593 * cannot be dropped on its own -- only through the parent constraint. It
10594 * also means the containing partition cannot be dropped on its own, but
10595 * it can be detached, at which point this dependency is removed (after
10596 * verifying that no rows are referenced via this FK.)
10597 *
10598 * When processing the referencing side, we link the constraint via the
10599 * special partitioning dependencies: the parent constraint is the primary
10600 * dependent, and the partition on which the foreign key exists is the
10601 * secondary dependency. That way, this constraint is dropped if either
10602 * of these objects is.
10603 *
10604 * Note that this is only necessary for the subsidiary pg_constraint rows
10605 * in partitions; the topmost row doesn't need any of this.
10606 */
10607 if (OidIsValid(parentConstr))
10608 {
10609 ObjectAddress referenced;
10610
10611 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10612
10613 Assert(fkside != addFkBothSides);
10614 if (fkside == addFkReferencedSide)
10615 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10616 else
10617 {
10618 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10619 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10620 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10621 }
10622 }
10623
10624 /* make new constraint visible, in case we add more */
10626
10627 return address;
10628}
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:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#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:1699
#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:37
unsigned int Oid
Definition: postgres_ext.h:32
#define RelationGetRelid(relation)
Definition: rel.h:513
#define RelationGetRelationName(relation)
Definition: rel.h:547
#define RelationGetNamespace(relation)
Definition: rel.h:554
bool initdeferred
Definition: parsenodes.h:2818
char fk_upd_action
Definition: parsenodes.h:2852
char fk_matchtype
Definition: parsenodes.h:2851
bool initially_valid
Definition: parsenodes.h:2821
bool deferrable
Definition: parsenodes.h:2817
char * conname
Definition: parsenodes.h:2816
char fk_del_action
Definition: parsenodes.h:2853
List * fk_attrs
Definition: parsenodes.h:2847
Form_pg_class rd_rel
Definition: rel.h:111
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition: tablecmds.c:9660
void CommandCounterIncrement(void)
Definition: xact.c:1100

References addFkBothSides, addFkReferencedSide, Assert(), ChooseConstraintName(), ChooseForeignKeyConstraintNameAddition(), CommandCounterIncrement(), Constraint::conname, CONSTRAINT_RELATION, ConstraintNameIsUsed(), CreateConstraintEntry(), Constraint::deferrable, DEPENDENCY_INTERNAL, DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, ereport, errcode(), errmsg(), ERROR, Constraint::fk_attrs, Constraint::fk_del_action, Constraint::fk_matchtype, Constraint::fk_upd_action, Constraint::initdeferred, Constraint::initially_valid, InvalidOid, 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 10663 of file tablecmds.c.

10672{
10673 Oid deleteTriggerOid,
10674 updateTriggerOid;
10675
10678
10679 /*
10680 * Create the action triggers that enforce the constraint.
10681 */
10683 fkconstraint,
10684 parentConstr, indexOid,
10685 parentDelTrigger, parentUpdTrigger,
10686 &deleteTriggerOid, &updateTriggerOid);
10687
10688 /*
10689 * If the referenced table is partitioned, recurse on ourselves to handle
10690 * each partition. We need one pg_constraint row created for each
10691 * partition in addition to the pg_constraint row for the parent table.
10692 */
10693 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10694 {
10695 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10696
10697 for (int i = 0; i < pd->nparts; i++)
10698 {
10699 Relation partRel;
10700 AttrMap *map;
10701 AttrNumber *mapped_pkattnum;
10702 Oid partIndexId;
10703 ObjectAddress address;
10704
10705 /* XXX would it be better to acquire these locks beforehand? */
10706 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10707
10708 /*
10709 * Map the attribute numbers in the referenced side of the FK
10710 * definition to match the partition's column layout.
10711 */
10713 RelationGetDescr(pkrel),
10714 false);
10715 if (map)
10716 {
10717 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10718 for (int j = 0; j < numfks; j++)
10719 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10720 }
10721 else
10722 mapped_pkattnum = pkattnum;
10723
10724 /* Determine the index to use at this level */
10725 partIndexId = index_get_partition(partRel, indexOid);
10726 if (!OidIsValid(partIndexId))
10727 elog(ERROR, "index for %u not found in partition %s",
10728 indexOid, RelationGetRelationName(partRel));
10729
10730 /* Create entry at this level ... */
10732 fkconstraint->conname, fkconstraint, rel,
10733 partRel, partIndexId, parentConstr,
10734 numfks, mapped_pkattnum,
10735 fkattnum, pfeqoperators, ppeqoperators,
10736 ffeqoperators, numfkdelsetcols,
10737 fkdelsetcols, true, with_period);
10738 /* ... and recurse to our children */
10739 addFkRecurseReferenced(fkconstraint, rel, partRel,
10740 partIndexId, address.objectId, numfks,
10741 mapped_pkattnum, fkattnum,
10742 pfeqoperators, ppeqoperators, ffeqoperators,
10743 numfkdelsetcols, fkdelsetcols,
10744 old_check_ok,
10745 deleteTriggerOid, updateTriggerOid,
10746 with_period);
10747
10748 /* Done -- clean up (but keep the lock) */
10749 table_close(partRel, NoLock);
10750 if (map)
10751 {
10752 pfree(mapped_pkattnum);
10753 free_attrmap(map);
10754 }
10755 }
10756 }
10757}
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:75
int i
Definition: isn.c:74
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:1524
void * palloc(Size size)
Definition: mcxt.c:1317
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:539
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(Relation rel, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13148
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:10487
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:10663

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(), 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 10798 of file tablecmds.c.

10806{
10807 Oid insertTriggerOid,
10808 updateTriggerOid;
10809
10810 Assert(OidIsValid(parentConstr));
10813
10814 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10815 ereport(ERROR,
10816 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10817 errmsg("foreign key constraints are not supported on foreign tables")));
10818
10819 /*
10820 * Add the check triggers to it and, if necessary, schedule it to be
10821 * checked in Phase 3.
10822 *
10823 * If the relation is partitioned, drill down to do it to its partitions.
10824 */
10826 RelationGetRelid(pkrel),
10827 fkconstraint,
10828 parentConstr,
10829 indexOid,
10830 parentInsTrigger, parentUpdTrigger,
10831 &insertTriggerOid, &updateTriggerOid);
10832
10833 if (rel->rd_rel->relkind == RELKIND_RELATION)
10834 {
10835 /*
10836 * Tell Phase 3 to check that the constraint is satisfied by existing
10837 * rows. We can skip this during table creation, when requested
10838 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10839 * and when we're recreating a constraint following a SET DATA TYPE
10840 * operation that did not impugn its validity.
10841 */
10842 if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10843 {
10844 NewConstraint *newcon;
10845 AlteredTableInfo *tab;
10846
10847 tab = ATGetQueueEntry(wqueue, rel);
10848
10849 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10850 newcon->name = get_constraint_name(parentConstr);
10851 newcon->contype = CONSTR_FOREIGN;
10852 newcon->refrelid = RelationGetRelid(pkrel);
10853 newcon->refindid = indexOid;
10854 newcon->conid = parentConstr;
10855 newcon->conwithperiod = fkconstraint->fk_with_period;
10856 newcon->qual = (Node *) fkconstraint;
10857
10858 tab->constraints = lappend(tab->constraints, newcon);
10859 }
10860 }
10861 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10862 {
10864 Relation trigrel;
10865
10866 /*
10867 * Triggers of the foreign keys will be manipulated a bunch of times
10868 * in the loop below. To avoid repeatedly opening/closing the trigger
10869 * catalog relation, we open it here and pass it to the subroutines
10870 * called below.
10871 */
10872 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10873
10874 /*
10875 * Recurse to take appropriate action on each partition; either we
10876 * find an existing constraint to reparent to ours, or we create a new
10877 * one.
10878 */
10879 for (int i = 0; i < pd->nparts; i++)
10880 {
10881 Relation partition = table_open(pd->oids[i], lockmode);
10882 List *partFKs;
10883 AttrMap *attmap;
10884 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10885 bool attached;
10886 ObjectAddress address;
10887
10888 CheckAlterTableIsSafe(partition);
10889
10890 attmap = build_attrmap_by_name(RelationGetDescr(partition),
10891 RelationGetDescr(rel),
10892 false);
10893 for (int j = 0; j < numfks; j++)
10894 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10895
10896 /* Check whether an existing constraint can be repurposed */
10897 partFKs = copyObject(RelationGetFKeyList(partition));
10898 attached = false;
10899 foreach_node(ForeignKeyCacheInfo, fk, partFKs)
10900 {
10902 fk,
10903 partition,
10904 parentConstr,
10905 numfks,
10906 mapped_fkattnum,
10907 pkattnum,
10908 pfeqoperators,
10909 insertTriggerOid,
10910 updateTriggerOid,
10911 trigrel))
10912 {
10913 attached = true;
10914 break;
10915 }
10916 }
10917 if (attached)
10918 {
10919 table_close(partition, NoLock);
10920 continue;
10921 }
10922
10923 /*
10924 * No luck finding a good constraint to reuse; create our own.
10925 */
10927 fkconstraint->conname, fkconstraint,
10928 partition, pkrel, indexOid, parentConstr,
10929 numfks, pkattnum,
10930 mapped_fkattnum, pfeqoperators,
10931 ppeqoperators, ffeqoperators,
10932 numfkdelsetcols, fkdelsetcols, true,
10933 with_period);
10934
10935 /* call ourselves to finalize the creation and we're done */
10936 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10937 indexOid,
10938 address.objectId,
10939 numfks,
10940 pkattnum,
10941 mapped_fkattnum,
10942 pfeqoperators,
10943 ppeqoperators,
10944 ffeqoperators,
10945 numfkdelsetcols,
10946 fkdelsetcols,
10947 old_check_ok,
10948 lockmode,
10949 insertTriggerOid,
10950 updateTriggerOid,
10951 with_period);
10952
10953 table_close(partition, NoLock);
10954 }
10955
10956 table_close(trigrel, RowExclusiveLock);
10957 }
10958}
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:1116
void * palloc0(Size size)
Definition: mcxt.c:1347
#define copyObject(obj)
Definition: nodes.h:226
@ CONSTR_FOREIGN
Definition: parsenodes.h:2791
#define INDEX_MAX_KEYS
#define foreach_node(type, var, lst)
Definition: pg_list.h:496
List * RelationGetFKeyList(Relation relation)
Definition: relcache.c:4655
List * constraints
Definition: tablecmds.c:187
bool fk_with_period
Definition: parsenodes.h:2849
bool skip_validation
Definition: parsenodes.h:2820
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:131
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition: tablecmds.c:6465
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:10798
static void CheckAlterTableIsSafe(Relation rel)
Definition: tablecmds.c:4410
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13285
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:11460

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

6500{
6501 switch (cmdtype)
6502 {
6503 case AT_AddColumn:
6504 case AT_AddColumnToView:
6505 return "ADD COLUMN";
6506 case AT_ColumnDefault:
6508 return "ALTER COLUMN ... SET DEFAULT";
6509 case AT_DropNotNull:
6510 return "ALTER COLUMN ... DROP NOT NULL";
6511 case AT_SetNotNull:
6512 return "ALTER COLUMN ... SET NOT NULL";
6513 case AT_SetExpression:
6514 return "ALTER COLUMN ... SET EXPRESSION";
6515 case AT_DropExpression:
6516 return "ALTER COLUMN ... DROP EXPRESSION";
6517 case AT_SetStatistics:
6518 return "ALTER COLUMN ... SET STATISTICS";
6519 case AT_SetOptions:
6520 return "ALTER COLUMN ... SET";
6521 case AT_ResetOptions:
6522 return "ALTER COLUMN ... RESET";
6523 case AT_SetStorage:
6524 return "ALTER COLUMN ... SET STORAGE";
6525 case AT_SetCompression:
6526 return "ALTER COLUMN ... SET COMPRESSION";
6527 case AT_DropColumn:
6528 return "DROP COLUMN";
6529 case AT_AddIndex:
6530 case AT_ReAddIndex:
6531 return NULL; /* not real grammar */
6532 case AT_AddConstraint:
6533 case AT_ReAddConstraint:
6536 return "ADD CONSTRAINT";
6537 case AT_AlterConstraint:
6538 return "ALTER CONSTRAINT";
6540 return "VALIDATE CONSTRAINT";
6541 case AT_DropConstraint:
6542 return "DROP CONSTRAINT";
6543 case AT_ReAddComment:
6544 return NULL; /* not real grammar */
6545 case AT_AlterColumnType:
6546 return "ALTER COLUMN ... SET DATA TYPE";
6548 return "ALTER COLUMN ... OPTIONS";
6549 case AT_ChangeOwner:
6550 return "OWNER TO";
6551 case AT_ClusterOn:
6552 return "CLUSTER ON";
6553 case AT_DropCluster:
6554 return "SET WITHOUT CLUSTER";
6555 case AT_SetAccessMethod:
6556 return "SET ACCESS METHOD";
6557 case AT_SetLogged:
6558 return "SET LOGGED";
6559 case AT_SetUnLogged:
6560 return "SET UNLOGGED";
6561 case AT_DropOids:
6562 return "SET WITHOUT OIDS";
6563 case AT_SetTableSpace:
6564 return "SET TABLESPACE";
6565 case AT_SetRelOptions:
6566 return "SET";
6567 case AT_ResetRelOptions:
6568 return "RESET";
6570 return NULL; /* not real grammar */
6571 case AT_EnableTrig:
6572 return "ENABLE TRIGGER";
6574 return "ENABLE ALWAYS TRIGGER";
6576 return "ENABLE REPLICA TRIGGER";
6577 case AT_DisableTrig:
6578 return "DISABLE TRIGGER";
6579 case AT_EnableTrigAll:
6580 return "ENABLE TRIGGER ALL";
6581 case AT_DisableTrigAll:
6582 return "DISABLE TRIGGER ALL";
6583 case AT_EnableTrigUser:
6584 return "ENABLE TRIGGER USER";
6585 case AT_DisableTrigUser:
6586 return "DISABLE TRIGGER USER";
6587 case AT_EnableRule:
6588 return "ENABLE RULE";
6590 return "ENABLE ALWAYS RULE";
6592 return "ENABLE REPLICA RULE";
6593 case AT_DisableRule:
6594 return "DISABLE RULE";
6595 case AT_AddInherit:
6596 return "INHERIT";
6597 case AT_DropInherit:
6598 return "NO INHERIT";
6599 case AT_AddOf:
6600 return "OF";
6601 case AT_DropOf:
6602 return "NOT OF";
6603 case AT_ReplicaIdentity:
6604 return "REPLICA IDENTITY";
6606 return "ENABLE ROW SECURITY";
6608 return "DISABLE ROW SECURITY";
6610 return "FORCE ROW SECURITY";
6612 return "NO FORCE ROW SECURITY";
6613 case AT_GenericOptions:
6614 return "OPTIONS";
6615 case AT_AttachPartition:
6616 return "ATTACH PARTITION";
6617 case AT_DetachPartition:
6618 return "DETACH PARTITION";
6620 return "DETACH PARTITION ... FINALIZE";
6621 case AT_AddIdentity:
6622 return "ALTER COLUMN ... ADD IDENTITY";
6623 case AT_SetIdentity:
6624 return "ALTER COLUMN ... SET";
6625 case AT_DropIdentity:
6626 return "ALTER COLUMN ... DROP IDENTITY";
6627 case AT_ReAddStatistics:
6628 return NULL; /* not real grammar */
6629 }
6630
6631 return NULL;
6632}
@ AT_AddIndexConstraint
Definition: parsenodes.h:2425
@ AT_DropOf
Definition: parsenodes.h:2456
@ AT_SetOptions
Definition: parsenodes.h:2413
@ AT_DropIdentity
Definition: parsenodes.h:2468
@ AT_DisableTrigUser
Definition: parsenodes.h:2448
@ AT_DropNotNull
Definition: parsenodes.h:2408
@ AT_AddOf
Definition: parsenodes.h:2455
@ AT_ResetOptions
Definition: parsenodes.h:2414
@ AT_ReplicaIdentity
Definition: parsenodes.h:2457
@ AT_ReplaceRelOptions
Definition: parsenodes.h:2440
@ AT_EnableRowSecurity
Definition: parsenodes.h:2458
@ AT_AddColumnToView
Definition: parsenodes.h:2405
@ AT_ResetRelOptions
Definition: parsenodes.h:2439
@ AT_EnableReplicaTrig
Definition: parsenodes.h:2443
@ AT_DropOids
Definition: parsenodes.h:2435
@ AT_SetIdentity
Definition: parsenodes.h:2467
@ AT_ReAddStatistics
Definition: parsenodes.h:2469
@ AT_SetUnLogged
Definition: parsenodes.h:2434
@ AT_DisableTrig
Definition: parsenodes.h:2444
@ AT_SetCompression
Definition: parsenodes.h:2416
@ AT_DropExpression
Definition: parsenodes.h:2411
@ AT_AddIndex
Definition: parsenodes.h:2418
@ AT_EnableReplicaRule
Definition: parsenodes.h:2451
@ AT_ReAddIndex
Definition: parsenodes.h:2419
@ AT_DropConstraint
Definition: parsenodes.h:2426
@ AT_SetNotNull
Definition: parsenodes.h:2409
@ AT_ClusterOn
Definition: parsenodes.h:2431
@ AT_AddIdentity
Definition: parsenodes.h:2466
@ AT_ForceRowSecurity
Definition: parsenodes.h:2460
@ AT_EnableAlwaysRule
Definition: parsenodes.h:2450
@ AT_SetAccessMethod
Definition: parsenodes.h:2436
@ AT_AlterColumnType
Definition: parsenodes.h:2428
@ AT_DetachPartitionFinalize
Definition: parsenodes.h:2465
@ AT_AddInherit
Definition: parsenodes.h:2453
@ AT_ReAddDomainConstraint
Definition: parsenodes.h:2422
@ AT_EnableTrig
Definition: parsenodes.h:2441
@ AT_DropColumn
Definition: parsenodes.h:2417
@ AT_ReAddComment
Definition: parsenodes.h:2427
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:2429
@ AT_DisableTrigAll
Definition: parsenodes.h:2446
@ AT_EnableRule
Definition: parsenodes.h:2449
@ AT_NoForceRowSecurity
Definition: parsenodes.h:2461
@ AT_DetachPartition
Definition: parsenodes.h:2464
@ AT_SetStatistics
Definition: parsenodes.h:2412
@ AT_AttachPartition
Definition: parsenodes.h:2463
@ AT_AddConstraint
Definition: parsenodes.h:2420
@ AT_DropInherit
Definition: parsenodes.h:2454
@ AT_EnableAlwaysTrig
Definition: parsenodes.h:2442
@ AT_SetLogged
Definition: parsenodes.h:2433
@ AT_SetStorage
Definition: parsenodes.h:2415
@ AT_DisableRule
Definition: parsenodes.h:2452
@ AT_DisableRowSecurity
Definition: parsenodes.h:2459
@ AT_SetRelOptions
Definition: parsenodes.h:2438
@ AT_ChangeOwner
Definition: parsenodes.h:2430
@ AT_EnableTrigUser
Definition: parsenodes.h:2447
@ AT_SetExpression
Definition: parsenodes.h:2410
@ AT_ReAddConstraint
Definition: parsenodes.h:2421
@ AT_SetTableSpace
Definition: parsenodes.h:2437
@ AT_GenericOptions
Definition: parsenodes.h:2462
@ AT_ColumnDefault
Definition: parsenodes.h:2406
@ AT_CookedColumnDefault
Definition: parsenodes.h:2407
@ AT_AlterConstraint
Definition: parsenodes.h:2423
@ AT_EnableTrigAll
Definition: parsenodes.h:2445
@ AT_DropCluster
Definition: parsenodes.h:2432
@ AT_ValidateConstraint
Definition: parsenodes.h:2424
@ AT_AddColumn
Definition: parsenodes.h:2404

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().

◆ AlterConstrTriggerDeferrability()

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

Definition at line 12212 of file tablecmds.c.

12215{
12216 HeapTuple tgtuple;
12217 ScanKeyData tgkey;
12218 SysScanDesc tgscan;
12219
12220 ScanKeyInit(&tgkey,
12221 Anum_pg_trigger_tgconstraint,
12222 BTEqualStrategyNumber, F_OIDEQ,
12223 ObjectIdGetDatum(conoid));
12224 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12225 NULL, 1, &tgkey);
12226 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12227 {
12228 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12229 Form_pg_trigger copy_tg;
12230 HeapTuple tgCopyTuple;
12231
12232 /*
12233 * Remember OIDs of other relation(s) involved in FK constraint.
12234 * (Note: it's likely that we could skip forcing a relcache inval for
12235 * other rels that don't have a trigger whose properties change, but
12236 * let's be conservative.)
12237 */
12238 if (tgform->tgrelid != RelationGetRelid(rel))
12239 *otherrelids = list_append_unique_oid(*otherrelids,
12240 tgform->tgrelid);
12241
12242 /*
12243 * Update enable status and deferrability of RI_FKey_noaction_del,
12244 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12245 * triggers, but not others; see createForeignKeyActionTriggers and
12246 * CreateFKCheckTrigger.
12247 */
12248 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12249 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12250 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12251 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12252 continue;
12253
12254 tgCopyTuple = heap_copytuple(tgtuple);
12255 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12256
12257 copy_tg->tgdeferrable = deferrable;
12258 copy_tg->tginitdeferred = initdeferred;
12259 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12260
12261 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12262
12263 heap_freetuple(tgCopyTuple);
12264 }
12265
12266 systable_endscan(tgscan);
12267}
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
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:778
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
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
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
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 ATExecAlterConstraintInternal().

◆ AlterIndexNamespaces()

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

Definition at line 18398 of file tablecmds.c.

18400{
18401 List *indexList;
18402 ListCell *l;
18403
18404 indexList = RelationGetIndexList(rel);
18405
18406 foreach(l, indexList)
18407 {
18408 Oid indexOid = lfirst_oid(l);
18409 ObjectAddress thisobj;
18410
18411 thisobj.classId = RelationRelationId;
18412 thisobj.objectId = indexOid;
18413 thisobj.objectSubId = 0;
18414
18415 /*
18416 * Note: currently, the index will not have its own dependency on the
18417 * namespace, so we don't need to do changeDependencyFor(). There's no
18418 * row type in pg_type, either.
18419 *
18420 * XXX this objsMoved test may be pointless -- surely we have a single
18421 * dependency link from a relation to each index?
18422 */
18423 if (!object_address_present(&thisobj, objsMoved))
18424 {
18425 AlterRelationNamespaceInternal(classRel, indexOid,
18426 oldNspOid, newNspOid,
18427 false, objsMoved);
18428 add_exact_object_address(&thisobj, objsMoved);
18429 }
18430 }
18431
18432 list_free(indexList);
18433}
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:4764
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
Definition: tablecmds.c:18321

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

18325{
18326 HeapTuple classTup;
18327 Form_pg_class classForm;
18328 ObjectAddress thisobj;
18329 bool already_done = false;
18330
18331 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
18332 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
18333 if (!HeapTupleIsValid(classTup))
18334 elog(ERROR, "cache lookup failed for relation %u", relOid);
18335 classForm = (Form_pg_class) GETSTRUCT(classTup);
18336
18337 Assert(classForm->relnamespace == oldNspOid);
18338
18339 thisobj.classId = RelationRelationId;
18340 thisobj.objectId = relOid;
18341 thisobj.objectSubId = 0;
18342
18343 /*
18344 * If the object has already been moved, don't move it again. If it's
18345 * already in the right place, don't move it, but still fire the object
18346 * access hook.
18347 */
18348 already_done = object_address_present(&thisobj, objsMoved);
18349 if (!already_done && oldNspOid != newNspOid)
18350 {
18351 ItemPointerData otid = classTup->t_self;
18352
18353 /* check for duplicate name (more friendly than unique-index failure) */
18354 if (get_relname_relid(NameStr(classForm->relname),
18355 newNspOid) != InvalidOid)
18356 ereport(ERROR,
18357 (errcode(ERRCODE_DUPLICATE_TABLE),
18358 errmsg("relation \"%s\" already exists in schema \"%s\"",
18359 NameStr(classForm->relname),
18360 get_namespace_name(newNspOid))));
18361
18362 /* classTup is a copy, so OK to scribble on */
18363 classForm->relnamespace = newNspOid;
18364
18365 CatalogTupleUpdate(classRel, &otid, classTup);
18366 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
18367
18368
18369 /* Update dependency on schema if caller said so */
18370 if (hasDependEntry &&
18371 changeDependencyFor(RelationRelationId,
18372 relOid,
18373 NamespaceRelationId,
18374 oldNspOid,
18375 newNspOid) != 1)
18376 elog(ERROR, "could not change schema dependency for relation \"%s\"",
18377 NameStr(classForm->relname));
18378 }
18379 else
18380 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
18381 if (!already_done)
18382 {
18383 add_exact_object_address(&thisobj, objsMoved);
18384
18385 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
18386 }
18387
18388 heap_freetuple(classTup);
18389}
#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:3449
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:1968
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 18443 of file tablecmds.c.

18446{
18447 Relation depRel;
18448 SysScanDesc scan;
18449 ScanKeyData key[2];
18450 HeapTuple tup;
18451
18452 /*
18453 * SERIAL sequences are those having an auto dependency on one of the
18454 * table's columns (we don't care *which* column, exactly).
18455 */
18456 depRel = table_open(DependRelationId, AccessShareLock);
18457
18458 ScanKeyInit(&key[0],
18459 Anum_pg_depend_refclassid,
18460 BTEqualStrategyNumber, F_OIDEQ,
18461 ObjectIdGetDatum(RelationRelationId));
18462 ScanKeyInit(&key[1],
18463 Anum_pg_depend_refobjid,
18464 BTEqualStrategyNumber, F_OIDEQ,
18466 /* we leave refobjsubid unspecified */
18467
18468 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
18469 NULL, 2, key);
18470
18471 while (HeapTupleIsValid(tup = systable_getnext(scan)))
18472 {
18473 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
18474 Relation seqRel;
18475
18476 /* skip dependencies other than auto dependencies on columns */
18477 if (depForm->refobjsubid == 0 ||
18478 depForm->classid != RelationRelationId ||
18479 depForm->objsubid != 0 ||
18480 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
18481 continue;
18482
18483 /* Use relation_open just in case it's an index */
18484 seqRel = relation_open(depForm->objid, lockmode);
18485
18486 /* skip non-sequence relations */
18487 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
18488 {
18489 /* No need to keep the lock */
18490 relation_close(seqRel, lockmode);
18491 continue;
18492 }
18493
18494 /* Fix the pg_class and pg_depend entries */
18495 AlterRelationNamespaceInternal(classRel, depForm->objid,
18496 oldNspOid, newNspOid,
18497 true, objsMoved);
18498
18499 /*
18500 * Sequences used to have entries in pg_type, but no longer do. If we
18501 * ever re-instate that, we'll need to move the pg_type entry to the
18502 * new namespace, too (using AlterTypeNamespaceInternal).
18503 */
18504 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
18505
18506 /* Now we can close it. Keep the lock till end of transaction. */
18507 relation_close(seqRel, NoLock);
18508 }
18509
18510 systable_endscan(scan);
18511
18513}
@ 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:507
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 4495 of file tablecmds.c.

4497{
4498 Relation rel;
4499
4500 /* Caller is required to provide an adequate lock. */
4501 rel = relation_open(context->relid, NoLock);
4502
4504
4505 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4506}
#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:4831

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

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4569 of file tablecmds.c.

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

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

4525{
4526 Relation rel;
4527 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4528
4529 rel = relation_open(relid, lockmode);
4530
4532
4533 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4534}
void EventTriggerAlterTableRelid(Oid objectId)
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition: tablecmds.c:4569

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4436 of file tablecmds.c.

4437{
4438 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4439 stmt->missing_ok ? RVR_MISSING_OK : 0,
4441 stmt);
4442}
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:18853

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

Referenced by ProcessUtilitySlow().

◆ AlterTableMoveAll()

Oid AlterTableMoveAll ( AlterTableMoveAllStmt stmt)

Definition at line 16252 of file tablecmds.c.

16253{
16254 List *relations = NIL;
16255 ListCell *l;
16256 ScanKeyData key[1];
16257 Relation rel;
16258 TableScanDesc scan;
16259 HeapTuple tuple;
16260 Oid orig_tablespaceoid;
16261 Oid new_tablespaceoid;
16262 List *role_oids = roleSpecsToIds(stmt->roles);
16263
16264 /* Ensure we were not asked to move something we can't */
16265 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16266 stmt->objtype != OBJECT_MATVIEW)
16267 ereport(ERROR,
16268 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16269 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16270
16271 /* Get the orig and new tablespace OIDs */
16272 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16273 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16274
16275 /* Can't move shared relations in to or out of pg_global */
16276 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16277 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16278 new_tablespaceoid == GLOBALTABLESPACE_OID)
16279 ereport(ERROR,
16280 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16281 errmsg("cannot move relations in to or out of pg_global tablespace")));
16282
16283 /*
16284 * Must have CREATE rights on the new tablespace, unless it is the
16285 * database default tablespace (which all users implicitly have CREATE
16286 * rights on).
16287 */
16288 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16289 {
16290 AclResult aclresult;
16291
16292 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16293 ACL_CREATE);
16294 if (aclresult != ACLCHECK_OK)
16296 get_tablespace_name(new_tablespaceoid));
16297 }
16298
16299 /*
16300 * Now that the checks are done, check if we should set either to
16301 * InvalidOid because it is our database's default tablespace.
16302 */
16303 if (orig_tablespaceoid == MyDatabaseTableSpace)
16304 orig_tablespaceoid = InvalidOid;
16305
16306 if (new_tablespaceoid == MyDatabaseTableSpace)
16307 new_tablespaceoid = InvalidOid;
16308
16309 /* no-op */
16310 if (orig_tablespaceoid == new_tablespaceoid)
16311 return new_tablespaceoid;
16312
16313 /*
16314 * Walk the list of objects in the tablespace and move them. This will
16315 * only find objects in our database, of course.
16316 */
16317 ScanKeyInit(&key[0],
16318 Anum_pg_class_reltablespace,
16319 BTEqualStrategyNumber, F_OIDEQ,
16320 ObjectIdGetDatum(orig_tablespaceoid));
16321
16322 rel = table_open(RelationRelationId, AccessShareLock);
16323 scan = table_beginscan_catalog(rel, 1, key);
16324 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
16325 {
16326 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
16327 Oid relOid = relForm->oid;
16328
16329 /*
16330 * Do not move objects in pg_catalog as part of this, if an admin
16331 * really wishes to do so, they can issue the individual ALTER
16332 * commands directly.
16333 *
16334 * Also, explicitly avoid any shared tables, temp tables, or TOAST
16335 * (TOAST will be moved with the main table).
16336 */
16337 if (IsCatalogNamespace(relForm->relnamespace) ||
16338 relForm->relisshared ||
16339 isAnyTempNamespace(relForm->relnamespace) ||
16340 IsToastNamespace(relForm->relnamespace))
16341 continue;
16342
16343 /* Only move the object type requested */
16344 if ((stmt->objtype == OBJECT_TABLE &&
16345 relForm->relkind != RELKIND_RELATION &&
16346 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
16347 (stmt->objtype == OBJECT_INDEX &&
16348 relForm->relkind != RELKIND_INDEX &&
16349 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
16350 (stmt->objtype == OBJECT_MATVIEW &&
16351 relForm->relkind != RELKIND_MATVIEW))
16352 continue;
16353
16354 /* Check if we are only moving objects owned by certain roles */
16355 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
16356 continue;
16357
16358 /*
16359 * Handle permissions-checking here since we are locking the tables
16360 * and also to avoid doing a bunch of work only to fail part-way. Note
16361 * that permissions will also be checked by AlterTableInternal().
16362 *
16363 * Caller must be considered an owner on the table to move it.
16364 */
16365 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
16367 NameStr(relForm->relname));
16368
16369 if (stmt->nowait &&
16371 ereport(ERROR,
16372 (errcode(ERRCODE_OBJECT_IN_USE),
16373 errmsg("aborting because lock on relation \"%s.%s\" is not available",
16374 get_namespace_name(relForm->relnamespace),
16375 NameStr(relForm->relname))));
16376 else
16378
16379 /* Add to our list of objects to move */
16380 relations = lappend_oid(relations, relOid);
16381 }
16382
16383 table_endscan(scan);
16385
16386 if (relations == NIL)
16388 (errcode(ERRCODE_NO_DATA_FOUND),
16389 errmsg("no matching relations in tablespace \"%s\" found",
16390 orig_tablespaceoid == InvalidOid ? "(database default)" :
16391 get_tablespace_name(orig_tablespaceoid))));
16392
16393 /* Everything is locked, loop through and move all of the relations. */
16394 foreach(l, relations)
16395 {
16396 List *cmds = NIL;
16398
16400 cmd->name = stmt->new_tablespacename;
16401
16402 cmds = lappend(cmds, cmd);
16403
16405 /* OID is set by AlterTableInternal */
16406 AlterTableInternal(lfirst_oid(l), cmds, false);
16408 }
16409
16410 return new_tablespaceoid;
16411}
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:2622
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3804
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4058
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:230
bool IsCatalogNamespace(Oid namespaceId)
Definition: catalog.c:212
#define NOTICE
Definition: elog.h:35
void EventTriggerAlterTableStart(Node *parsetree)
void EventTriggerAlterTableEnd(void)
Oid MyDatabaseTableSpace
Definition: globals.c:95
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1347
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:2086
Oid GetUserId(void)
Definition: miscinit.c:520
bool isAnyTempNamespace(Oid namespaceId)
Definition: namespace.c:3687
#define makeNode(_type_)
Definition: nodes.h:157
ObjectType get_relkind_objtype(char relkind)
@ OBJECT_MATVIEW
Definition: parsenodes.h:2335
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2354
@ OBJECT_INDEX
Definition: parsenodes.h:2332
@ OBJECT_TABLE
Definition: parsenodes.h:2353
#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:989
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition: tablecmds.c:4524
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 18213 of file tablecmds.c.

18214{
18215 Relation rel;
18216 Oid relid;
18217 Oid oldNspOid;
18218 Oid nspOid;
18219 RangeVar *newrv;
18220 ObjectAddresses *objsMoved;
18221 ObjectAddress myself;
18222
18224 stmt->missing_ok ? RVR_MISSING_OK : 0,
18226 stmt);
18227
18228 if (!OidIsValid(relid))
18229 {
18231 (errmsg("relation \"%s\" does not exist, skipping",
18232 stmt->relation->relname)));
18233 return InvalidObjectAddress;
18234 }
18235
18236 rel = relation_open(relid, NoLock);
18237
18238 oldNspOid = RelationGetNamespace(rel);
18239
18240 /* If it's an owned sequence, disallow moving it by itself. */
18241 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18242 {
18243 Oid tableId;
18244 int32 colId;
18245
18246 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18247 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18248 ereport(ERROR,
18249 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18250 errmsg("cannot move an owned sequence into another schema"),
18251 errdetail("Sequence \"%s\" is linked to table \"%s\".",
18253 get_rel_name(tableId))));
18254 }
18255
18256 /* Get and lock schema OID and check its permissions. */
18257 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18258 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18259
18260 /* common checks on switching namespaces */
18261 CheckSetNamespace(oldNspOid, nspOid);
18262
18263 objsMoved = new_object_addresses();
18264 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18265 free_object_addresses(objsMoved);
18266
18267 ObjectAddressSet(myself, RelationRelationId, relid);
18268
18269 if (oldschema)
18270 *oldschema = oldNspOid;
18271
18272 /* close rel, but keep lock until commit */
18273 relation_close(rel, NoLock);
18274
18275 return myself;
18276}
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:1203
char * get_rel_name(Oid relid)
Definition: lsyscache.c:2011
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:18284

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

18286{
18287 Relation classRel;
18288
18289 Assert(objsMoved != NULL);
18290
18291 /* OK, modify the pg_class row and pg_depend entry */
18292 classRel = table_open(RelationRelationId, RowExclusiveLock);
18293
18294 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18295 nspOid, true, objsMoved);
18296
18297 /* Fix the table's row type too, if it has one */
18298 if (OidIsValid(rel->rd_rel->reltype))
18299 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18300 false, /* isImplicitArray */
18301 false, /* ignoreDependent */
18302 false, /* errorOnTableType */
18303 objsMoved);
18304
18305 /* Fix other dependent stuff */
18306 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
18307 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
18308 objsMoved, AccessExclusiveLock);
18309 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
18310 false, objsMoved);
18311
18312 table_close(classRel, RowExclusiveLock);
18313}
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:18443
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:18398
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition: typecmds.c:4143

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

9705{
9706 List *newcons;
9707 ListCell *lcon;
9708 List *children;
9709 ListCell *child;
9711
9712 /* Guard against stack overflow due to overly deep inheritance tree. */
9714
9715 /* At top level, permission check was done in ATPrepCmd, else do it */
9716 if (recursing)
9719
9720 /*
9721 * Call AddRelationNewConstraints to do the work, making sure it works on
9722 * a copy of the Constraint so transformExpr can't modify the original. It
9723 * returns a list of cooked constraints.
9724 *
9725 * If the constraint ends up getting merged with a pre-existing one, it's
9726 * omitted from the returned list, which is what we want: we do not need
9727 * to do any validation work. That can only happen at child tables,
9728 * though, since we disallow merging at the top level.
9729 */
9730 newcons = AddRelationNewConstraints(rel, NIL,
9731 list_make1(copyObject(constr)),
9732 recursing || is_readd, /* allow_merge */
9733 !recursing, /* is_local */
9734 is_readd, /* is_internal */
9735 NULL); /* queryString not available
9736 * here */
9737
9738 /* we don't expect more than one constraint here */
9739 Assert(list_length(newcons) <= 1);
9740
9741 /* Add each to-be-validated constraint to Phase 3's queue */
9742 foreach(lcon, newcons)
9743 {
9744 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9745
9746 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9747 {
9748 NewConstraint *newcon;
9749
9750 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9751 newcon->name = ccon->name;
9752 newcon->contype = ccon->contype;
9753 newcon->qual = ccon->expr;
9754
9755 tab->constraints = lappend(tab->constraints, newcon);
9756 }
9757
9758 /* Save the actually assigned name if it was defaulted */
9759 if (constr->conname == NULL)
9760 constr->conname = ccon->name;
9761
9762 /*
9763 * If adding a not-null constraint, set the pg_attribute flag and tell
9764 * phase 3 to verify existing rows, if needed.
9765 */
9766 if (constr->contype == CONSTR_NOTNULL)
9767 set_attnotnull(wqueue, rel, ccon->attnum, lockmode);
9768
9769 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9770 }
9771
9772 /* At this point we must have a locked-down name to use */
9773 Assert(newcons == NIL || constr->conname != NULL);
9774
9775 /* Advance command counter in case same table is visited multiple times */
9777
9778 /*
9779 * If the constraint got merged with an existing constraint, we're done.
9780 * We mustn't recurse to child tables in this case, because they've
9781 * already got the constraint, and visiting them again would lead to an
9782 * incorrect value for coninhcount.
9783 */
9784 if (newcons == NIL)
9785 return address;
9786
9787 /*
9788 * If adding a NO INHERIT constraint, no need to find our children.
9789 */
9790 if (constr->is_no_inherit)
9791 return address;
9792
9793 /*
9794 * Propagate to children as appropriate. Unlike most other ALTER
9795 * routines, we have to do this one level of recursion at a time; we can't
9796 * use find_all_inheritors to do it in one pass.
9797 */
9798 children =
9800
9801 /*
9802 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9803 * constraint creation only if there are no children currently. Error out
9804 * otherwise.
9805 */
9806 if (!recurse && children != NIL)
9807 ereport(ERROR,
9808 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9809 errmsg("constraint must be added to child tables too")));
9810
9811 /*
9812 * Recurse to create the constraint on each child.
9813 */
9814 foreach(child, children)
9815 {
9816 Oid childrelid = lfirst_oid(child);
9817 Relation childrel;
9818 AlteredTableInfo *childtab;
9819
9820 /* find_inheritance_children already got lock */
9821 childrel = table_open(childrelid, NoLock);
9822 CheckAlterTableIsSafe(childrel);
9823
9824 /* Find or create work queue entry for this table */
9825 childtab = ATGetQueueEntry(wqueue, childrel);
9826
9827 /* Recurse to this child */
9828 ATAddCheckNNConstraint(wqueue, childtab, childrel,
9829 constr, recurse, true, is_readd, lockmode);
9830
9831 table_close(childrel, NoLock);
9832 }
9833
9834 return address;
9835}
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:2783
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:2822
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 void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, LOCKMODE lockmode)
Definition: tablecmds.c:7739
static ObjectAddress ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9702
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition: tablecmds.c:6642
#define ATT_FOREIGN_TABLE
Definition: tablecmds.c:333
#define ATT_PARTITIONED_TABLE
Definition: tablecmds.c:336

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

9856{
9857 Relation pkrel;
9858 int16 pkattnum[INDEX_MAX_KEYS] = {0};
9859 int16 fkattnum[INDEX_MAX_KEYS] = {0};
9860 Oid pktypoid[INDEX_MAX_KEYS] = {0};
9861 Oid fktypoid[INDEX_MAX_KEYS] = {0};
9862 Oid pkcolloid[INDEX_MAX_KEYS] = {0};
9863 Oid fkcolloid[INDEX_MAX_KEYS] = {0};
9864 Oid opclasses[INDEX_MAX_KEYS] = {0};
9865 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9866 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9867 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9868 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9869 bool with_period;
9870 bool pk_has_without_overlaps;
9871 int i;
9872 int numfks,
9873 numpks,
9874 numfkdelsetcols;
9875 Oid indexOid;
9876 bool old_check_ok;
9877 ObjectAddress address;
9878 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9879
9880 /*
9881 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9882 * delete rows out from under us.
9883 */
9884 if (OidIsValid(fkconstraint->old_pktable_oid))
9885 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9886 else
9887 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9888
9889 /*
9890 * Validity checks (permission checks wait till we have the column
9891 * numbers)
9892 */
9893 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9894 ereport(ERROR,
9895 errcode(ERRCODE_WRONG_OBJECT_TYPE),
9896 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9898 RelationGetRelationName(pkrel)));
9899
9900 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9901 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9902 ereport(ERROR,
9903 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9904 errmsg("referenced relation \"%s\" is not a table",
9905 RelationGetRelationName(pkrel))));
9906
9908 ereport(ERROR,
9909 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9910 errmsg("permission denied: \"%s\" is a system catalog",
9911 RelationGetRelationName(pkrel))));
9912
9913 /*
9914 * References from permanent or unlogged tables to temp tables, and from
9915 * permanent tables to unlogged tables, are disallowed because the
9916 * referenced data can vanish out from under us. References from temp
9917 * tables to any other table type are also disallowed, because other
9918 * backends might need to run the RI triggers on the perm table, but they
9919 * can't reliably see tuples in the local buffers of other backends.
9920 */
9921 switch (rel->rd_rel->relpersistence)
9922 {
9923 case RELPERSISTENCE_PERMANENT:
9924 if (!RelationIsPermanent(pkrel))
9925 ereport(ERROR,
9926 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9927 errmsg("constraints on permanent tables may reference only permanent tables")));
9928 break;
9929 case RELPERSISTENCE_UNLOGGED:
9930 if (!RelationIsPermanent(pkrel)
9931 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9932 ereport(ERROR,
9933 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9934 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9935 break;
9936 case RELPERSISTENCE_TEMP:
9937 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9938 ereport(ERROR,
9939 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9940 errmsg("constraints on temporary tables may reference only temporary tables")));
9941 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9942 ereport(ERROR,
9943 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9944 errmsg("constraints on temporary tables must involve temporary tables of this session")));
9945 break;
9946 }
9947
9948 /*
9949 * Look up the referencing attributes to make sure they exist, and record
9950 * their attnums and type and collation OIDs.
9951 */
9953 fkconstraint->fk_attrs,
9954 fkattnum, fktypoid, fkcolloid);
9955 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
9956 if (with_period && !fkconstraint->fk_with_period)
9957 ereport(ERROR,
9958 errcode(ERRCODE_INVALID_FOREIGN_KEY),
9959 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9960
9961 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9962 fkconstraint->fk_del_set_cols,
9963 fkdelsetcols, NULL, NULL);
9964 validateFkOnDeleteSetColumns(numfks, fkattnum,
9965 numfkdelsetcols, fkdelsetcols,
9966 fkconstraint->fk_del_set_cols);
9967
9968 /*
9969 * If the attribute list for the referenced table was omitted, lookup the
9970 * definition of the primary key and use it. Otherwise, validate the
9971 * supplied attribute list. In either case, discover the index OID and
9972 * index opclasses, and the attnums and type and collation OIDs of the
9973 * attributes.
9974 */
9975 if (fkconstraint->pk_attrs == NIL)
9976 {
9977 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
9978 &fkconstraint->pk_attrs,
9979 pkattnum, pktypoid, pkcolloid,
9980 opclasses, &pk_has_without_overlaps);
9981
9982 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
9983 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
9984 ereport(ERROR,
9985 errcode(ERRCODE_INVALID_FOREIGN_KEY),
9986 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9987 }
9988 else
9989 {
9991 fkconstraint->pk_attrs,
9992 pkattnum, pktypoid, pkcolloid);
9993
9994 /* Since we got pk_attrs, one should be a period. */
9995 if (with_period && !fkconstraint->pk_with_period)
9996 ereport(ERROR,
9997 errcode(ERRCODE_INVALID_FOREIGN_KEY),
9998 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
9999
10000 /* Look for an index matching the column list */
10001 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10002 with_period, opclasses, &pk_has_without_overlaps);
10003 }
10004
10005 /*
10006 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10007 * must use PERIOD.
10008 */
10009 if (pk_has_without_overlaps && !with_period)
10010 ereport(ERROR,
10011 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10012 errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
10013
10014 /*
10015 * Now we can check permissions.
10016 */
10017 checkFkeyPermissions(pkrel, pkattnum, numpks);
10018
10019 /*
10020 * Check some things for generated columns.
10021 */
10022 for (i = 0; i < numfks; i++)
10023 {
10024 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10025
10026 if (attgenerated)
10027 {
10028 /*
10029 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10030 */
10031 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10032 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10033 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10034 ereport(ERROR,
10035 (errcode(ERRCODE_SYNTAX_ERROR),
10036 errmsg("invalid %s action for foreign key constraint containing generated column",
10037 "ON UPDATE")));
10038 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10040 ereport(ERROR,
10041 (errcode(ERRCODE_SYNTAX_ERROR),
10042 errmsg("invalid %s action for foreign key constraint containing generated column",
10043 "ON DELETE")));
10044 }
10045
10046 /*
10047 * FKs on virtual columns are not supported. This would require
10048 * various additional support in ri_triggers.c, including special
10049 * handling in ri_NullCheck(), ri_KeysEqual(),
10050 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10051 * as NULL there). Also not really practical as long as you can't
10052 * index virtual columns.
10053 */
10054 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10055 ereport(ERROR,
10056 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10057 errmsg("foreign key constraints on virtual generated columns are not supported")));
10058 }
10059
10060 /*
10061 * Some actions are currently unsupported for foreign keys using PERIOD.
10062 */
10063 if (fkconstraint->fk_with_period)
10064 {
10065 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10066 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10067 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10069 ereport(ERROR,
10070 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10071 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10072 "ON UPDATE"));
10073
10074 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10075 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10076 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10078 ereport(ERROR,
10079 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10080 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10081 "ON DELETE"));
10082 }
10083
10084 /*
10085 * Look up the equality operators to use in the constraint.
10086 *
10087 * Note that we have to be careful about the difference between the actual
10088 * PK column type and the opclass' declared input type, which might be
10089 * only binary-compatible with it. The declared opcintype is the right
10090 * thing to probe pg_amop with.
10091 */
10092 if (numfks != numpks)
10093 ereport(ERROR,
10094 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10095 errmsg("number of referencing and referenced columns for foreign key disagree")));
10096
10097 /*
10098 * On the strength of a previous constraint, we might avoid scanning
10099 * tables to validate this one. See below.
10100 */
10101 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10102 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10103
10104 for (i = 0; i < numpks; i++)
10105 {
10106 Oid pktype = pktypoid[i];
10107 Oid fktype = fktypoid[i];
10108 Oid fktyped;
10109 Oid pkcoll = pkcolloid[i];
10110 Oid fkcoll = fkcolloid[i];
10111 HeapTuple cla_ht;
10112 Form_pg_opclass cla_tup;
10113 Oid amid;
10114 Oid opfamily;
10115 Oid opcintype;
10116 bool for_overlaps;
10117 CompareType cmptype;
10118 Oid pfeqop;
10119 Oid ppeqop;
10120 Oid ffeqop;
10121 int16 eqstrategy;
10122 Oid pfeqop_right;
10123
10124 /* We need several fields out of the pg_opclass entry */
10125 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10126 if (!HeapTupleIsValid(cla_ht))
10127 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10128 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10129 amid = cla_tup->opcmethod;
10130 opfamily = cla_tup->opcfamily;
10131 opcintype = cla_tup->opcintype;
10132 ReleaseSysCache(cla_ht);
10133
10134 /*
10135 * Get strategy number from index AM.
10136 *
10137 * For a normal foreign-key constraint, this should not fail, since we
10138 * already checked that the index is unique and should therefore have
10139 * appropriate equal operators. For a period foreign key, this could
10140 * fail if we selected a non-matching exclusion constraint earlier.
10141 * (XXX Maybe we should do these lookups earlier so we don't end up
10142 * doing that.)
10143 */
10144 for_overlaps = with_period && i == numpks - 1;
10145 cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10146 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10147 if (eqstrategy == InvalidStrategy)
10148 ereport(ERROR,
10149 errcode(ERRCODE_UNDEFINED_OBJECT),
10150 for_overlaps
10151 ? errmsg("could not identify an overlaps operator for foreign key")
10152 : errmsg("could not identify an equality operator for foreign key"),
10153 errdetail("Could not translate compare type %d for operator family \"%s\", input type %s, access method \"%s\".",
10154 cmptype, get_opfamily_name(opfamily, false), format_type_be(opcintype), get_am_name(amid)));
10155
10156 /*
10157 * There had better be a primary equality operator for the index.
10158 * We'll use it for PK = PK comparisons.
10159 */
10160 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10161 eqstrategy);
10162
10163 if (!OidIsValid(ppeqop))
10164 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10165 eqstrategy, opcintype, opcintype, opfamily);
10166
10167 /*
10168 * Are there equality operators that take exactly the FK type? Assume
10169 * we should look through any domain here.
10170 */
10171 fktyped = getBaseType(fktype);
10172
10173 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10174 eqstrategy);
10175 if (OidIsValid(pfeqop))
10176 {
10177 pfeqop_right = fktyped;
10178 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10179 eqstrategy);
10180 }
10181 else
10182 {
10183 /* keep compiler quiet */
10184 pfeqop_right = InvalidOid;
10185 ffeqop = InvalidOid;
10186 }
10187
10188 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10189 {
10190 /*
10191 * Otherwise, look for an implicit cast from the FK type to the
10192 * opcintype, and if found, use the primary equality operator.
10193 * This is a bit tricky because opcintype might be a polymorphic
10194 * type such as ANYARRAY or ANYENUM; so what we have to test is
10195 * whether the two actual column types can be concurrently cast to
10196 * that type. (Otherwise, we'd fail to reject combinations such
10197 * as int[] and point[].)
10198 */
10199 Oid input_typeids[2];
10200 Oid target_typeids[2];
10201
10202 input_typeids[0] = pktype;
10203 input_typeids[1] = fktype;
10204 target_typeids[0] = opcintype;
10205 target_typeids[1] = opcintype;
10206 if (can_coerce_type(2, input_typeids, target_typeids,
10208 {
10209 pfeqop = ffeqop = ppeqop;
10210 pfeqop_right = opcintype;
10211 }
10212 }
10213
10214 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10215 ereport(ERROR,
10216 (errcode(ERRCODE_DATATYPE_MISMATCH),
10217 errmsg("foreign key constraint \"%s\" cannot be implemented",
10218 fkconstraint->conname),
10219 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10220 "are of incompatible types: %s and %s.",
10221 strVal(list_nth(fkconstraint->fk_attrs, i)),
10222 strVal(list_nth(fkconstraint->pk_attrs, i)),
10223 format_type_be(fktype),
10224 format_type_be(pktype))));
10225
10226 /*
10227 * This shouldn't be possible, but better check to make sure we have a
10228 * consistent state for the check below.
10229 */
10230 if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10231 elog(ERROR, "key columns are not both collatable");
10232
10233 if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10234 {
10235 bool pkcolldet;
10236 bool fkcolldet;
10237
10238 pkcolldet = get_collation_isdeterministic(pkcoll);
10239 fkcolldet = get_collation_isdeterministic(fkcoll);
10240
10241 /*
10242 * SQL requires that both collations are the same. This is
10243 * because we need a consistent notion of equality on both
10244 * columns. We relax this by allowing different collations if
10245 * they are both deterministic. (This is also for backward
10246 * compatibility, because PostgreSQL has always allowed this.)
10247 */
10248 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10249 ereport(ERROR,
10250 (errcode(ERRCODE_COLLATION_MISMATCH),
10251 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10252 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10253 "have incompatible collations: \"%s\" and \"%s\". "
10254 "If either collation is nondeterministic, then both collations have to be the same.",
10255 strVal(list_nth(fkconstraint->fk_attrs, i)),
10256 strVal(list_nth(fkconstraint->pk_attrs, i)),
10257 get_collation_name(fkcoll),
10258 get_collation_name(pkcoll))));
10259 }
10260
10261 if (old_check_ok)
10262 {
10263 /*
10264 * When a pfeqop changes, revalidate the constraint. We could
10265 * permit intra-opfamily changes, but that adds subtle complexity
10266 * without any concrete benefit for core types. We need not
10267 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10268 */
10269 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10270 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10271 old_pfeqop_item);
10272 }
10273 if (old_check_ok)
10274 {
10275 Oid old_fktype;
10276 Oid new_fktype;
10277 CoercionPathType old_pathtype;
10278 CoercionPathType new_pathtype;
10279 Oid old_castfunc;
10280 Oid new_castfunc;
10281 Oid old_fkcoll;
10282 Oid new_fkcoll;
10284 fkattnum[i] - 1);
10285
10286 /*
10287 * Identify coercion pathways from each of the old and new FK-side
10288 * column types to the right (foreign) operand type of the pfeqop.
10289 * We may assume that pg_constraint.conkey is not changing.
10290 */
10291 old_fktype = attr->atttypid;
10292 new_fktype = fktype;
10293 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10294 &old_castfunc);
10295 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10296 &new_castfunc);
10297
10298 old_fkcoll = attr->attcollation;
10299 new_fkcoll = fkcoll;
10300
10301 /*
10302 * Upon a change to the cast from the FK column to its pfeqop
10303 * operand, revalidate the constraint. For this evaluation, a
10304 * binary coercion cast is equivalent to no cast at all. While
10305 * type implementors should design implicit casts with an eye
10306 * toward consistency of operations like equality, we cannot
10307 * assume here that they have done so.
10308 *
10309 * A function with a polymorphic argument could change behavior
10310 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10311 * when the cast destination is polymorphic, we only avoid
10312 * revalidation if the input type has not changed at all. Given
10313 * just the core data types and operator classes, this requirement
10314 * prevents no would-be optimizations.
10315 *
10316 * If the cast converts from a base type to a domain thereon, then
10317 * that domain type must be the opcintype of the unique index.
10318 * Necessarily, the primary key column must then be of the domain
10319 * type. Since the constraint was previously valid, all values on
10320 * the foreign side necessarily exist on the primary side and in
10321 * turn conform to the domain. Consequently, we need not treat
10322 * domains specially here.
10323 *
10324 * If the collation changes, revalidation is required, unless both
10325 * collations are deterministic, because those share the same
10326 * notion of equality (because texteq reduces to bitwise
10327 * equality).
10328 *
10329 * We need not directly consider the PK type. It's necessarily
10330 * binary coercible to the opcintype of the unique index column,
10331 * and ri_triggers.c will only deal with PK datums in terms of
10332 * that opcintype. Changing the opcintype also changes pfeqop.
10333 */
10334 old_check_ok = (new_pathtype == old_pathtype &&
10335 new_castfunc == old_castfunc &&
10336 (!IsPolymorphicType(pfeqop_right) ||
10337 new_fktype == old_fktype) &&
10338 (new_fkcoll == old_fkcoll ||
10339 (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10340 }
10341
10342 pfeqoperators[i] = pfeqop;
10343 ppeqoperators[i] = ppeqop;
10344 ffeqoperators[i] = ffeqop;
10345 }
10346
10347 /*
10348 * For FKs with PERIOD we need additional operators to check whether the
10349 * referencing row's range is contained by the aggregated ranges of the
10350 * referenced row(s). For rangetypes and multirangetypes this is
10351 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10352 * support for now. FKs will look these up at "runtime", but we should
10353 * make sure the lookup works here, even if we don't use the values.
10354 */
10355 if (with_period)
10356 {
10357 Oid periodoperoid;
10358 Oid aggedperiodoperoid;
10359 Oid intersectoperoid;
10360
10361 FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10362 &intersectoperoid);
10363 }
10364
10365 /* First, create the constraint catalog entry itself. */
10367 fkconstraint->conname, fkconstraint, rel, pkrel,
10368 indexOid,
10369 InvalidOid, /* no parent constraint */
10370 numfks,
10371 pkattnum,
10372 fkattnum,
10373 pfeqoperators,
10374 ppeqoperators,
10375 ffeqoperators,
10376 numfkdelsetcols,
10377 fkdelsetcols,
10378 false,
10379 with_period);
10380
10381 /* Next process the action triggers at the referenced side and recurse */
10382 addFkRecurseReferenced(fkconstraint, rel, pkrel,
10383 indexOid,
10384 address.objectId,
10385 numfks,
10386 pkattnum,
10387 fkattnum,
10388 pfeqoperators,
10389 ppeqoperators,
10390 ffeqoperators,
10391 numfkdelsetcols,
10392 fkdelsetcols,
10393 old_check_ok,
10395 with_period);
10396
10397 /* Lastly create the check triggers at the referencing side and recurse */
10398 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10399 indexOid,
10400 address.objectId,
10401 numfks,
10402 pkattnum,
10403 fkattnum,
10404 pfeqoperators,
10405 ppeqoperators,
10406 ffeqoperators,
10407 numfkdelsetcols,
10408 fkdelsetcols,
10409 old_check_ok,
10410 lockmode,
10412 with_period);
10413
10414 /*
10415 * Done. Close pk table, but keep lock until we've committed.
10416 */
10417 table_close(pkrel, NoLock);
10418
10419 return address;
10420}
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition: amapi.c:143
char * get_am_name(Oid amOid)
Definition: amcmds.c:192
bool IsSystemRelation(Relation relation)
Definition: catalog.c:73
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:129
char * get_collation_name(Oid colloid)
Definition: lsyscache.c:1070
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:1089
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition: lsyscache.c:1336
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2604
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:2802
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2805
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2803
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2804
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:200
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:625
#define InvalidStrategy
Definition: stratnum.h:24
TupleDesc oldDesc
Definition: tablecmds.c:173
List * pk_attrs
Definition: parsenodes.h:2848
List * fk_del_set_cols
Definition: parsenodes.h:2854
Oid old_pktable_oid
Definition: parsenodes.h:2856
List * old_conpfeqop
Definition: parsenodes.h:2855
bool pk_with_period
Definition: parsenodes.h:2850
RangeVar * pktable
Definition: parsenodes.h:2846
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 CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
Definition: tablecmds.c:12927
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:12776
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
Definition: tablecmds.c:12956
static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, const int16 *fksetcolsattnums, List *fksetcols)
Definition: tablecmds.c:10428
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:12673
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
Definition: tablecmds.c:12618
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:154
#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 6764 of file tablecmds.c.

6765{
6766 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6767 {
6768 List *inh;
6769 ListCell *cell;
6770
6771 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6772 /* first element is the parent rel; must ignore it */
6773 for_each_from(cell, inh, 1)
6774 {
6775 Relation childrel;
6776
6777 /* find_all_inheritors already got lock */
6778 childrel = table_open(lfirst_oid(cell), NoLock);
6779 CheckAlterTableIsSafe(childrel);
6780 table_close(childrel, NoLock);
6781 }
6782 list_free(inh);
6783 }
6784}
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 13969 of file tablecmds.c.

13970{
13971 Assert(expr != NULL);
13972
13973 for (;;)
13974 {
13975 /* only one varno, so no need to check that */
13976 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13977 return false;
13978 else if (IsA(expr, RelabelType))
13979 expr = (Node *) ((RelabelType *) expr)->arg;
13980 else if (IsA(expr, CoerceToDomain))
13981 {
13982 CoerceToDomain *d = (CoerceToDomain *) expr;
13983
13985 return true;
13986 expr = (Node *) d->arg;
13987 }
13988 else if (IsA(expr, FuncExpr))
13989 {
13990 FuncExpr *f = (FuncExpr *) expr;
13991
13992 switch (f->funcid)
13993 {
13994 case F_TIMESTAMPTZ_TIMESTAMP:
13995 case F_TIMESTAMP_TIMESTAMPTZ:
13997 return true;
13998 else
13999 expr = linitial(f->args);
14000 break;
14001 default:
14002 return true;
14003 }
14004 }
14005 else
14006 return true;
14007 }
14008}
bool TimestampTimestampTzRequiresRewrite(void)
Definition: timestamp.c:6387
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:1487

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

4834{
4835 List *wqueue = NIL;
4836 ListCell *lcmd;
4837
4838 /* Phase 1: preliminary examination of commands, create work queue */
4839 foreach(lcmd, cmds)
4840 {
4841 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4842
4843 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4844 }
4845
4846 /* Close the relation, but keep lock until commit */
4847 relation_close(rel, NoLock);
4848
4849 /* Phase 2: update system catalogs */
4850 ATRewriteCatalogs(&wqueue, lockmode, context);
4851
4852 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4853 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4854}
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5263
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4866
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5799

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

21232{
21233 List *constraints;
21234 ListCell *cell;
21235
21236 constraints = GetParentedForeignKeyRefs(partition);
21237
21238 foreach(cell, constraints)
21239 {
21240 Oid constrOid = lfirst_oid(cell);
21241 HeapTuple tuple;
21242 Form_pg_constraint constrForm;
21243 Relation rel;
21244 Trigger trig = {0};
21245
21246 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21247 if (!HeapTupleIsValid(tuple))
21248 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21249 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21250
21251 Assert(OidIsValid(constrForm->conparentid));
21252 Assert(constrForm->confrelid == RelationGetRelid(partition));
21253
21254 /* prevent data changes into the referencing table until commit */
21255 rel = table_open(constrForm->conrelid, ShareLock);
21256
21257 trig.tgoid = InvalidOid;
21258 trig.tgname = NameStr(constrForm->conname);
21260 trig.tgisinternal = true;
21261 trig.tgconstrrelid = RelationGetRelid(partition);
21262 trig.tgconstrindid = constrForm->conindid;
21263 trig.tgconstraint = constrForm->oid;
21264 trig.tgdeferrable = false;
21265 trig.tginitdeferred = false;
21266 /* we needn't fill in remaining fields */
21267
21268 RI_PartitionRemove_Check(&trig, rel, partition);
21269
21270 ReleaseSysCache(tuple);
21271
21272 table_close(rel, NoLock);
21273 }
21274}
#define ShareLock
Definition: lockdefs.h:40
FormData_pg_constraint * Form_pg_constraint
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:21178
#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 18726 of file tablecmds.c.

18728{
18729 ListCell *cur_item;
18730
18731 foreach(cur_item, on_commits)
18732 {
18733 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18734
18735 if (!isCommit && oc->creating_subid == mySubid)
18736 {
18737 /* cur_item must be removed */
18739 pfree(oc);
18740 }
18741 else
18742 {
18743 /* cur_item must be preserved */
18744 if (oc->creating_subid == mySubid)
18745 oc->creating_subid = parentSubid;
18746 if (oc->deleting_subid == mySubid)
18747 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18748 }
18749 }
18750}
#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 18694 of file tablecmds.c.

18695{
18696 ListCell *cur_item;
18697
18698 foreach(cur_item, on_commits)
18699 {
18700 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18701
18702 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18704 {
18705 /* cur_item must be removed */
18707 pfree(oc);
18708 }
18709 else
18710 {
18711 /* cur_item must be preserved */
18714 }
18715 }
18716}

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

7124{
7125 Oid myrelid = RelationGetRelid(rel);
7126 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7127 bool if_not_exists = (*cmd)->missing_ok;
7128 Relation pgclass,
7129 attrdesc;
7130 HeapTuple reltup;
7131 Form_pg_class relform;
7132 Form_pg_attribute attribute;
7133 int newattnum;
7134 char relkind;
7135 Expr *defval;
7136 List *children;
7137 ListCell *child;
7138 AlterTableCmd *childcmd;
7139 ObjectAddress address;
7140 TupleDesc tupdesc;
7141
7142 /* since this function recurses, it could be driven to stack overflow */
7144
7145 /* At top level, permission check was done in ATPrepCmd, else do it */
7146 if (recursing)
7147 ATSimplePermissions((*cmd)->subtype, rel,
7149
7150 if (rel->rd_rel->relispartition && !recursing)
7151 ereport(ERROR,
7152 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7153 errmsg("cannot add column to a partition")));
7154
7155 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7156
7157 /*
7158 * Are we adding the column to a recursion child? If so, check whether to
7159 * merge with an existing definition for the column. If we do merge, we
7160 * must not recurse. Children will already have the column, and recursing
7161 * into them would mess up attinhcount.
7162 */
7163 if (colDef->inhcount > 0)
7164 {
7165 HeapTuple tuple;
7166
7167 /* Does child already have a column by this name? */
7168 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7169 if (HeapTupleIsValid(tuple))
7170 {
7171 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7172 Oid ctypeId;
7173 int32 ctypmod;
7174 Oid ccollid;
7175
7176 /* Child column must match on type, typmod, and collation */
7177 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7178 if (ctypeId != childatt->atttypid ||
7179 ctypmod != childatt->atttypmod)
7180 ereport(ERROR,
7181 (errcode(ERRCODE_DATATYPE_MISMATCH),
7182 errmsg("child table \"%s\" has different type for column \"%s\"",
7183 RelationGetRelationName(rel), colDef->colname)));
7184 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7185 if (ccollid != childatt->attcollation)
7186 ereport(ERROR,
7187 (errcode(ERRCODE_COLLATION_MISMATCH),
7188 errmsg("child table \"%s\" has different collation for column \"%s\"",
7189 RelationGetRelationName(rel), colDef->colname),
7190 errdetail("\"%s\" versus \"%s\"",
7191 get_collation_name(ccollid),
7192 get_collation_name(childatt->attcollation))));
7193
7194 /* Bump the existing child att's inhcount */
7195 if (pg_add_s16_overflow(childatt->attinhcount, 1,
7196 &childatt->attinhcount))
7197 ereport(ERROR,
7198 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7199 errmsg("too many inheritance parents"));
7200 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7201
7202 heap_freetuple(tuple);
7203
7204 /* Inform the user about the merge */
7206 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7207 colDef->colname, RelationGetRelationName(rel))));
7208
7209 table_close(attrdesc, RowExclusiveLock);
7210
7211 /* Make the child column change visible */
7213
7214 return InvalidObjectAddress;
7215 }
7216 }
7217
7218 /* skip if the name already exists and if_not_exists is true */
7219 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7220 {
7221 table_close(attrdesc, RowExclusiveLock);
7222 return InvalidObjectAddress;
7223 }
7224
7225 /*
7226 * Okay, we need to add the column, so go ahead and do parse
7227 * transformation. This can result in queueing up, or even immediately
7228 * executing, subsidiary operations (such as creation of unique indexes);
7229 * so we mustn't do it until we have made the if_not_exists check.
7230 *
7231 * When recursing, the command was already transformed and we needn't do
7232 * so again. Also, if context isn't given we can't transform. (That
7233 * currently happens only for AT_AddColumnToView; we expect that view.c
7234 * passed us a ColumnDef that doesn't need work.)
7235 */
7236 if (context != NULL && !recursing)
7237 {
7238 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7239 cur_pass, context);
7240 Assert(*cmd != NULL);
7241 colDef = castNode(ColumnDef, (*cmd)->def);
7242 }
7243
7244 /*
7245 * Regular inheritance children are independent enough not to inherit the
7246 * identity column from parent hence cannot recursively add identity
7247 * column if the table has inheritance children.
7248 *
7249 * Partitions, on the other hand, are integral part of a partitioned table
7250 * and inherit identity column. Hence propagate identity column down the
7251 * partition hierarchy.
7252 */
7253 if (colDef->identity &&
7254 recurse &&
7255 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7257 ereport(ERROR,
7258 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7259 errmsg("cannot recursively add identity column to table that has child tables")));
7260
7261 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7262
7263 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7264 if (!HeapTupleIsValid(reltup))
7265 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7266 relform = (Form_pg_class) GETSTRUCT(reltup);
7267 relkind = relform->relkind;
7268
7269 /* Determine the new attribute's number */
7270 newattnum = relform->relnatts + 1;
7271 if (newattnum > MaxHeapAttributeNumber)
7272 ereport(ERROR,
7273 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7274 errmsg("tables can have at most %d columns",
7276
7277 /*
7278 * Construct new attribute's pg_attribute entry.
7279 */
7280 tupdesc = BuildDescForRelation(list_make1(colDef));
7281
7282 attribute = TupleDescAttr(tupdesc, 0);
7283
7284 /* Fix up attribute number */
7285 attribute->attnum = newattnum;
7286
7287 /* make sure datatype is legal for a column */
7288 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7289 list_make1_oid(rel->rd_rel->reltype),
7290 0);
7291
7292 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7293
7294 table_close(attrdesc, RowExclusiveLock);
7295
7296 /*
7297 * Update pg_class tuple as appropriate
7298 */
7299 relform->relnatts = newattnum;
7300
7301 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7302
7303 heap_freetuple(reltup);
7304
7305 /* Post creation hook for new attribute */
7306 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7307
7308 table_close(pgclass, RowExclusiveLock);
7309
7310 /* Make the attribute's catalog entry visible */
7312
7313 /*
7314 * Store the DEFAULT, if any, in the catalogs
7315 */
7316 if (colDef->raw_default)
7317 {
7318 RawColumnDefault *rawEnt;
7319
7320 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7321 rawEnt->attnum = attribute->attnum;
7322 rawEnt->raw_default = copyObject(colDef->raw_default);
7323 rawEnt->generated = colDef->generated;
7324
7325 /*
7326 * This function is intended for CREATE TABLE, so it processes a
7327 * _list_ of defaults, but we just do one.
7328 */
7330 false, true, false, NULL);
7331
7332 /* Make the additional catalog changes visible */
7334 }
7335
7336 /*
7337 * Tell Phase 3 to fill in the default expression, if there is one.
7338 *
7339 * If there is no default, Phase 3 doesn't have to do anything, because
7340 * that effectively means that the default is NULL. The heap tuple access
7341 * routines always check for attnum > # of attributes in tuple, and return
7342 * NULL if so, so without any modification of the tuple data we will get
7343 * the effect of NULL values in the new column.
7344 *
7345 * An exception occurs when the new column is of a domain type: the domain
7346 * might have a not-null constraint, or a check constraint that indirectly
7347 * rejects nulls. If there are any domain constraints then we construct
7348 * an explicit NULL default value that will be passed through
7349 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7350 * rewriting the table which we really wouldn't have to do; but we do it
7351 * to preserve the historical behavior that such a failure will be raised
7352 * only if the table currently contains some rows.)
7353 *
7354 * Note: we use build_column_default, and not just the cooked default
7355 * returned by AddRelationNewConstraints, so that the right thing happens
7356 * when a datatype's default applies.
7357 *
7358 * Note: it might seem that this should happen at the end of Phase 2, so
7359 * that the effects of subsequent subcommands can be taken into account.
7360 * It's intentional that we do it now, though. The new column should be
7361 * filled according to what is said in the ADD COLUMN subcommand, so that
7362 * the effects are the same as if this subcommand had been run by itself
7363 * and the later subcommands had been issued in new ALTER TABLE commands.
7364 *
7365 * We can skip this entirely for relations without storage, since Phase 3
7366 * is certainly not going to touch them.
7367 */
7368 if (RELKIND_HAS_STORAGE(relkind))
7369 {
7370 bool has_domain_constraints;
7371 bool has_missing = false;
7372
7373 /*
7374 * For an identity column, we can't use build_column_default(),
7375 * because the sequence ownership isn't set yet. So do it manually.
7376 */
7377 if (colDef->identity)
7378 {
7380
7381 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7382 nve->typeId = attribute->atttypid;
7383
7384 defval = (Expr *) nve;
7385 }
7386 else
7387 defval = (Expr *) build_column_default(rel, attribute->attnum);
7388
7389 /* Build CoerceToDomain(NULL) expression if needed */
7390 has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7391 if (!defval && has_domain_constraints)
7392 {
7393 Oid baseTypeId;
7394 int32 baseTypeMod;
7395 Oid baseTypeColl;
7396
7397 baseTypeMod = attribute->atttypmod;
7398 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7399 baseTypeColl = get_typcollation(baseTypeId);
7400 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7401 defval = (Expr *) coerce_to_target_type(NULL,
7402 (Node *) defval,
7403 baseTypeId,
7404 attribute->atttypid,
7405 attribute->atttypmod,
7408 -1);
7409 if (defval == NULL) /* should not happen */
7410 elog(ERROR, "failed to coerce base type to domain");
7411 }
7412
7413 if (defval)
7414 {
7416
7417 /* Prepare defval for execution, either here or in Phase 3 */
7418 defval = expression_planner(defval);
7419
7420 /* Add the new default to the newvals list */
7422 newval->attnum = attribute->attnum;
7423 newval->expr = defval;
7424 newval->is_generated = (colDef->generated != '\0');
7425
7426 tab->newvals = lappend(tab->newvals, newval);
7427
7428 /*
7429 * Attempt to skip a complete table rewrite by storing the
7430 * specified DEFAULT value outside of the heap. This is only
7431 * allowed for plain relations and non-generated columns, and the
7432 * default expression can't be volatile (stable is OK). Note that
7433 * contain_volatile_functions deems CoerceToDomain immutable, but
7434 * here we consider that coercion to a domain with constraints is
7435 * volatile; else it might fail even when the table is empty.
7436 */
7437 if (rel->rd_rel->relkind == RELKIND_RELATION &&
7438 !colDef->generated &&
7439 !has_domain_constraints &&
7440 !contain_volatile_functions((Node *) defval))
7441 {
7442 EState *estate;
7443 ExprState *exprState;
7444 Datum missingval;
7445 bool missingIsNull;
7446
7447 /* Evaluate the default expression */
7448 estate = CreateExecutorState();
7449 exprState = ExecPrepareExpr(defval, estate);
7450 missingval = ExecEvalExpr(exprState,
7451 GetPerTupleExprContext(estate),
7452 &missingIsNull);
7453 /* If it turns out NULL, nothing to do; else store it */
7454 if (!missingIsNull)
7455 {
7456 StoreAttrMissingVal(rel, attribute->attnum, missingval);
7457 has_missing = true;
7458 }
7459 FreeExecutorState(estate);
7460 }
7461 else
7462 {
7463 /*
7464 * Failed to use missing mode. We have to do a table rewrite
7465 * to install the value --- unless it's a virtual generated
7466 * column.
7467 */
7468 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7470 }
7471 }
7472
7473 if (!has_missing)
7474 {
7475 /*
7476 * If the new column is NOT NULL, and there is no missing value,
7477 * tell Phase 3 it needs to check for NULLs.
7478 */
7479 tab->verify_new_notnull |= colDef->is_not_null;
7480 }
7481 }
7482
7483 /*
7484 * Add needed dependency entries for the new column.
7485 */
7486 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7487 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7488
7489 /*
7490 * Propagate to children as appropriate. Unlike most other ALTER
7491 * routines, we have to do this one level of recursion at a time; we can't
7492 * use find_all_inheritors to do it in one pass.
7493 */
7494 children =
7496
7497 /*
7498 * If we are told not to recurse, there had better not be any child
7499 * tables; else the addition would put them out of step.
7500 */
7501 if (children && !recurse)
7502 ereport(ERROR,
7503 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7504 errmsg("column must be added to child tables too")));
7505
7506 /* Children should see column as singly inherited */
7507 if (!recursing)
7508 {
7509 childcmd = copyObject(*cmd);
7510 colDef = castNode(ColumnDef, childcmd->def);
7511 colDef->inhcount = 1;
7512 colDef->is_local = false;
7513 }
7514 else
7515 childcmd = *cmd; /* no need to copy again */
7516
7517 foreach(child, children)
7518 {
7519 Oid childrelid = lfirst_oid(child);
7520 Relation childrel;
7521 AlteredTableInfo *childtab;
7522
7523 /* find_inheritance_children already got lock */
7524 childrel = table_open(childrelid, NoLock);
7525 CheckAlterTableIsSafe(childrel);
7526
7527 /* Find or create work queue entry for this table */
7528 childtab = ATGetQueueEntry(wqueue, childrel);
7529
7530 /* Recurse to child; return value is ignored */
7531 ATExecAddColumn(wqueue, childtab, childrel,
7532 &childcmd, recurse, true,
7533 lockmode, cur_pass, context);
7534
7535 table_close(childrel, NoLock);
7536 }
7537
7538 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7539 return address;
7540}
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:537
#define AT_REWRITE_DEFAULT_VAL
Definition: event_trigger.h:41
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:765
void FreeExecutorState(EState *estate)
Definition: execUtils.c:193
EState * CreateExecutorState(void)
Definition: execUtils.c:88
#define GetPerTupleExprContext(estate)
Definition: executor.h:637
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:374
#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:3139
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition: lsyscache.c:2621
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:178
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:173
#define ObjectAddressSubSet(addr, class_id, object_id, object_sub_id)
Definition: objectaddress.h:33
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:78
void typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName, Oid *typeid_p, int32 *typmod_p)
Definition: parse_type.c:310
Oid GetColumnDefCollation(ParseState *pstate, const ColumnDef *coldef, Oid typeOid)
Definition: parse_type.c:540
#define list_make1_oid(x1)
Definition: pg_list.h:242
Expr * expression_planner(Expr *expr)
Definition: planner.c:6645
uintptr_t Datum
Definition: postgres.h:69
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:753
@ COERCION_ASSIGNMENT
Definition: primnodes.h:732
Node * build_column_default(Relation rel, int attrno)
bool verify_new_notnull
Definition: tablecmds.c:190
bool is_not_null
Definition: parsenodes.h:742
char identity
Definition: parsenodes.h:748
RangeVar * identitySequence
Definition: parsenodes.h:749
char * colname
Definition: parsenodes.h:737
TypeName * typeName
Definition: parsenodes.h:738
char generated
Definition: parsenodes.h:751
Node * raw_default
Definition: parsenodes.h:746
bool is_local
Definition: parsenodes.h:741
int16 inhcount
Definition: parsenodes.h:740
Node * raw_default
Definition: heap.h:31
AttrNumber attnum
Definition: heap.h:30
char generated
Definition: heap.h:32
HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname)
Definition: syscache.c:503
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:91
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
Definition: tablecmds.c:7600
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition: tablecmds.c:7618
TupleDesc BuildDescForRelation(const List *columns)
Definition: tablecmds.c:1341
static AlterTableCmd * ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5672
static bool check_for_column_name_collision(Relation rel, const char *colname, bool if_not_exists)
Definition: tablecmds.c:7547
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:7120

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

9589{
9591
9592 Assert(IsA(newConstraint, Constraint));
9593
9594 /*
9595 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9596 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9597 * parse_utilcmd.c).
9598 */
9599 switch (newConstraint->contype)
9600 {
9601 case CONSTR_CHECK:
9602 case CONSTR_NOTNULL:
9603 address =
9604 ATAddCheckNNConstraint(wqueue, tab, rel,
9605 newConstraint, recurse, false, is_readd,
9606 lockmode);
9607 break;
9608
9609 case CONSTR_FOREIGN:
9610
9611 /*
9612 * Assign or validate constraint name
9613 */
9614 if (newConstraint->conname)
9615 {
9617 RelationGetRelid(rel),
9618 newConstraint->conname))
9619 ereport(ERROR,
9621 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9622 newConstraint->conname,
9624 }
9625 else
9626 newConstraint->conname =
9629 "fkey",
9631 NIL);
9632
9633 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9634 newConstraint,
9635 recurse, false,
9636 lockmode);
9637 break;
9638
9639 default:
9640 elog(ERROR, "unrecognized constraint type: %d",
9641 (int) newConstraint->contype);
9642 }
9643
9644 return address;
9645}
@ CONSTR_CHECK
Definition: parsenodes.h:2787
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:9853

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

8129{
8130 Relation attrelation;
8131 HeapTuple tuple;
8132 Form_pg_attribute attTup;
8134 ObjectAddress address;
8135 ColumnDef *cdef = castNode(ColumnDef, def);
8136 bool ispartitioned;
8137
8138 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8139 if (ispartitioned && !recurse)
8140 ereport(ERROR,
8141 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8142 errmsg("cannot add identity to a column of only the partitioned table"),
8143 errhint("Do not specify the ONLY keyword.")));
8144
8145 if (rel->rd_rel->relispartition && !recursing)
8146 ereport(ERROR,
8147 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8148 errmsg("cannot add identity to a column of a partition"));
8149
8150 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8151
8152 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8153 if (!HeapTupleIsValid(tuple))
8154 ereport(ERROR,
8155 (errcode(ERRCODE_UNDEFINED_COLUMN),
8156 errmsg("column \"%s\" of relation \"%s\" does not exist",
8157 colName, RelationGetRelationName(rel))));
8158 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8159 attnum = attTup->attnum;
8160
8161 /* Can't alter a system attribute */
8162 if (attnum <= 0)
8163 ereport(ERROR,
8164 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8165 errmsg("cannot alter system column \"%s\"",
8166 colName)));
8167
8168 /*
8169 * Creating a column as identity implies NOT NULL, so adding the identity
8170 * to an existing column that is not NOT NULL would create a state that
8171 * cannot be reproduced without contortions.
8172 */
8173 if (!attTup->attnotnull)
8174 ereport(ERROR,
8175 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8176 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8177 colName, RelationGetRelationName(rel))));
8178
8179 if (attTup->attidentity)
8180 ereport(ERROR,
8181 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8182 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8183 colName, RelationGetRelationName(rel))));
8184
8185 if (attTup->atthasdef)
8186 ereport(ERROR,
8187 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8188 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8189 colName, RelationGetRelationName(rel))));
8190
8191 attTup->attidentity = cdef->identity;
8192 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8193
8194 InvokeObjectPostAlterHook(RelationRelationId,
8195 RelationGetRelid(rel),
8196 attTup->attnum);
8197 ObjectAddressSubSet(address, RelationRelationId,
8198 RelationGetRelid(rel), attnum);
8199 heap_freetuple(tuple);
8200
8201 table_close(attrelation, RowExclusiveLock);
8202
8203 /*
8204 * Recurse to propagate the identity column to partitions. Identity is
8205 * not inherited in regular inheritance children.
8206 */
8207 if (recurse && ispartitioned)
8208 {
8209 List *children;
8210 ListCell *lc;
8211
8212 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8213
8214 foreach(lc, children)
8215 {
8216 Relation childrel;
8217
8218 childrel = table_open(lfirst_oid(lc), NoLock);
8219 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8220 table_close(childrel, NoLock);
8221 }
8222 }
8223
8224 return address;
8225}
int errhint(const char *fmt,...)
Definition: elog.c:1317
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8127

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

9412{
9413 bool check_rights;
9414 bool skip_build;
9415 bool quiet;
9416 ObjectAddress address;
9417
9419 Assert(!stmt->concurrent);
9420
9421 /* The IndexStmt has already been through transformIndexStmt */
9422 Assert(stmt->transformed);
9423
9424 /* suppress schema rights check when rebuilding existing index */
9425 check_rights = !is_rebuild;
9426 /* skip index build if phase 3 will do it or we're reusing an old one */
9427 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9428 /* suppress notices when rebuilding existing index */
9429 quiet = is_rebuild;
9430
9431 address = DefineIndex(RelationGetRelid(rel),
9432 stmt,
9433 InvalidOid, /* no predefined OID */
9434 InvalidOid, /* no parent index */
9435 InvalidOid, /* no parent constraint */
9436 -1, /* total_parts unknown */
9437 true, /* is_alter_table */
9438 check_rights,
9439 false, /* check_not_in_use - we did it already */
9440 skip_build,
9441 quiet);
9442
9443 /*
9444 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9445 * new index instead of building from scratch. Restore associated fields.
9446 * This may store InvalidSubTransactionId in both fields, in which case
9447 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9448 * this after the CCI that made catalog rows visible to any rebuild. The
9449 * DROP of the old edition of this index will have scheduled the storage
9450 * for deletion at commit, so cancel that pending deletion.
9451 */
9452 if (RelFileNumberIsValid(stmt->oldNumber))
9453 {
9454 Relation irel = index_open(address.objectId, NoLock);
9455
9456 irel->rd_createSubid = stmt->oldCreateSubid;
9457 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9459 index_close(irel, NoLock);
9460 }
9461
9462 return address;
9463}
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:251
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 9494 of file tablecmds.c.

9496{
9497 Oid index_oid = stmt->indexOid;
9498 Relation indexRel;
9499 char *indexName;
9500 IndexInfo *indexInfo;
9501 char *constraintName;
9502 char constraintType;
9503 ObjectAddress address;
9504 bits16 flags;
9505
9507 Assert(OidIsValid(index_oid));
9508 Assert(stmt->isconstraint);
9509
9510 /*
9511 * Doing this on partitioned tables is not a simple feature to implement,
9512 * so let's punt for now.
9513 */
9514 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9515 ereport(ERROR,
9516 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9517 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9518
9519 indexRel = index_open(index_oid, AccessShareLock);
9520
9521 indexName = pstrdup(RelationGetRelationName(indexRel));
9522
9523 indexInfo = BuildIndexInfo(indexRel);
9524
9525 /* this should have been checked at parse time */
9526 if (!indexInfo->ii_Unique)
9527 elog(ERROR, "index \"%s\" is not unique", indexName);
9528
9529 /*
9530 * Determine name to assign to constraint. We require a constraint to
9531 * have the same name as the underlying index; therefore, use the index's
9532 * existing name as the default constraint name, and if the user
9533 * explicitly gives some other name for the constraint, rename the index
9534 * to match.
9535 */
9536 constraintName = stmt->idxname;
9537 if (constraintName == NULL)
9538 constraintName = indexName;
9539 else if (strcmp(constraintName, indexName) != 0)
9540 {
9542 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9543 indexName, constraintName)));
9544 RenameRelationInternal(index_oid, constraintName, false, true);
9545 }
9546
9547 /* Extra checks needed if making primary key */
9548 if (stmt->primary)
9549 index_check_primary_key(rel, indexInfo, true, stmt);
9550
9551 /* Note we currently don't support EXCLUSION constraints here */
9552 if (stmt->primary)
9553 constraintType = CONSTRAINT_PRIMARY;
9554 else
9555 constraintType = CONSTRAINT_UNIQUE;
9556
9557 /* Create the catalog entries for the constraint */
9560 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9561 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9563
9564 address = index_constraint_create(rel,
9565 index_oid,
9566 InvalidOid,
9567 indexInfo,
9568 constraintName,
9569 constraintType,
9570 flags,
9572 false); /* is_internal */
9573
9574 index_close(indexRel, NoLock);
9575
9576 return address;
9577}
uint16 bits16
Definition: c.h:510
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:2428
void index_check_primary_key(Relation heapRel, const IndexInfo *indexInfo, bool is_alter_table, const IndexStmt *stmt)
Definition: index.c:202
ObjectAddress index_constraint_create(Relation heapRelation, Oid indexRelationId, Oid parentConstraintId, const IndexInfo *indexInfo, const char *constraintName, char constraintType, bits16 constr_flags, bool allow_system_table_mods, bool is_internal)
Definition: index.c:1885
#define INDEX_CONSTR_CREATE_UPDATE_INDEX
Definition: index.h:94
#define INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
Definition: index.h:95
#define INDEX_CONSTR_CREATE_DEFERRABLE
Definition: index.h:92
#define INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
Definition: index.h:91
#define INDEX_CONSTR_CREATE_INIT_DEFERRED
Definition: index.h:93
bool ii_Unique
Definition: execnodes.h:209
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:4231

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

16529{
16530 Relation parent_rel;
16531 List *children;
16532 ObjectAddress address;
16533 const char *trigger_name;
16534
16535 /*
16536 * A self-exclusive lock is needed here. See the similar case in
16537 * MergeAttributes() for a full explanation.
16538 */
16539 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
16540
16541 /*
16542 * Must be owner of both parent and child -- child was checked by
16543 * ATSimplePermissions call in ATPrepCmd
16544 */
16547
16548 /* Permanent rels cannot inherit from temporary ones */
16549 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16550 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16551 ereport(ERROR,
16552 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16553 errmsg("cannot inherit from temporary relation \"%s\"",
16554 RelationGetRelationName(parent_rel))));
16555
16556 /* If parent rel is temp, it must belong to this session */
16557 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16558 !parent_rel->rd_islocaltemp)
16559 ereport(ERROR,
16560 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16561 errmsg("cannot inherit from temporary relation of another session")));
16562
16563 /* Ditto for the child */
16564 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16565 !child_rel->rd_islocaltemp)
16566 ereport(ERROR,
16567 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16568 errmsg("cannot inherit to temporary relation of another session")));
16569
16570 /* Prevent partitioned tables from becoming inheritance parents */
16571 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16572 ereport(ERROR,
16573 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16574 errmsg("cannot inherit from partitioned table \"%s\"",
16575 parent->relname)));
16576
16577 /* Likewise for partitions */
16578 if (parent_rel->rd_rel->relispartition)
16579 ereport(ERROR,
16580 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16581 errmsg("cannot inherit from a partition")));
16582
16583 /*
16584 * Prevent circularity by seeing if proposed parent inherits from child.
16585 * (In particular, this disallows making a rel inherit from itself.)
16586 *
16587 * This is not completely bulletproof because of race conditions: in
16588 * multi-level inheritance trees, someone else could concurrently be
16589 * making another inheritance link that closes the loop but does not join
16590 * either of the rels we have locked. Preventing that seems to require
16591 * exclusive locks on the entire inheritance tree, which is a cure worse
16592 * than the disease. find_all_inheritors() will cope with circularity
16593 * anyway, so don't sweat it too much.
16594 *
16595 * We use weakest lock we can on child's children, namely AccessShareLock.
16596 */
16597 children = find_all_inheritors(RelationGetRelid(child_rel),
16598 AccessShareLock, NULL);
16599
16600 if (list_member_oid(children, RelationGetRelid(parent_rel)))
16601 ereport(ERROR,
16602 (errcode(ERRCODE_DUPLICATE_TABLE),
16603 errmsg("circular inheritance not allowed"),
16604 errdetail("\"%s\" is already a child of \"%s\".",
16605 parent->relname,
16606 RelationGetRelationName(child_rel))));
16607
16608 /*
16609 * If child_rel has row-level triggers with transition tables, we
16610 * currently don't allow it to become an inheritance child. See also
16611 * prohibitions in ATExecAttachPartition() and CreateTrigger().
16612 */
16613 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
16614 if (trigger_name != NULL)
16615 ereport(ERROR,
16616 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16617 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16618 trigger_name, RelationGetRelationName(child_rel)),
16619 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16620
16621 /* OK to create inheritance */
16622 CreateInheritance(child_rel, parent_rel, false);
16623
16624 ObjectAddressSet(address, RelationRelationId,
16625 RelationGetRelid(parent_rel));
16626
16627 /* keep our lock on the parent relation until commit */
16628 table_close(parent_rel, NoLock);
16629
16630 return address;
16631}
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:16641
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 17483 of file tablecmds.c.

17484{
17485 Oid relid = RelationGetRelid(rel);
17486 Type typetuple;
17487 Form_pg_type typeform;
17488 Oid typeid;
17489 Relation inheritsRelation,
17490 relationRelation;
17491 SysScanDesc scan;
17493 AttrNumber table_attno,
17494 type_attno;
17495 TupleDesc typeTupleDesc,
17496 tableTupleDesc;
17497 ObjectAddress tableobj,
17498 typeobj;
17499 HeapTuple classtuple;
17500
17501 /* Validate the type. */
17502 typetuple = typenameType(NULL, ofTypename, NULL);
17503 check_of_type(typetuple);
17504 typeform = (Form_pg_type) GETSTRUCT(typetuple);
17505 typeid = typeform->oid;
17506
17507 /* Fail if the table has any inheritance parents. */
17508 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
17510 Anum_pg_inherits_inhrelid,
17511 BTEqualStrategyNumber, F_OIDEQ,
17512 ObjectIdGetDatum(relid));
17513 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
17514 true, NULL, 1, &key);
17516 ereport(ERROR,
17517 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17518 errmsg("typed tables cannot inherit")));
17519 systable_endscan(scan);
17520 table_close(inheritsRelation, AccessShareLock);
17521
17522 /*
17523 * Check the tuple descriptors for compatibility. Unlike inheritance, we
17524 * require that the order also match. However, attnotnull need not match.
17525 */
17526 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
17527 tableTupleDesc = RelationGetDescr(rel);
17528 table_attno = 1;
17529 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
17530 {
17531 Form_pg_attribute type_attr,
17532 table_attr;
17533 const char *type_attname,
17534 *table_attname;
17535
17536 /* Get the next non-dropped type attribute. */
17537 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
17538 if (type_attr->attisdropped)
17539 continue;
17540 type_attname = NameStr(type_attr->attname);
17541
17542 /* Get the next non-dropped table attribute. */
17543 do
17544 {
17545 if (table_attno > tableTupleDesc->natts)
17546 ereport(ERROR,
17547 (errcode(ERRCODE_DATATYPE_MISMATCH),
17548 errmsg("table is missing column \"%s\"",
17549 type_attname)));
17550 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
17551 table_attno++;
17552 } while (table_attr->attisdropped);
17553 table_attname = NameStr(table_attr->attname);
17554
17555 /* Compare name. */
17556 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
17557 ereport(ERROR,
17558 (errcode(ERRCODE_DATATYPE_MISMATCH),
17559 errmsg("table has column \"%s\" where type requires \"%s\"",
17560 table_attname, type_attname)));
17561
17562 /* Compare type. */
17563 if (table_attr->atttypid != type_attr->atttypid ||
17564 table_attr->atttypmod != type_attr->atttypmod ||
17565 table_attr->attcollation != type_attr->attcollation)
17566 ereport(ERROR,
17567 (errcode(ERRCODE_DATATYPE_MISMATCH),
17568 errmsg("table \"%s\" has different type for column \"%s\"",
17569 RelationGetRelationName(rel), type_attname)));
17570 }
17571 ReleaseTupleDesc(typeTupleDesc);
17572
17573 /* Any remaining columns at the end of the table had better be dropped. */
17574 for (; table_attno <= tableTupleDesc->natts; table_attno++)
17575 {
17576 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
17577 table_attno - 1);
17578
17579 if (!table_attr->attisdropped)
17580 ereport(ERROR,
17581 (errcode(ERRCODE_DATATYPE_MISMATCH),
17582 errmsg("table has extra column \"%s\"",
17583 NameStr(table_attr->attname))));
17584 }
17585
17586 /* If the table was already typed, drop the existing dependency. */
17587 if (rel->rd_rel->reloftype)
17588 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17590
17591 /* Record a dependency on the new type. */
17592 tableobj.classId = RelationRelationId;
17593 tableobj.objectId = relid;
17594 tableobj.objectSubId = 0;
17595 typeobj.classId = TypeRelationId;
17596 typeobj.objectId = typeid;
17597 typeobj.objectSubId = 0;
17598 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
17599
17600 /* Update pg_class.reloftype */
17601 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17602 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17603 if (!HeapTupleIsValid(classtuple))
17604 elog(ERROR, "cache lookup failed for relation %u", relid);
17605 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
17606 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
17607
17608 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17609
17610 heap_freetuple(classtuple);
17611 table_close(relationRelation, RowExclusiveLock);
17612
17613 ReleaseSysCache(typetuple);
17614
17615 return typeobj;
17616}
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:17431
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:7046
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:213
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1920

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

9475{
9476 ObjectAddress address;
9477
9479
9480 /* The CreateStatsStmt has already been through transformStatsStmt */
9481 Assert(stmt->transformed);
9482
9483 address = CreateStatistics(stmt);
9484
9485 return address;
9486}
ObjectAddress CreateStatistics(CreateStatsStmt *stmt)
Definition: statscmds.c:62

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

Referenced by ATExecCmd().

◆ ATExecAlterChildConstr()

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

Definition at line 12281 of file tablecmds.c.

12285{
12286 Form_pg_constraint currcon;
12287 Oid conoid;
12288 ScanKeyData pkey;
12289 SysScanDesc pscan;
12290 HeapTuple childtup;
12291
12292 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12293 conoid = currcon->oid;
12294
12295 ScanKeyInit(&pkey,
12296 Anum_pg_constraint_conparentid,
12297 BTEqualStrategyNumber, F_OIDEQ,
12298 ObjectIdGetDatum(conoid));
12299
12300 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12301 true, NULL, 1, &pkey);
12302
12303 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12304 {
12305 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12306 Relation childrel;
12307
12308 childrel = table_open(childcon->conrelid, lockmode);
12309 ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, childrel,
12310 childtup, recurse, otherrelids, lockmode);
12311 table_close(childrel, NoLock);
12312 }
12313
12314 systable_endscan(pscan);
12315}
static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12070

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

Referenced by ATExecAlterConstraintInternal().

◆ ATExecAlterColumnGenericOptions()

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

Definition at line 15222 of file tablecmds.c.

15226{
15227 Relation ftrel;
15228 Relation attrel;
15229 ForeignServer *server;
15230 ForeignDataWrapper *fdw;
15231 HeapTuple tuple;
15232 HeapTuple newtuple;
15233 bool isnull;
15234 Datum repl_val[Natts_pg_attribute];
15235 bool repl_null[Natts_pg_attribute];
15236 bool repl_repl[Natts_pg_attribute];
15237 Datum datum;
15238 Form_pg_foreign_table fttableform;
15239 Form_pg_attribute atttableform;
15241 ObjectAddress address;
15242
15243 if (options == NIL)
15244 return InvalidObjectAddress;
15245
15246 /* First, determine FDW validator associated to the foreign table. */
15247 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15248 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15249 if (!HeapTupleIsValid(tuple))
15250 ereport(ERROR,
15251 (errcode(ERRCODE_UNDEFINED_OBJECT),
15252 errmsg("foreign table \"%s\" does not exist",
15254 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15255 server = GetForeignServer(fttableform->ftserver);
15256 fdw = GetForeignDataWrapper(server->fdwid);
15257
15259 ReleaseSysCache(tuple);
15260
15261 attrel = table_open(AttributeRelationId, RowExclusiveLock);
15262 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15263 if (!HeapTupleIsValid(tuple))
15264 ereport(ERROR,
15265 (errcode(ERRCODE_UNDEFINED_COLUMN),
15266 errmsg("column \"%s\" of relation \"%s\" does not exist",
15267 colName, RelationGetRelationName(rel))));
15268
15269 /* Prevent them from altering a system attribute */
15270 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15271 attnum = atttableform->attnum;
15272 if (attnum <= 0)
15273 ereport(ERROR,
15274 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15275 errmsg("cannot alter system column \"%s\"", colName)));
15276
15277
15278 /* Initialize buffers for new tuple values */
15279 memset(repl_val, 0, sizeof(repl_val));
15280 memset(repl_null, false, sizeof(repl_null));
15281 memset(repl_repl, false, sizeof(repl_repl));
15282
15283 /* Extract the current options */
15284 datum = SysCacheGetAttr(ATTNAME,
15285 tuple,
15286 Anum_pg_attribute_attfdwoptions,
15287 &isnull);
15288 if (isnull)
15289 datum = PointerGetDatum(NULL);
15290
15291 /* Transform the options */
15292 datum = transformGenericOptions(AttributeRelationId,
15293 datum,
15294 options,
15295 fdw->fdwvalidator);
15296
15297 if (PointerIsValid(DatumGetPointer(datum)))
15298 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
15299 else
15300 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
15301
15302 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
15303
15304 /* Everything looks good - update the tuple */
15305
15306 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
15307 repl_val, repl_null, repl_repl);
15308
15309 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
15310
15311 InvokeObjectPostAlterHook(RelationRelationId,
15312 RelationGetRelid(rel),
15313 atttableform->attnum);
15314 ObjectAddressSubSet(address, RelationRelationId,
15315 RelationGetRelid(rel), attnum);
15316
15317 ReleaseSysCache(tuple);
15318
15320
15321 heap_freetuple(newtuple);
15322
15323 return address;
15324}
#define PointerIsValid(pointer)
Definition: c.h:734
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition: foreign.c:37
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:111
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
Definition: foreigncmds.c:110
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition: heaptuple.c:1210
FormData_pg_foreign_table * Form_pg_foreign_table
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
Oid rd_id
Definition: rel.h:113
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:600
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition: syscache.c:480

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

Referenced by ATExecCmd().

◆ ATExecAlterColumnType()

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

Definition at line 14016 of file tablecmds.c.

14018{
14019 char *colName = cmd->name;
14020 ColumnDef *def = (ColumnDef *) cmd->def;
14021 TypeName *typeName = def->typeName;
14022 HeapTuple heapTup;
14023 Form_pg_attribute attTup,
14024 attOldTup;
14026 HeapTuple typeTuple;
14027 Form_pg_type tform;
14028 Oid targettype;
14029 int32 targettypmod;
14030 Oid targetcollid;
14031 Node *defaultexpr;
14032 Relation attrelation;
14033 Relation depRel;
14034 ScanKeyData key[3];
14035 SysScanDesc scan;
14036 HeapTuple depTup;
14037 ObjectAddress address;
14038
14039 /*
14040 * Clear all the missing values if we're rewriting the table, since this
14041 * renders them pointless.
14042 */
14043 if (tab->rewrite)
14044 {
14045 Relation newrel;
14046
14047 newrel = table_open(RelationGetRelid(rel), NoLock);
14048 RelationClearMissing(newrel);
14049 relation_close(newrel, NoLock);
14050 /* make sure we don't conflict with later attribute modifications */
14052 }
14053
14054 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14055
14056 /* Look up the target column */
14057 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14058 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14059 ereport(ERROR,
14060 (errcode(ERRCODE_UNDEFINED_COLUMN),
14061 errmsg("column \"%s\" of relation \"%s\" does not exist",
14062 colName, RelationGetRelationName(rel))));
14063 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14064 attnum = attTup->attnum;
14065 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14066
14067 /* Check for multiple ALTER TYPE on same column --- can't cope */
14068 if (attTup->atttypid != attOldTup->atttypid ||
14069 attTup->atttypmod != attOldTup->atttypmod)
14070 ereport(ERROR,
14071 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14072 errmsg("cannot alter type of column \"%s\" twice",
14073 colName)));
14074
14075 /* Look up the target type (should not fail, since prep found it) */
14076 typeTuple = typenameType(NULL, typeName, &targettypmod);
14077 tform = (Form_pg_type) GETSTRUCT(typeTuple);
14078 targettype = tform->oid;
14079 /* And the collation */
14080 targetcollid = GetColumnDefCollation(NULL, def, targettype);
14081
14082 /*
14083 * If there is a default expression for the column, get it and ensure we
14084 * can coerce it to the new datatype. (We must do this before changing
14085 * the column type, because build_column_default itself will try to
14086 * coerce, and will not issue the error message we want if it fails.)
14087 *
14088 * We remove any implicit coercion steps at the top level of the old
14089 * default expression; this has been agreed to satisfy the principle of
14090 * least surprise. (The conversion to the new column type should act like
14091 * it started from what the user sees as the stored expression, and the
14092 * implicit coercions aren't going to be shown.)
14093 */
14094 if (attTup->atthasdef)
14095 {
14096 defaultexpr = build_column_default(rel, attnum);
14097 Assert(defaultexpr);
14098 defaultexpr = strip_implicit_coercions(defaultexpr);
14099 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14100 defaultexpr, exprType(defaultexpr),
14101 targettype, targettypmod,
14104 -1);
14105 if (defaultexpr == NULL)
14106 {
14107 if (attTup->attgenerated)
14108 ereport(ERROR,
14109 (errcode(ERRCODE_DATATYPE_MISMATCH),
14110 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14111 colName, format_type_be(targettype))));
14112 else
14113 ereport(ERROR,
14114 (errcode(ERRCODE_DATATYPE_MISMATCH),
14115 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14116 colName, format_type_be(targettype))));
14117 }
14118 }
14119 else
14120 defaultexpr = NULL;
14121
14122 /*
14123 * Find everything that depends on the column (constraints, indexes, etc),
14124 * and record enough information to let us recreate the objects.
14125 *
14126 * The actual recreation does not happen here, but only after we have
14127 * performed all the individual ALTER TYPE operations. We have to save
14128 * the info before executing ALTER TYPE, though, else the deparser will
14129 * get confused.
14130 */
14132
14133 /*
14134 * Now scan for dependencies of this column on other things. The only
14135 * things we should find are the dependency on the column datatype and
14136 * possibly a collation dependency. Those can be removed.
14137 */
14138 depRel = table_open(DependRelationId, RowExclusiveLock);
14139
14140 ScanKeyInit(&key[0],
14141 Anum_pg_depend_classid,
14142 BTEqualStrategyNumber, F_OIDEQ,
14143 ObjectIdGetDatum(RelationRelationId));
14144 ScanKeyInit(&key[1],
14145 Anum_pg_depend_objid,
14146 BTEqualStrategyNumber, F_OIDEQ,
14148 ScanKeyInit(&key[2],
14149 Anum_pg_depend_objsubid,
14150 BTEqualStrategyNumber, F_INT4EQ,
14152
14153 scan = systable_beginscan(depRel, DependDependerIndexId, true,
14154 NULL, 3, key);
14155
14156 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14157 {
14158 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14159 ObjectAddress foundObject;
14160
14161 foundObject.classId = foundDep->refclassid;
14162 foundObject.objectId = foundDep->refobjid;
14163 foundObject.objectSubId = foundDep->refobjsubid;
14164
14165 if (foundDep->deptype != DEPENDENCY_NORMAL)
14166 elog(ERROR, "found unexpected dependency type '%c'",
14167 foundDep->deptype);
14168 if (!(foundDep->refclassid == TypeRelationId &&
14169 foundDep->refobjid == attTup->atttypid) &&
14170 !(foundDep->refclassid == CollationRelationId &&
14171 foundDep->refobjid == attTup->attcollation))
14172 elog(ERROR, "found unexpected dependency for column: %s",
14173 getObjectDescription(&foundObject, false));
14174
14175 CatalogTupleDelete(depRel, &depTup->t_self);
14176 }
14177
14178 systable_endscan(scan);
14179
14181
14182 /*
14183 * Here we go --- change the recorded column type and collation. (Note
14184 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14185 * fix up the missing value if any.
14186 */
14187 if (attTup->atthasmissing)
14188 {
14189 Datum missingval;
14190 bool missingNull;
14191
14192 /* if rewrite is true the missing value should already be cleared */
14193 Assert(tab->rewrite == 0);
14194
14195 /* Get the missing value datum */
14196 missingval = heap_getattr(heapTup,
14197 Anum_pg_attribute_attmissingval,
14198 attrelation->rd_att,
14199 &missingNull);
14200
14201 /* if it's a null array there is nothing to do */
14202
14203 if (!missingNull)
14204 {
14205 /*
14206 * Get the datum out of the array and repack it in a new array
14207 * built with the new type data. We assume that since the table
14208 * doesn't need rewriting, the actual Datum doesn't need to be
14209 * changed, only the array metadata.
14210 */
14211
14212 int one = 1;
14213 bool isNull;
14214 Datum valuesAtt[Natts_pg_attribute] = {0};
14215 bool nullsAtt[Natts_pg_attribute] = {0};
14216 bool replacesAtt[Natts_pg_attribute] = {0};
14217 HeapTuple newTup;
14218
14219 missingval = array_get_element(missingval,
14220 1,
14221 &one,
14222 0,
14223 attTup->attlen,
14224 attTup->attbyval,
14225 attTup->attalign,
14226 &isNull);
14227 missingval = PointerGetDatum(construct_array(&missingval,
14228 1,
14229 targettype,
14230 tform->typlen,
14231 tform->typbyval,
14232 tform->typalign));
14233
14234 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14235 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14236 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14237
14238 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14239 valuesAtt, nullsAtt, replacesAtt);
14240 heap_freetuple(heapTup);
14241 heapTup = newTup;
14242 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14243 }
14244 }
14245
14246 attTup->atttypid = targettype;
14247 attTup->atttypmod = targettypmod;
14248 attTup->attcollation = targetcollid;
14249 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14250 ereport(ERROR,
14251 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14252 errmsg("too many array dimensions"));
14253 attTup->attndims = list_length(typeName->arrayBounds);
14254 attTup->attlen = tform->typlen;
14255 attTup->attbyval = tform->typbyval;
14256 attTup->attalign = tform->typalign;
14257 attTup->attstorage = tform->typstorage;
14258 attTup->attcompression = InvalidCompressionMethod;
14259
14260 ReleaseSysCache(typeTuple);
14261
14262 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14263
14264 table_close(attrelation, RowExclusiveLock);
14265
14266 /* Install dependencies on new datatype and collation */
14269
14270 /*
14271 * Drop any pg_statistic entry for the column, since it's now wrong type
14272 */
14274
14275 InvokeObjectPostAlterHook(RelationRelationId,
14276 RelationGetRelid(rel), attnum);
14277
14278 /*
14279 * Update the default, if present, by brute force --- remove and re-add
14280 * the default. Probably unsafe to take shortcuts, since the new version
14281 * may well have additional dependencies. (It's okay to do this now,
14282 * rather than after other ALTER TYPE commands, since the default won't
14283 * depend on other column types.)
14284 */
14285 if (defaultexpr)
14286 {
14287 /*
14288 * If it's a GENERATED default, drop its dependency records, in
14289 * particular its INTERNAL dependency on the column, which would
14290 * otherwise cause dependency.c to refuse to perform the deletion.
14291 */
14292 if (attTup->attgenerated)
14293 {
14294 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14295
14296 if (!OidIsValid(attrdefoid))
14297 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14298 RelationGetRelid(rel), attnum);
14299 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14300 }
14301
14302 /*
14303 * Make updates-so-far visible, particularly the new pg_attribute row
14304 * which will be updated again.
14305 */
14307
14308 /*
14309 * We use RESTRICT here for safety, but at present we do not expect
14310 * anything to depend on the default.
14311 */
14313 true);
14314
14315 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
14316 }
14317
14318 ObjectAddressSubSet(address, RelationRelationId,
14319 RelationGetRelid(rel), attnum);
14320
14321 /* Cleanup */
14322 heap_freetuple(heapTup);
14323
14324 return address;
14325}
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:3403
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:903
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:78
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:2385
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:14333
#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 11916 of file tablecmds.c.

11918{
11919 Relation conrel;
11920 Relation tgrel;
11921 SysScanDesc scan;
11922 ScanKeyData skey[3];
11923 HeapTuple contuple;
11924 Form_pg_constraint currcon;
11925 ObjectAddress address;
11926 List *otherrelids = NIL;
11927
11928 /*
11929 * Disallow altering ONLY a partitioned table, as it would make no sense.
11930 * This is okay for legacy inheritance.
11931 */
11932 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
11933 ereport(ERROR,
11934 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11935 errmsg("constraint must be altered in child tables too"),
11936 errhint("Do not specify the ONLY keyword."));
11937
11938
11939 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11940 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11941
11942 /*
11943 * Find and check the target constraint
11944 */
11945 ScanKeyInit(&skey[0],
11946 Anum_pg_constraint_conrelid,
11947 BTEqualStrategyNumber, F_OIDEQ,
11949 ScanKeyInit(&skey[1],
11950 Anum_pg_constraint_contypid,
11951 BTEqualStrategyNumber, F_OIDEQ,
11953 ScanKeyInit(&skey[2],
11954 Anum_pg_constraint_conname,
11955 BTEqualStrategyNumber, F_NAMEEQ,
11956 CStringGetDatum(cmdcon->conname));
11957 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11958 true, NULL, 3, skey);
11959
11960 /* There can be at most one matching row */
11961 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11962 ereport(ERROR,
11963 (errcode(ERRCODE_UNDEFINED_OBJECT),
11964 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11965 cmdcon->conname, RelationGetRelationName(rel))));
11966
11967 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11968 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
11969 ereport(ERROR,
11970 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11971 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11972 cmdcon->conname, RelationGetRelationName(rel))));
11973 if (cmdcon->alterInheritability &&
11974 currcon->contype != CONSTRAINT_NOTNULL)
11975 ereport(ERROR,
11976 errcode(ERRCODE_WRONG_OBJECT_TYPE),
11977 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
11978 cmdcon->conname, RelationGetRelationName(rel)));
11979
11980 /* Refuse to modify inheritability of inherited constraints */
11981 if (cmdcon->alterInheritability &&
11982 cmdcon->noinherit && currcon->coninhcount > 0)
11983 ereport(ERROR,
11984 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11985 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
11986 NameStr(currcon->conname),
11988
11989 /*
11990 * If it's not the topmost constraint, raise an error.
11991 *
11992 * Altering a non-topmost constraint leaves some triggers untouched, since
11993 * they are not directly connected to this constraint; also, pg_dump would
11994 * ignore the deferrability status of the individual constraint, since it
11995 * only dumps topmost constraints. Avoid these problems by refusing this
11996 * operation and telling the user to alter the parent constraint instead.
11997 */
11998 if (OidIsValid(currcon->conparentid))
11999 {
12000 HeapTuple tp;
12001 Oid parent = currcon->conparentid;
12002 char *ancestorname = NULL;
12003 char *ancestortable = NULL;
12004
12005 /* Loop to find the topmost constraint */
12006 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12007 {
12009
12010 /* If no parent, this is the constraint we want */
12011 if (!OidIsValid(contup->conparentid))
12012 {
12013 ancestorname = pstrdup(NameStr(contup->conname));
12014 ancestortable = get_rel_name(contup->conrelid);
12015 ReleaseSysCache(tp);
12016 break;
12017 }
12018
12019 parent = contup->conparentid;
12020 ReleaseSysCache(tp);
12021 }
12022
12023 ereport(ERROR,
12024 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12025 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12026 cmdcon->conname, RelationGetRelationName(rel)),
12027 ancestorname && ancestortable ?
12028 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12029 cmdcon->conname, ancestorname, ancestortable) : 0,
12030 errhint("You may alter the constraint it derives from instead.")));
12031 }
12032
12033 address = InvalidObjectAddress;
12034
12035 /*
12036 * Do the actual catalog work, and recurse if necessary.
12037 */
12038 if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12039 contuple, recurse, &otherrelids, lockmode))
12040 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12041
12042 /*
12043 * ATExecAlterConstraintInternal already invalidated relcache for the
12044 * relations having the constraint itself; here we also invalidate for
12045 * relations that have any triggers that are part of the constraint.
12046 */
12047 foreach_oid(relid, otherrelids)
12049
12050 systable_endscan(scan);
12051
12054
12055 return address;
12056}
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1677
#define foreach_oid(var, lst)
Definition: pg_list.h:471
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:355

References ATAlterConstraint::alterDeferrability, ATAlterConstraint::alterInheritability, ATExecAlterConstraintInternal(), BTEqualStrategyNumber, CacheInvalidateRelcacheByRelid(), ATAlterConstraint::conname, CStringGetDatum(), ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, foreach_oid, get_rel_name(), GETSTRUCT(), HeapTupleIsValid, InvalidObjectAddress, InvalidOid, NameStr, NIL, 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,
List **  otherrelids,
LOCKMODE  lockmode 
)
static

Definition at line 12070 of file tablecmds.c.

12074{
12075 Form_pg_constraint currcon;
12076 Oid refrelid = InvalidOid;
12077 bool changed = false;
12078
12079 /* since this function recurses, it could be driven to stack overflow */
12081
12082 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12083 if (currcon->contype == CONSTRAINT_FOREIGN)
12084 refrelid = currcon->confrelid;
12085
12086 /*
12087 * Update pg_constraint with the flags from cmdcon.
12088 *
12089 * If called to modify a constraint that's already in the desired state,
12090 * silently do nothing.
12091 */
12092 if (cmdcon->alterDeferrability &&
12093 (currcon->condeferrable != cmdcon->deferrable ||
12094 currcon->condeferred != cmdcon->initdeferred))
12095 {
12096 HeapTuple copyTuple;
12097 Form_pg_constraint copy_con;
12098
12099 copyTuple = heap_copytuple(contuple);
12100 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12101 copy_con->condeferrable = cmdcon->deferrable;
12102 copy_con->condeferred = cmdcon->initdeferred;
12103 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12104
12105 InvokeObjectPostAlterHook(ConstraintRelationId, currcon->oid, 0);
12106
12107 heap_freetuple(copyTuple);
12108 changed = true;
12109
12110 /* Make new constraint flags visible to others */
12112
12113 /*
12114 * Now we need to update the multiple entries in pg_trigger that
12115 * implement the constraint.
12116 */
12117 AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12118 cmdcon->deferrable,
12119 cmdcon->initdeferred, otherrelids);
12120 }
12121
12122 /*
12123 * If the table at either end of the constraint is partitioned, we need to
12124 * handle every constraint that is a child of this one.
12125 */
12126 if (recurse && changed &&
12127 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12128 (OidIsValid(refrelid) &&
12129 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)))
12130 ATExecAlterChildConstr(wqueue, cmdcon, conrel, tgrel, rel, contuple,
12131 recurse, otherrelids, lockmode);
12132
12133 /*
12134 * Update the catalog for inheritability. No work if the constraint is
12135 * already in the requested state.
12136 */
12137 if (cmdcon->alterInheritability &&
12138 (cmdcon->noinherit != currcon->connoinherit))
12139 {
12140 AttrNumber colNum;
12141 char *colName;
12142 List *children;
12143 HeapTuple copyTuple;
12144 Form_pg_constraint copy_con;
12145
12146 /* The current implementation only works for NOT NULL constraints */
12147 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12148
12149 copyTuple = heap_copytuple(contuple);
12150 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12151 copy_con->connoinherit = cmdcon->noinherit;
12152
12153 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12155 heap_freetuple(copyTuple);
12156 changed = true;
12157
12158 /* Fetch the column number and name */
12159 colNum = extractNotNullColumn(contuple);
12160 colName = get_attname(currcon->conrelid, colNum, false);
12161
12162 /*
12163 * Propagate the change to children. For SET NO INHERIT, we don't
12164 * recursively affect children, just the immediate level.
12165 */
12167 lockmode);
12168 foreach_oid(childoid, children)
12169 {
12170 ObjectAddress addr;
12171
12172 if (cmdcon->noinherit)
12173 {
12174 HeapTuple childtup;
12175 Form_pg_constraint childcon;
12176
12177 childtup = findNotNullConstraint(childoid, colName);
12178 if (!childtup)
12179 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12180 colName, childoid);
12181 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12182 Assert(childcon->coninhcount > 0);
12183 childcon->coninhcount--;
12184 childcon->conislocal = true;
12185 CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12186 heap_freetuple(childtup);
12187 }
12188 else
12189 {
12190 Relation childrel = table_open(childoid, NoLock);
12191
12192 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12193 colName, true, true, lockmode);
12194 if (OidIsValid(addr.objectId))
12196 table_close(childrel, NoLock);
12197 }
12198 }
12199 }
12200
12201 return changed;
12202}
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1621
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:862
HeapTuple findNotNullConstraint(Oid relid, const char *colname)
AttrNumber extractNotNullColumn(HeapTuple constrTup)
static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel, char *constrname, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:7801
static void ATExecAlterChildConstr(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12281
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
Definition: tablecmds.c:12212

References AlterConstrTriggerDeferrability(), ATAlterConstraint::alterDeferrability, ATAlterConstraint::alterInheritability, Assert(), ATExecAlterChildConstr(), ATExecSetNotNull(), CacheInvalidateRelcache(), CatalogTupleUpdate(), check_stack_depth(), CommandCounterIncrement(), ATAlterConstraint::deferrable, elog, ERROR, extractNotNullColumn(), find_inheritance_children(), findNotNullConstraint(), foreach_oid, get_attname(), get_rel_relkind(), GETSTRUCT(), heap_copytuple(), heap_freetuple(), ATAlterConstraint::initdeferred, InvalidOid, InvokeObjectPostAlterHook, NameStr, ATAlterConstraint::noinherit, NoLock, ObjectAddress::objectId, OidIsValid, RelationData::rd_rel, RelationGetRelid, HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecAlterChildConstr(), and ATExecAlterConstraint().

◆ ATExecAttachPartition()

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

Definition at line 19505 of file tablecmds.c.

19507{
19508 Relation attachrel,
19509 catalog;
19510 List *attachrel_children;
19511 List *partConstraint;
19512 SysScanDesc scan;
19513 ScanKeyData skey;
19514 AttrNumber attno;
19515 int natts;
19516 TupleDesc tupleDesc;
19517 ObjectAddress address;
19518 const char *trigger_name;
19519 Oid defaultPartOid;
19520 List *partBoundConstraint;
19521 ParseState *pstate = make_parsestate(NULL);
19522
19523 pstate->p_sourcetext = context->queryString;
19524
19525 /*
19526 * We must lock the default partition if one exists, because attaching a
19527 * new partition will change its partition constraint.
19528 */
19529 defaultPartOid =
19531 if (OidIsValid(defaultPartOid))
19532 LockRelationOid(defaultPartOid, AccessExclusiveLock);
19533
19534 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
19535
19536 /*
19537 * XXX I think it'd be a good idea to grab locks on all tables referenced
19538 * by FKs at this point also.
19539 */
19540
19541 /*
19542 * Must be owner of both parent and source table -- parent was checked by
19543 * ATSimplePermissions call in ATPrepCmd
19544 */
19547
19548 /* A partition can only have one parent */
19549 if (attachrel->rd_rel->relispartition)
19550 ereport(ERROR,
19551 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19552 errmsg("\"%s\" is already a partition",
19553 RelationGetRelationName(attachrel))));
19554
19555 if (OidIsValid(attachrel->rd_rel->reloftype))
19556 ereport(ERROR,
19557 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19558 errmsg("cannot attach a typed table as partition")));
19559
19560 /*
19561 * Table being attached should not already be part of inheritance; either
19562 * as a child table...
19563 */
19564 catalog = table_open(InheritsRelationId, AccessShareLock);
19565 ScanKeyInit(&skey,
19566 Anum_pg_inherits_inhrelid,
19567 BTEqualStrategyNumber, F_OIDEQ,
19569 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
19570 NULL, 1, &skey);
19572 ereport(ERROR,
19573 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19574 errmsg("cannot attach inheritance child as partition")));
19575 systable_endscan(scan);
19576
19577 /* ...or as a parent table (except the case when it is partitioned) */
19578 ScanKeyInit(&skey,
19579 Anum_pg_inherits_inhparent,
19580 BTEqualStrategyNumber, F_OIDEQ,
19582 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
19583 1, &skey);
19585 attachrel->rd_rel->relkind == RELKIND_RELATION)
19586 ereport(ERROR,
19587 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19588 errmsg("cannot attach inheritance parent as partition")));
19589 systable_endscan(scan);
19590 table_close(catalog, AccessShareLock);
19591
19592 /*
19593 * Prevent circularity by seeing if rel is a partition of attachrel. (In
19594 * particular, this disallows making a rel a partition of itself.)
19595 *
19596 * We do that by checking if rel is a member of the list of attachrel's
19597 * partitions provided the latter is partitioned at all. We want to avoid
19598 * having to construct this list again, so we request the strongest lock
19599 * on all partitions. We need the strongest lock, because we may decide
19600 * to scan them if we find out that the table being attached (or its leaf
19601 * partitions) may contain rows that violate the partition constraint. If
19602 * the table has a constraint that would prevent such rows, which by
19603 * definition is present in all the partitions, we need not scan the
19604 * table, nor its partitions. But we cannot risk a deadlock by taking a
19605 * weaker lock now and the stronger one only when needed.
19606 */
19607 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
19608 AccessExclusiveLock, NULL);
19609 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
19610 ereport(ERROR,
19611 (errcode(ERRCODE_DUPLICATE_TABLE),
19612 errmsg("circular inheritance not allowed"),
19613 errdetail("\"%s\" is already a child of \"%s\".",
19615 RelationGetRelationName(attachrel))));
19616
19617 /* If the parent is permanent, so must be all of its partitions. */
19618 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
19619 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
19620 ereport(ERROR,
19621 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19622 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19624
19625 /* Temp parent cannot have a partition that is itself not a temp */
19626 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19627 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
19628 ereport(ERROR,
19629 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19630 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19632
19633 /* If the parent is temp, it must belong to this session */
19634 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19635 !rel->rd_islocaltemp)
19636 ereport(ERROR,
19637 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19638 errmsg("cannot attach as partition of temporary relation of another session")));
19639
19640 /* Ditto for the partition */
19641 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19642 !attachrel->rd_islocaltemp)
19643 ereport(ERROR,
19644 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19645 errmsg("cannot attach temporary relation of another session as partition")));
19646
19647 /*
19648 * Check if attachrel has any identity columns or any columns that aren't
19649 * in the parent.
19650 */
19651 tupleDesc = RelationGetDescr(attachrel);
19652 natts = tupleDesc->natts;
19653 for (attno = 1; attno <= natts; attno++)
19654 {
19655 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
19656 char *attributeName = NameStr(attribute->attname);
19657
19658 /* Ignore dropped */
19659 if (attribute->attisdropped)
19660 continue;
19661
19662 if (attribute->attidentity)
19663 ereport(ERROR,
19664 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19665 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19666 RelationGetRelationName(attachrel), attributeName),
19667 errdetail("The new partition may not contain an identity column."));
19668
19669 /* Try to find the column in parent (matching on column name) */
19670 if (!SearchSysCacheExists2(ATTNAME,
19672 CStringGetDatum(attributeName)))
19673 ereport(ERROR,
19674 (errcode(ERRCODE_DATATYPE_MISMATCH),
19675 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19676 RelationGetRelationName(attachrel), attributeName,
19678 errdetail("The new partition may contain only the columns present in parent.")));
19679 }
19680
19681 /*
19682 * If child_rel has row-level triggers with transition tables, we
19683 * currently don't allow it to become a partition. See also prohibitions
19684 * in ATExecAddInherit() and CreateTrigger().
19685 */
19686 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
19687 if (trigger_name != NULL)
19688 ereport(ERROR,
19689 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19690 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19691 trigger_name, RelationGetRelationName(attachrel)),
19692 errdetail("ROW triggers with transition tables are not supported on partitions.")));
19693
19694 /*
19695 * Check that the new partition's bound is valid and does not overlap any
19696 * of existing partitions of the parent - note that it does not return on
19697 * error.
19698 */
19700 cmd->bound, pstate);
19701
19702 /* OK to create inheritance. Rest of the checks performed there */
19703 CreateInheritance(attachrel, rel, true);
19704
19705 /* Update the pg_class entry. */
19706 StorePartitionBound(attachrel, rel, cmd->bound);
19707
19708 /* Ensure there exists a correct set of indexes in the partition. */
19709 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19710
19711 /* and triggers */
19712 CloneRowTriggersToPartition(rel, attachrel);
19713
19714 /*
19715 * Clone foreign key constraints. Callee is responsible for setting up
19716 * for phase 3 constraint verification.
19717 */
19718 CloneForeignKeyConstraints(wqueue, rel, attachrel);
19719
19720 /*
19721 * Generate partition constraint from the partition bound specification.
19722 * If the parent itself is a partition, make sure to include its
19723 * constraint as well.
19724 */
19725 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
19726
19727 /*
19728 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
19729 * since it's needed later to construct the constraint expression for
19730 * validating against the default partition, if any.
19731 */
19732 partConstraint = list_concat_copy(partBoundConstraint,
19734
19735 /* Skip validation if there are no constraints to validate. */
19736 if (partConstraint)
19737 {
19738 /*
19739 * Run the partition quals through const-simplification similar to
19740 * check constraints. We skip canonicalize_qual, though, because
19741 * partition quals should be in canonical form already.
19742 */
19743 partConstraint =
19745 (Node *) partConstraint);
19746
19747 /* XXX this sure looks wrong */
19748 partConstraint = list_make1(make_ands_explicit(partConstraint));
19749
19750 /*
19751 * Adjust the generated constraint to match this partition's attribute
19752 * numbers.
19753 */
19754 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
19755 rel);
19756
19757 /* Validate partition constraints against the table being attached. */
19758 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
19759 false);
19760 }
19761
19762 /*
19763 * If we're attaching a partition other than the default partition and a
19764 * default one exists, then that partition's partition constraint changes,
19765 * so add an entry to the work queue to validate it, too. (We must not do
19766 * this when the partition being attached is the default one; we already
19767 * did it above!)
19768 */
19769 if (OidIsValid(defaultPartOid))
19770 {
19771 Relation defaultrel;
19772 List *defPartConstraint;
19773
19774 Assert(!cmd->bound->is_default);
19775
19776 /* we already hold a lock on the default partition */
19777 defaultrel = table_open(defaultPartOid, NoLock);
19778 defPartConstraint =
19779 get_proposed_default_constraint(partBoundConstraint);
19780
19781 /*
19782 * Map the Vars in the constraint expression from rel's attnos to
19783 * defaultrel's.
19784 */
19785 defPartConstraint =
19786 map_partition_varattnos(defPartConstraint,
19787 1, defaultrel, rel);
19788 QueuePartitionConstraintValidation(wqueue, defaultrel,
19789 defPartConstraint, true);
19790
19791 /* keep our lock until commit. */
19792 table_close(defaultrel, NoLock);
19793 }
19794
19795 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
19796
19797 /*
19798 * If the partition we just attached is partitioned itself, invalidate
19799 * relcache for all descendent partitions too to ensure that their
19800 * rd_partcheck expression trees are rebuilt; partitions already locked at
19801 * the beginning of this function.
19802 */
19803 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19804 {
19805 ListCell *l;
19806
19807 foreach(l, attachrel_children)
19808 {
19810 }
19811 }
19812
19813 /* keep our lock until commit */
19814 table_close(attachrel, NoLock);
19815
19816 return address;
19817}
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2254
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition: heap.c:3961
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:20010
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:19828
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:10974
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
Definition: tablecmds.c:19432

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

20870{
20871 Relation partIdx;
20872 Relation partTbl;
20873 Relation parentTbl;
20874 ObjectAddress address;
20875 Oid partIdxId;
20876 Oid currParent;
20878
20879 /*
20880 * We need to obtain lock on the index 'name' to modify it, but we also
20881 * need to read its owning table's tuple descriptor -- so we need to lock
20882 * both. To avoid deadlocks, obtain lock on the table before doing so on
20883 * the index. Furthermore, we need to examine the parent table of the
20884 * partition, so lock that one too.
20885 */
20886 state.partitionOid = InvalidOid;
20887 state.parentTblOid = parentIdx->rd_index->indrelid;
20888 state.lockedParentTbl = false;
20889 partIdxId =
20892 &state);
20893 /* Not there? */
20894 if (!OidIsValid(partIdxId))
20895 ereport(ERROR,
20896 (errcode(ERRCODE_UNDEFINED_OBJECT),
20897 errmsg("index \"%s\" does not exist", name->relname)));
20898
20899 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20900 partIdx = relation_open(partIdxId, AccessExclusiveLock);
20901
20902 /* we already hold locks on both tables, so this is safe: */
20903 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
20904 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
20905
20906 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
20907
20908 /* Silently do nothing if already in the right state */
20909 currParent = partIdx->rd_rel->relispartition ?
20910 get_partition_parent(partIdxId, false) : InvalidOid;
20911 if (currParent != RelationGetRelid(parentIdx))
20912 {
20913 IndexInfo *childInfo;
20914 IndexInfo *parentInfo;
20915 AttrMap *attmap;
20916 bool found;
20917 int i;
20918 PartitionDesc partDesc;
20919 Oid constraintOid,
20920 cldConstrId = InvalidOid;
20921
20922 /*
20923 * If this partition already has an index attached, refuse the
20924 * operation.
20925 */
20926 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
20927
20928 if (OidIsValid(currParent))
20929 ereport(ERROR,
20930 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20931 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20932 RelationGetRelationName(partIdx),
20933 RelationGetRelationName(parentIdx)),
20934 errdetail("Index \"%s\" is already attached to another index.",
20935 RelationGetRelationName(partIdx))));
20936
20937 /* Make sure it indexes a partition of the other index's table */
20938 partDesc = RelationGetPartitionDesc(parentTbl, true);
20939 found = false;
20940 for (i = 0; i < partDesc->nparts; i++)
20941 {
20942 if (partDesc->oids[i] == state.partitionOid)
20943 {
20944 found = true;
20945 break;
20946 }
20947 }
20948 if (!found)
20949 ereport(ERROR,
20950 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20951 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20952 RelationGetRelationName(partIdx),
20953 RelationGetRelationName(parentIdx)),
20954 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20955 RelationGetRelationName(partIdx),
20956 RelationGetRelationName(parentTbl))));
20957
20958 /* Ensure the indexes are compatible */
20959 childInfo = BuildIndexInfo(partIdx);
20960 parentInfo = BuildIndexInfo(parentIdx);
20961 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
20962 RelationGetDescr(parentTbl),
20963 false);
20964 if (!CompareIndexInfo(childInfo, parentInfo,
20965 partIdx->rd_indcollation,
20966 parentIdx->rd_indcollation,
20967 partIdx->rd_opfamily,
20968 parentIdx->rd_opfamily,
20969 attmap))
20970 ereport(ERROR,
20971 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20972 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20973 RelationGetRelationName(partIdx),
20974 RelationGetRelationName(parentIdx)),
20975 errdetail("The index definitions do not match.")));
20976
20977 /*
20978 * If there is a constraint in the parent, make sure there is one in
20979 * the child too.
20980 */
20981 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
20982 RelationGetRelid(parentIdx));
20983
20984 if (OidIsValid(constraintOid))
20985 {
20987 partIdxId);
20988 if (!OidIsValid(cldConstrId))
20989 ereport(ERROR,
20990 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20991 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20992 RelationGetRelationName(partIdx),
20993 RelationGetRelationName(parentIdx)),
20994 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20995 RelationGetRelationName(parentIdx),
20996 RelationGetRelationName(parentTbl),
20997 RelationGetRelationName(partIdx))));
20998 }
20999
21000 /*
21001 * If it's a primary key, make sure the columns in the partition are
21002 * NOT NULL.
21003 */
21004 if (parentIdx->rd_index->indisprimary)
21005 verifyPartitionIndexNotNull(childInfo, partTbl);
21006
21007 /* All good -- do it */
21008 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21009 if (OidIsValid(constraintOid))
21010 ConstraintSetParentConstraint(cldConstrId, constraintOid,
21011 RelationGetRelid(partTbl));
21012
21013 free_attrmap(attmap);
21014
21015 validatePartitionedIndex(parentIdx, parentTbl);
21016 }
21017
21018 relation_close(parentTbl, AccessShareLock);
21019 /* keep these locks till commit */
21020 relation_close(partTbl, NoLock);
21021 relation_close(partIdx, NoLock);
21022
21023 return address;
21024}
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:4407
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 verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx)
Definition: tablecmds.c:21156
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:20815
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
Definition: tablecmds.c:21054
static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
Definition: tablecmds.c:21031
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 15339 of file tablecmds.c.

15340{
15341 Relation target_rel;
15342 Relation class_rel;
15343 HeapTuple tuple;
15344 Form_pg_class tuple_class;
15345
15346 /*
15347 * Get exclusive lock till end of transaction on the target table. Use
15348 * relation_open so that we can work on indexes and sequences.
15349 */
15350 target_rel = relation_open(relationOid, lockmode);
15351
15352 /* Get its pg_class tuple, too */
15353 class_rel = table_open(RelationRelationId, RowExclusiveLock);
15354
15355 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
15356 if (!HeapTupleIsValid(tuple))
15357 elog(ERROR, "cache lookup failed for relation %u", relationOid);
15358 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
15359
15360 /* Can we change the ownership of this tuple? */
15361 switch (tuple_class->relkind)
15362 {
15363 case RELKIND_RELATION:
15364 case RELKIND_VIEW:
15365 case RELKIND_MATVIEW:
15366 case RELKIND_FOREIGN_TABLE:
15367 case RELKIND_PARTITIONED_TABLE:
15368 /* ok to change owner */
15369 break;
15370 case RELKIND_INDEX:
15371 if (!recursing)
15372 {
15373 /*
15374 * Because ALTER INDEX OWNER used to be allowed, and in fact
15375 * is generated by old versions of pg_dump, we give a warning
15376 * and do nothing rather than erroring out. Also, to avoid
15377 * unnecessary chatter while restoring those old dumps, say
15378 * nothing at all if the command would be a no-op anyway.
15379 */
15380 if (tuple_class->relowner != newOwnerId)
15382 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15383 errmsg("cannot change owner of index \"%s\"",
15384 NameStr(tuple_class->relname)),
15385 errhint("Change the ownership of the index's table instead.")));
15386 /* quick hack to exit via the no-op path */
15387 newOwnerId = tuple_class->relowner;
15388 }
15389 break;
15390 case RELKIND_PARTITIONED_INDEX:
15391 if (recursing)
15392 break;
15393 ereport(ERROR,
15394 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15395 errmsg("cannot change owner of index \"%s\"",
15396 NameStr(tuple_class->relname)),
15397 errhint("Change the ownership of the index's table instead.")));
15398 break;
15399 case RELKIND_SEQUENCE:
15400 if (!recursing &&
15401 tuple_class->relowner != newOwnerId)
15402 {
15403 /* if it's an owned sequence, disallow changing it by itself */
15404 Oid tableId;
15405 int32 colId;
15406
15407 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
15408 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
15409 ereport(ERROR,
15410 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15411 errmsg("cannot change owner of sequence \"%s\"",
15412 NameStr(tuple_class->relname)),
15413 errdetail("Sequence \"%s\" is linked to table \"%s\".",
15414 NameStr(tuple_class->relname),
15415 get_rel_name(tableId))));
15416 }
15417 break;
15418 case RELKIND_COMPOSITE_TYPE:
15419 if (recursing)
15420 break;
15421 ereport(ERROR,
15422 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15423 errmsg("\"%s\" is a composite type",
15424 NameStr(tuple_class->relname)),
15425 /* translator: %s is an SQL ALTER command */
15426 errhint("Use %s instead.",
15427 "ALTER TYPE")));
15428 break;
15429 case RELKIND_TOASTVALUE:
15430 if (recursing)
15431 break;
15432 /* FALL THRU */
15433 default:
15434 ereport(ERROR,
15435 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15436 errmsg("cannot change owner of relation \"%s\"",
15437 NameStr(tuple_class->relname)),
15438 errdetail_relkind_not_supported(tuple_class->relkind)));
15439 }
15440
15441 /*
15442 * If the new owner is the same as the existing owner, consider the
15443 * command to have succeeded. This is for dump restoration purposes.
15444 */
15445 if (tuple_class->relowner != newOwnerId)
15446 {
15447 Datum repl_val[Natts_pg_class];
15448 bool repl_null[Natts_pg_class];
15449 bool repl_repl[Natts_pg_class];
15450 Acl *newAcl;
15451 Datum aclDatum;
15452 bool isNull;
15453 HeapTuple newtuple;
15454
15455 /* skip permission checks when recursing to index or toast table */
15456 if (!recursing)
15457 {
15458 /* Superusers can always do it */
15459 if (!superuser())
15460 {
15461 Oid namespaceOid = tuple_class->relnamespace;
15462 AclResult aclresult;
15463
15464 /* Otherwise, must be owner of the existing object */
15465 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
15467 RelationGetRelationName(target_rel));
15468
15469 /* Must be able to become new owner */
15470 check_can_set_role(GetUserId(), newOwnerId);
15471
15472 /* New owner must have CREATE privilege on namespace */
15473 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
15474 ACL_CREATE);
15475 if (aclresult != ACLCHECK_OK)
15476 aclcheck_error(aclresult, OBJECT_SCHEMA,
15477 get_namespace_name(namespaceOid));
15478 }
15479 }
15480
15481 memset(repl_null, false, sizeof(repl_null));
15482 memset(repl_repl, false, sizeof(repl_repl));
15483
15484 repl_repl[Anum_pg_class_relowner - 1] = true;
15485 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
15486
15487 /*
15488 * Determine the modified ACL for the new owner. This is only
15489 * necessary when the ACL is non-null.
15490 */
15491 aclDatum = SysCacheGetAttr(RELOID, tuple,
15492 Anum_pg_class_relacl,
15493 &isNull);
15494 if (!isNull)
15495 {
15496 newAcl = aclnewowner(DatumGetAclP(aclDatum),
15497 tuple_class->relowner, newOwnerId);
15498 repl_repl[Anum_pg_class_relacl - 1] = true;
15499 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
15500 }
15501
15502 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
15503
15504 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
15505
15506 heap_freetuple(newtuple);
15507
15508 /*
15509 * We must similarly update any per-column ACLs to reflect the new
15510 * owner; for neatness reasons that's split out as a subroutine.
15511 */
15512 change_owner_fix_column_acls(relationOid,
15513 tuple_class->relowner,
15514 newOwnerId);
15515
15516 /*
15517 * Update owner dependency reference, if any. A composite type has
15518 * none, because it's tracked for the pg_type entry instead of here;
15519 * indexes and TOAST tables don't have their own entries either.
15520 */
15521 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
15522 tuple_class->relkind != RELKIND_INDEX &&
15523 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
15524 tuple_class->relkind != RELKIND_TOASTVALUE)
15525 changeDependencyOnOwner(RelationRelationId, relationOid,
15526 newOwnerId);
15527
15528 /*
15529 * Also change the ownership of the table's row type, if it has one
15530 */
15531 if (OidIsValid(tuple_class->reltype))
15532 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
15533
15534 /*
15535 * If we are operating on a table or materialized view, also change
15536 * the ownership of any indexes and sequences that belong to the
15537 * relation, as well as its toast table (if it has one).
15538 */
15539 if (tuple_class->relkind == RELKIND_RELATION ||
15540 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
15541 tuple_class->relkind == RELKIND_MATVIEW ||
15542 tuple_class->relkind == RELKIND_TOASTVALUE)
15543 {
15544 List *index_oid_list;
15545 ListCell *i;
15546
15547 /* Find all the indexes belonging to this relation */
15548 index_oid_list = RelationGetIndexList(target_rel);
15549
15550 /* For each index, recursively change its ownership */
15551 foreach(i, index_oid_list)
15552 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
15553
15554 list_free(index_oid_list);
15555 }
15556
15557 /* If it has a toast table, recurse to change its ownership */
15558 if (tuple_class->reltoastrelid != InvalidOid)
15559 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
15560 true, lockmode);
15561
15562 /* If it has dependent sequences, recurse to change them too */
15563 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
15564 }
15565
15566 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
15567
15568 ReleaseSysCache(tuple);
15569 table_close(class_rel, RowExclusiveLock);
15570 relation_close(target_rel, NoLock);
15571}
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:2348
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:15339
static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
Definition: tablecmds.c:15645
static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
Definition: tablecmds.c:15580
void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
Definition: typecmds.c:3974

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

15715{
15716 Oid indexOid;
15717 ObjectAddress address;
15718
15719 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15720
15721 if (!OidIsValid(indexOid))
15722 ereport(ERROR,
15723 (errcode(ERRCODE_UNDEFINED_OBJECT),
15724 errmsg("index \"%s\" for table \"%s\" does not exist",
15725 indexName, RelationGetRelationName(rel))));
15726
15727 /* Check index is valid to cluster on */
15728 check_index_is_clusterable(rel, indexOid, lockmode);
15729
15730 /* And do the work */
15731 mark_index_clustered(rel, indexOid, false);
15732
15733 ObjectAddressSet(address,
15734 RelationRelationId, indexOid);
15735
15736 return address;
15737}
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 5337 of file tablecmds.c.

5340{
5342 Relation rel = tab->rel;
5343
5344 switch (cmd->subtype)
5345 {
5346 case AT_AddColumn: /* ADD COLUMN */
5347 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5348 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5349 cmd->recurse, false,
5350 lockmode, cur_pass, context);
5351 break;
5352 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5353 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5354 break;
5355 case AT_CookedColumnDefault: /* add a pre-cooked default */
5356 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5357 break;
5358 case AT_AddIdentity:
5359 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5360 cur_pass, context);
5361 Assert(cmd != NULL);
5362 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5363 break;
5364 case AT_SetIdentity:
5365 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5366 cur_pass, context);
5367 Assert(cmd != NULL);
5368 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5369 break;
5370 case AT_DropIdentity:
5371 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5372 break;
5373 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5374 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5375 break;
5376 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5377 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5378 cmd->recurse, false, lockmode);
5379 break;
5380 case AT_SetExpression:
5381 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5382 break;
5383 case AT_DropExpression:
5384 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5385 break;
5386 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5387 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5388 break;
5389 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5390 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5391 break;
5392 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5393 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5394 break;
5395 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5396 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5397 break;
5398 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5399 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5400 lockmode);
5401 break;
5402 case AT_DropColumn: /* DROP COLUMN */
5403 address = ATExecDropColumn(wqueue, rel, cmd->name,
5404 cmd->behavior, cmd->recurse, false,
5405 cmd->missing_ok, lockmode,
5406 NULL);
5407 break;
5408 case AT_AddIndex: /* ADD INDEX */
5409 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5410 lockmode);
5411 break;
5412 case AT_ReAddIndex: /* ADD INDEX */
5413 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5414 lockmode);
5415 break;
5416 case AT_ReAddStatistics: /* ADD STATISTICS */
5417 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5418 true, lockmode);
5419 break;
5420 case AT_AddConstraint: /* ADD CONSTRAINT */
5421 /* Transform the command only during initial examination */
5422 if (cur_pass == AT_PASS_ADD_CONSTR)
5423 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5424 cmd->recurse, lockmode,
5425 cur_pass, context);
5426 /* Depending on constraint type, might be no more work to do now */
5427 if (cmd != NULL)
5428 address =
5429 ATExecAddConstraint(wqueue, tab, rel,
5430 (Constraint *) cmd->def,
5431 cmd->recurse, false, lockmode);
5432 break;
5433 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5434 address =
5435 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5436 true, true, lockmode);
5437 break;
5438 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5439 * constraint */
5440 address =
5441 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5442 ((AlterDomainStmt *) cmd->def)->def,
5443 NULL);
5444 break;
5445 case AT_ReAddComment: /* Re-add existing comment */
5446 address = CommentObject((CommentStmt *) cmd->def);
5447 break;
5448 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5449 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5450 lockmode);
5451 break;
5452 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5453 address = ATExecAlterConstraint(wqueue, rel,
5455 cmd->recurse, lockmode);
5456 break;
5457 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5458 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5459 false, lockmode);
5460 break;
5461 case AT_DropConstraint: /* DROP CONSTRAINT */
5462 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5463 cmd->recurse,
5464 cmd->missing_ok, lockmode);
5465 break;
5466 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5467 /* parse transformation was done earlier */
5468 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5469 break;
5470 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5471 address =
5473 (List *) cmd->def, lockmode);
5474 break;
5475 case AT_ChangeOwner: /* ALTER OWNER */
5477 get_rolespec_oid(cmd->newowner, false),
5478 false, lockmode);
5479 break;
5480 case AT_ClusterOn: /* CLUSTER ON */
5481 address = ATExecClusterOn(rel, cmd->name, lockmode);
5482 break;
5483 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5484 ATExecDropCluster(rel, lockmode);
5485 break;
5486 case AT_SetLogged: /* SET LOGGED */
5487 case AT_SetUnLogged: /* SET UNLOGGED */
5488 break;
5489 case AT_DropOids: /* SET WITHOUT OIDS */
5490 /* nothing to do here, oid columns don't exist anymore */
5491 break;
5492 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5493
5494 /*
5495 * Only do this for partitioned tables, for which this is just a
5496 * catalog change. Tables with storage are handled by Phase 3.
5497 */
5498 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5499 tab->chgAccessMethod)
5501 break;
5502 case AT_SetTableSpace: /* SET TABLESPACE */
5503
5504 /*
5505 * Only do this for partitioned tables and indexes, for which this
5506 * is just a catalog change. Other relation types which have
5507 * storage are handled by Phase 3.
5508 */
5509 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5510 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5512
5513 break;
5514 case AT_SetRelOptions: /* SET (...) */
5515 case AT_ResetRelOptions: /* RESET (...) */
5516 case AT_ReplaceRelOptions: /* replace entire option list */
5517 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5518 break;
5519 case AT_EnableTrig: /* ENABLE TRIGGER name */
5522 cmd->recurse,
5523 lockmode);
5524 break;
5525 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5527 TRIGGER_FIRES_ALWAYS, false,
5528 cmd->recurse,
5529 lockmode);
5530 break;
5531 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5534 cmd->recurse,
5535 lockmode);
5536 break;
5537 case AT_DisableTrig: /* DISABLE TRIGGER name */
5539 TRIGGER_DISABLED, false,
5540 cmd->recurse,
5541 lockmode);
5542 break;
5543 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5546 cmd->recurse,
5547 lockmode);
5548 break;
5549 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5551 TRIGGER_DISABLED, false,
5552 cmd->recurse,
5553 lockmode);
5554 break;
5555 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5558 cmd->recurse,
5559 lockmode);
5560 break;
5561 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5563 TRIGGER_DISABLED, true,
5564 cmd->recurse,
5565 lockmode);
5566 break;
5567
5568 case AT_EnableRule: /* ENABLE RULE name */
5569 ATExecEnableDisableRule(rel, cmd->name,
5570 RULE_FIRES_ON_ORIGIN, lockmode);
5571 break;
5572 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5573 ATExecEnableDisableRule(rel, cmd->name,
5574 RULE_FIRES_ALWAYS, lockmode);
5575 break;
5576 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5577 ATExecEnableDisableRule(rel, cmd->name,
5578 RULE_FIRES_ON_REPLICA, lockmode);
5579 break;
5580 case AT_DisableRule: /* DISABLE RULE name */
5581 ATExecEnableDisableRule(rel, cmd->name,
5582 RULE_DISABLED, lockmode);
5583 break;
5584
5585 case AT_AddInherit:
5586 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5587 break;
5588 case AT_DropInherit:
5589 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5590 break;
5591 case AT_AddOf:
5592 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5593 break;
5594 case AT_DropOf:
5595 ATExecDropOf(rel, lockmode);
5596 break;
5597 case AT_ReplicaIdentity:
5598 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5599 break;
5601 ATExecSetRowSecurity(rel, true);
5602 break;
5604 ATExecSetRowSecurity(rel, false);
5605 break;
5608 break;
5611 break;
5612 case AT_GenericOptions:
5613 ATExecGenericOptions(rel, (List *) cmd->def);
5614 break;
5615 case AT_AttachPartition:
5616 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5617 cur_pass, context);
5618 Assert(cmd != NULL);
5619 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5620 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5621 context);
5622 else
5623 address = ATExecAttachPartitionIdx(wqueue, rel,
5624 ((PartitionCmd *) cmd->def)->name);
5625 break;
5626 case AT_DetachPartition:
5627 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5628 cur_pass, context);
5629 Assert(cmd != NULL);
5630 /* ATPrepCmd ensures it must be a table */
5631 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5632 address = ATExecDetachPartition(wqueue, tab, rel,
5633 ((PartitionCmd *) cmd->def)->name,
5634 ((PartitionCmd *) cmd->def)->concurrent);
5635 break;
5637 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5638 break;
5639 default: /* oops */
5640 elog(ERROR, "unrecognized alter table type: %d",
5641 (int) cmd->subtype);
5642 break;
5643 }
5644
5645 /*
5646 * Report the subcommand to interested event triggers.
5647 */
5648 if (cmd)
5649 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5650
5651 /*
5652 * Bump the command counter to ensure the next subcommand in the sequence
5653 * can see the changes so far
5654 */
5656}
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:2480
DropBehavior behavior
Definition: parsenodes.h:2483
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:15222
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:16528
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:17625
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:11916
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:8661
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition: tablecmds.c:8013
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8350
static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:12329
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:7643
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9586
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
Definition: tablecmds.c:20167
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:17901
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:16471
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
Definition: tablecmds.c:8910
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:9143
static void ATExecSetRowSecurity(Relation rel, bool rls)
Definition: tablecmds.c:17871
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition: tablecmds.c:8098
static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
Definition: tablecmds.c:15912
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8233
static void ATExecGenericOptions(Relation rel, List *options)
Definition: tablecmds.c:17930
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8765
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode)
Definition: tablecmds.c:14016
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:15746
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
Definition: tablecmds.c:8464
static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
Definition: tablecmds.c:17483
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17092
static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:9494
static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:17757
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:20665
static ObjectAddress ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:9052
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:19505
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
Definition: tablecmds.c:15792
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:13305
static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9410
static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:18011
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:16213
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:20869
static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9473
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
Definition: tablecmds.c:15714
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:16489
#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:2923

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

8015{
8016 TupleDesc tupdesc = RelationGetDescr(rel);
8018 ObjectAddress address;
8019
8020 /*
8021 * get the number of the attribute
8022 */
8023 attnum = get_attnum(RelationGetRelid(rel), colName);
8025 ereport(ERROR,
8026 (errcode(ERRCODE_UNDEFINED_COLUMN),
8027 errmsg("column \"%s\" of relation \"%s\" does not exist",
8028 colName, RelationGetRelationName(rel))));
8029
8030 /* Prevent them from altering a system attribute */
8031 if (attnum <= 0)
8032 ereport(ERROR,
8033 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8034 errmsg("cannot alter system column \"%s\"",
8035 colName)));
8036
8037 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8038 ereport(ERROR,
8039 (errcode(ERRCODE_SYNTAX_ERROR),
8040 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8041 colName, RelationGetRelationName(rel)),
8042 /* translator: %s is an SQL ALTER command */
8043 newDefault ? 0 : errhint("Use %s instead.",
8044 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8045
8046 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8047 ereport(ERROR,
8048 (errcode(ERRCODE_SYNTAX_ERROR),
8049 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8050 colName, RelationGetRelationName(rel)),
8051 newDefault ?
8052 /* translator: %s is an SQL ALTER command */
8053 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8054 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8055 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8056
8057 /*
8058 * Remove any old default for the column. We use RESTRICT here for
8059 * safety, but at present we do not expect anything to depend on the
8060 * default.
8061 *
8062 * We treat removing the existing default as an internal operation when it
8063 * is preparatory to adding a new default, but as a user-initiated
8064 * operation when the user asked for a drop.
8065 */
8067 newDefault != NULL);
8068
8069 if (newDefault)
8070 {
8071 /* SET DEFAULT */
8072 RawColumnDefault *rawEnt;
8073
8074 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8075 rawEnt->attnum = attnum;
8076 rawEnt->raw_default = newDefault;
8077 rawEnt->generated = '\0';
8078
8079 /*
8080 * This function is intended for CREATE TABLE, so it processes a
8081 * _list_ of defaults, but we just do one.
8082 */
8084 false, true, false, NULL);
8085 }
8086
8087 ObjectAddressSubSet(address, RelationRelationId,
8088 RelationGetRelid(rel), attnum);
8089 return address;
8090}
#define InvalidAttrNumber
Definition: attnum.h:23
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:893

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

8100{
8101 ObjectAddress address;
8102
8103 /* We assume no checking is required */
8104
8105 /*
8106 * Remove any old default for the column. We use RESTRICT here for
8107 * safety, but at present we do not expect anything to depend on the
8108 * default. (In ordinary cases, there could not be a default in place
8109 * anyway, but it's possible when combining LIKE with inheritance.)
8110 */
8112 true);
8113
8114 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8115
8116 ObjectAddressSubSet(address, RelationRelationId,
8117 RelationGetRelid(rel), attnum);
8118 return address;
8119}

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

20169{
20170 Relation partRel;
20171 ObjectAddress address;
20172 Oid defaultPartOid;
20173
20174 /*
20175 * We must lock the default partition, because detaching this partition
20176 * will change its partition constraint.
20177 */
20178 defaultPartOid =
20180 if (OidIsValid(defaultPartOid))
20181 {
20182 /*
20183 * Concurrent detaching when a default partition exists is not
20184 * supported. The main problem is that the default partition
20185 * constraint would change. And there's a definitional problem: what
20186 * should happen to the tuples that are being inserted that belong to
20187 * the partition being detached? Putting them on the partition being
20188 * detached would be wrong, since they'd become "lost" after the
20189 * detaching completes but we cannot put them in the default partition
20190 * either until we alter its partition constraint.
20191 *
20192 * I think we could solve this problem if we effected the constraint
20193 * change before committing the first transaction. But the lock would
20194 * have to remain AEL and it would cause concurrent query planning to
20195 * be blocked, so changing it that way would be even worse.
20196 */
20197 if (concurrent)
20198 ereport(ERROR,
20199 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20200 errmsg("cannot detach partitions concurrently when a default partition exists")));
20201 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20202 }
20203
20204 /*
20205 * In concurrent mode, the partition is locked with share-update-exclusive
20206 * in the first transaction. This allows concurrent transactions to be
20207 * doing DML to the partition.
20208 */
20209 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20211
20212 /*
20213 * Check inheritance conditions and either delete the pg_inherits row (in
20214 * non-concurrent mode) or just set the inhdetachpending flag.
20215 */
20216 if (!concurrent)
20217 RemoveInheritance(partRel, rel, false);
20218 else
20219 MarkInheritDetached(partRel, rel);
20220
20221 /*
20222 * Ensure that foreign keys still hold after this detach. This keeps
20223 * locks on the referencing tables, which prevents concurrent transactions
20224 * from adding rows that we wouldn't see. For this to work in concurrent
20225 * mode, it is critical that the partition appears as no longer attached
20226 * for the RI queries as soon as the first transaction commits.
20227 */
20229
20230 /*
20231 * Concurrent mode has to work harder; first we add a new constraint to
20232 * the partition that matches the partition constraint. Then we close our
20233 * existing transaction, and in a new one wait for all processes to catch
20234 * up on the catalog updates we've done so far; at that point we can
20235 * complete the operation.
20236 */
20237 if (concurrent)
20238 {
20239 Oid partrelid,
20240 parentrelid;
20241 LOCKTAG tag;
20242 char *parentrelname;
20243 char *partrelname;
20244
20245 /*
20246 * Add a new constraint to the partition being detached, which
20247 * supplants the partition constraint (unless there is one already).
20248 */
20249 DetachAddConstraintIfNeeded(wqueue, partRel);
20250
20251 /*
20252 * We're almost done now; the only traces that remain are the
20253 * pg_inherits tuple and the partition's relpartbounds. Before we can
20254 * remove those, we need to wait until all transactions that know that
20255 * this is a partition are gone.
20256 */
20257
20258 /*
20259 * Remember relation OIDs to re-acquire them later; and relation names
20260 * too, for error messages if something is dropped in between.
20261 */
20262 partrelid = RelationGetRelid(partRel);
20263 parentrelid = RelationGetRelid(rel);
20264 parentrelname = MemoryContextStrdup(PortalContext,
20266 partrelname = MemoryContextStrdup(PortalContext,
20267 RelationGetRelationName(partRel));
20268
20269 /* Invalidate relcache entries for the parent -- must be before close */
20271
20272 table_close(partRel, NoLock);
20273 table_close(rel, NoLock);
20274 tab->rel = NULL;
20275
20276 /* Make updated catalog entry visible */
20279
20281
20282 /*
20283 * Now wait. This ensures that all queries that were planned
20284 * including the partition are finished before we remove the rest of
20285 * catalog entries. We don't need or indeed want to acquire this
20286 * lock, though -- that would block later queries.
20287 *
20288 * We don't need to concern ourselves with waiting for a lock on the
20289 * partition itself, since we will acquire AccessExclusiveLock below.
20290 */
20291 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20293
20294 /*
20295 * Now acquire locks in both relations again. Note they may have been
20296 * removed in the meantime, so care is required.
20297 */
20298 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20299 partRel = try_relation_open(partrelid, AccessExclusiveLock);
20300
20301 /* If the relations aren't there, something bad happened; bail out */
20302 if (rel == NULL)
20303 {
20304 if (partRel != NULL) /* shouldn't happen */
20305 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
20306 partrelname);
20307 ereport(ERROR,
20308 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20309 errmsg("partitioned table \"%s\" was removed concurrently",
20310 parentrelname)));
20311 }
20312 if (partRel == NULL)
20313 ereport(ERROR,
20314 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20315 errmsg("partition \"%s\" was removed concurrently", partrelname)));
20316
20317 tab->rel = rel;
20318 }
20319
20320 /* Do the final part of detaching */
20321 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
20322
20323 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20324
20325 /* keep our lock until commit */
20326 table_close(partRel, NoLock);
20327
20328 return address;
20329}
Oid MyDatabaseId
Definition: globals.c:93
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:1686
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:17134
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:17217
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:20338
static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
Definition: tablecmds.c:20700
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21231
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 20665 of file tablecmds.c.

20666{
20667 Relation partRel;
20668 ObjectAddress address;
20669 Snapshot snap = GetActiveSnapshot();
20670
20672
20673 /*
20674 * Wait until existing snapshots are gone. This is important if the
20675 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20676 * user could immediately run DETACH FINALIZE without actually waiting for
20677 * existing transactions. We must not complete the detach action until
20678 * all such queries are complete (otherwise we would present them with an
20679 * inconsistent view of catalogs).
20680 */
20681 WaitForOlderSnapshots(snap->xmin, false);
20682
20683 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20684
20685 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20686
20687 table_close(partRel, NoLock);
20688
20689 return address;
20690}
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 15746 of file tablecmds.c.

15747{
15748 mark_index_clustered(rel, InvalidOid, false);
15749}

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

9148{
9149 HeapTuple tuple;
9150 Form_pg_attribute targetatt;
9152 List *children;
9153 ObjectAddress object;
9154 bool is_expr;
9155
9156 /* At top level, permission check was done in ATPrepCmd, else do it */
9157 if (recursing)
9160
9161 /* Initialize addrs on the first invocation */
9162 Assert(!recursing || addrs != NULL);
9163
9164 /* since this function recurses, it could be driven to stack overflow */
9166
9167 if (!recursing)
9168 addrs = new_object_addresses();
9169
9170 /*
9171 * get the number of the attribute
9172 */
9173 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9174 if (!HeapTupleIsValid(tuple))
9175 {
9176 if (!missing_ok)
9177 {
9178 ereport(ERROR,
9179 (errcode(ERRCODE_UNDEFINED_COLUMN),
9180 errmsg("column \"%s\" of relation \"%s\" does not exist",
9181 colName, RelationGetRelationName(rel))));
9182 }
9183 else
9184 {
9186 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9187 colName, RelationGetRelationName(rel))));
9188 return InvalidObjectAddress;
9189 }
9190 }
9191 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9192
9193 attnum = targetatt->attnum;
9194
9195 /* Can't drop a system attribute */
9196 if (attnum <= 0)
9197 ereport(ERROR,
9198 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9199 errmsg("cannot drop system column \"%s\"",
9200 colName)));
9201
9202 /*
9203 * Don't drop inherited columns, unless recursing (presumably from a drop
9204 * of the parent column)
9205 */
9206 if (targetatt->attinhcount > 0 && !recursing)
9207 ereport(ERROR,
9208 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9209 errmsg("cannot drop inherited column \"%s\"",
9210 colName)));
9211
9212 /*
9213 * Don't drop columns used in the partition key, either. (If we let this
9214 * go through, the key column's dependencies would cause a cascaded drop
9215 * of the whole table, which is surely not what the user expected.)
9216 */
9217 if (has_partition_attrs(rel,
9219 &is_expr))
9220 ereport(ERROR,
9221 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9222 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9223 colName, RelationGetRelationName(rel))));
9224
9225 ReleaseSysCache(tuple);
9226
9227 /*
9228 * Propagate to children as appropriate. Unlike most other ALTER
9229 * routines, we have to do this one level of recursion at a time; we can't
9230 * use find_all_inheritors to do it in one pass.
9231 */
9232 children =
9234
9235 if (children)
9236 {
9237 Relation attr_rel;
9238 ListCell *child;
9239
9240 /*
9241 * In case of a partitioned table, the column must be dropped from the
9242 * partitions as well.
9243 */
9244 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9245 ereport(ERROR,
9246 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9247 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9248 errhint("Do not specify the ONLY keyword.")));
9249
9250 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9251 foreach(child, children)
9252 {
9253 Oid childrelid = lfirst_oid(child);
9254 Relation childrel;
9255 Form_pg_attribute childatt;
9256
9257 /* find_inheritance_children already got lock */
9258 childrel = table_open(childrelid, NoLock);
9259 CheckAlterTableIsSafe(childrel);
9260
9261 tuple = SearchSysCacheCopyAttName(childrelid, colName);
9262 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9263 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9264 colName, childrelid);
9265 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9266
9267 if (childatt->attinhcount <= 0) /* shouldn't happen */
9268 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9269 childrelid, colName);
9270
9271 if (recurse)
9272 {
9273 /*
9274 * If the child column has other definition sources, just
9275 * decrement its inheritance count; if not, recurse to delete
9276 * it.
9277 */
9278 if (childatt->attinhcount == 1 && !childatt->attislocal)
9279 {
9280 /* Time to delete this child column, too */
9281 ATExecDropColumn(wqueue, childrel, colName,
9282 behavior, true, true,
9283 false, lockmode, addrs);
9284 }
9285 else
9286 {
9287 /* Child column must survive my deletion */
9288 childatt->attinhcount--;
9289
9290 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9291
9292 /* Make update visible */
9294 }
9295 }
9296 else
9297 {
9298 /*
9299 * If we were told to drop ONLY in this table (no recursion),
9300 * we need to mark the inheritors' attributes as locally
9301 * defined rather than inherited.
9302 */
9303 childatt->attinhcount--;
9304 childatt->attislocal = true;
9305
9306 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9307
9308 /* Make update visible */
9310 }
9311
9312 heap_freetuple(tuple);
9313
9314 table_close(childrel, NoLock);
9315 }
9316 table_close(attr_rel, RowExclusiveLock);
9317 }
9318
9319 /* Add object to delete */
9320 object.classId = RelationRelationId;
9321 object.objectId = RelationGetRelid(rel);
9322 object.objectSubId = attnum;
9323 add_exact_object_address(&object, addrs);
9324
9325 if (!recursing)
9326 {
9327 /* Recursion has ended, drop everything that was collected */
9328 performMultipleDeletions(addrs, behavior, 0);
9329 free_object_addresses(addrs);
9330 }
9331
9332 return object;
9333}
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 13305 of file tablecmds.c.

13308{
13309 Relation conrel;
13310 SysScanDesc scan;
13311 ScanKeyData skey[3];
13312 HeapTuple tuple;
13313 bool found = false;
13314
13315 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13316
13317 /*
13318 * Find and drop the target constraint
13319 */
13320 ScanKeyInit(&skey[0],
13321 Anum_pg_constraint_conrelid,
13322 BTEqualStrategyNumber, F_OIDEQ,
13324 ScanKeyInit(&skey[1],
13325 Anum_pg_constraint_contypid,
13326 BTEqualStrategyNumber, F_OIDEQ,
13328 ScanKeyInit(&skey[2],
13329 Anum_pg_constraint_conname,
13330 BTEqualStrategyNumber, F_NAMEEQ,
13331 CStringGetDatum(constrName));
13332 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13333 true, NULL, 3, skey);
13334
13335 /* There can be at most one matching row */
13336 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
13337 {
13338 dropconstraint_internal(rel, tuple, behavior, recurse, false,
13339 missing_ok, lockmode);
13340 found = true;
13341 }
13342
13343 systable_endscan(scan);
13344
13345 if (!found)
13346 {
13347 if (!missing_ok)
13348 ereport(ERROR,
13349 errcode(ERRCODE_UNDEFINED_OBJECT),
13350 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13351 constrName, RelationGetRelationName(rel)));
13352 else
13354 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
13355 constrName, RelationGetRelationName(rel)));
13356 }
13357
13359}
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:13370

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

8662{
8663 HeapTuple tuple;
8664 Form_pg_attribute attTup;
8666 Relation attrelation;
8667 Oid attrdefoid;
8668 ObjectAddress address;
8669
8670 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8671 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8672 if (!HeapTupleIsValid(tuple))
8673 ereport(ERROR,
8674 (errcode(ERRCODE_UNDEFINED_COLUMN),
8675 errmsg("column \"%s\" of relation \"%s\" does not exist",
8676 colName, RelationGetRelationName(rel))));
8677
8678 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8679 attnum = attTup->attnum;
8680
8681 if (attnum <= 0)
8682 ereport(ERROR,
8683 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8684 errmsg("cannot alter system column \"%s\"",
8685 colName)));
8686
8687 /*
8688 * TODO: This could be done, but it would need a table rewrite to
8689 * materialize the generated values. Note that for the time being, we
8690 * still error with missing_ok, so that we don't silently leave the column
8691 * as generated.
8692 */
8693 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8694 ereport(ERROR,
8695 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8696 errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8697 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8698 colName, RelationGetRelationName(rel))));
8699
8700 if (!attTup->attgenerated)
8701 {
8702 if (!missing_ok)
8703 ereport(ERROR,
8704 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8705 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8706 colName, RelationGetRelationName(rel))));
8707 else
8708 {
8710 (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8711 colName, RelationGetRelationName(rel))));
8712 heap_freetuple(tuple);
8713 table_close(attrelation, RowExclusiveLock);
8714 return InvalidObjectAddress;
8715 }
8716 }
8717
8718 /*
8719 * Mark the column as no longer generated. (The atthasdef flag needs to
8720 * get cleared too, but RemoveAttrDefault will handle that.)
8721 */
8722 attTup->attgenerated = '\0';
8723 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8724
8725 InvokeObjectPostAlterHook(RelationRelationId,
8726 RelationGetRelid(rel),
8727 attnum);
8728 heap_freetuple(tuple);
8729
8730 table_close(attrelation, RowExclusiveLock);
8731
8732 /*
8733 * Drop the dependency records of the GENERATED expression, in particular
8734 * its INTERNAL dependency on the column, which would otherwise cause
8735 * dependency.c to refuse to perform the deletion.
8736 */
8737 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8738 if (!OidIsValid(attrdefoid))
8739 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8740 RelationGetRelid(rel), attnum);
8741 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8742
8743 /* Make above changes visible */
8745
8746 /*
8747 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8748 * safety, but at present we do not expect anything to depend on the
8749 * default.
8750 */
8752 false, false);
8753
8754 ObjectAddressSubSet(address, RelationRelationId,
8755 RelationGetRelid(rel), attnum);
8756 return address;
8757}

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

8352{
8353 HeapTuple tuple;
8354 Form_pg_attribute attTup;
8356 Relation attrelation;
8357 ObjectAddress address;
8358 Oid seqid;
8359 ObjectAddress seqaddress;
8360 bool ispartitioned;
8361
8362 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8363 if (ispartitioned && !recurse)
8364 ereport(ERROR,
8365 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8366 errmsg("cannot drop identity from a column of only the partitioned table"),
8367 errhint("Do not specify the ONLY keyword.")));
8368
8369 if (rel->rd_rel->relispartition && !recursing)
8370 ereport(ERROR,
8371 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8372 errmsg("cannot drop identity from a column of a partition"));
8373
8374 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8375 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8376 if (!HeapTupleIsValid(tuple))
8377 ereport(ERROR,
8378 (errcode(ERRCODE_UNDEFINED_COLUMN),
8379 errmsg("column \"%s\" of relation \"%s\" does not exist",
8380 colName, RelationGetRelationName(rel))));
8381
8382 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8383 attnum = attTup->attnum;
8384
8385 if (attnum <= 0)
8386 ereport(ERROR,
8387 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8388 errmsg("cannot alter system column \"%s\"",
8389 colName)));
8390
8391 if (!attTup->attidentity)
8392 {
8393 if (!missing_ok)
8394 ereport(ERROR,
8395 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8396 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8397 colName, RelationGetRelationName(rel))));
8398 else
8399 {
8401 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8402 colName, RelationGetRelationName(rel))));
8403 heap_freetuple(tuple);
8404 table_close(attrelation, RowExclusiveLock);
8405 return InvalidObjectAddress;
8406 }
8407 }
8408
8409 attTup->attidentity = '\0';
8410 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8411
8412 InvokeObjectPostAlterHook(RelationRelationId,
8413 RelationGetRelid(rel),
8414 attTup->attnum);
8415 ObjectAddressSubSet(address, RelationRelationId,
8416 RelationGetRelid(rel), attnum);
8417 heap_freetuple(tuple);
8418
8419 table_close(attrelation, RowExclusiveLock);
8420
8421 /*
8422 * Recurse to drop the identity from column in partitions. Identity is
8423 * not inherited in regular inheritance children so ignore them.
8424 */
8425 if (recurse && ispartitioned)
8426 {
8427 List *children;
8428 ListCell *lc;
8429
8430 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8431
8432 foreach(lc, children)
8433 {
8434 Relation childrel;
8435
8436 childrel = table_open(lfirst_oid(lc), NoLock);
8437 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8438 table_close(childrel, NoLock);
8439 }
8440 }
8441
8442 if (!recursing)
8443 {
8444 /* drop the internal sequence */
8445 seqid = getIdentitySequence(rel, attnum, false);
8446 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8447 RelationRelationId, DEPENDENCY_INTERNAL);
8449 seqaddress.classId = RelationRelationId;
8450 seqaddress.objectId = seqid;
8451 seqaddress.objectSubId = 0;
8453 }
8454
8455 return address;
8456}
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 17092 of file tablecmds.c.

17093{
17094 ObjectAddress address;
17095 Relation parent_rel;
17096
17097 if (rel->rd_rel->relispartition)
17098 ereport(ERROR,
17099 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17100 errmsg("cannot change inheritance of a partition")));
17101
17102 /*
17103 * AccessShareLock on the parent is probably enough, seeing that DROP
17104 * TABLE doesn't lock parent tables at all. We need some lock since we'll
17105 * be inspecting the parent's schema.
17106 */
17107 parent_rel = table_openrv(parent, AccessShareLock);
17108
17109 /*
17110 * We don't bother to check ownership of the parent table --- ownership of
17111 * the child is presumed enough rights.
17112 */
17113
17114 /* Off to RemoveInheritance() where most of the work happens */
17115 RemoveInheritance(rel, parent_rel, false);
17116
17117 ObjectAddressSet(address, RelationRelationId,
17118 RelationGetRelid(parent_rel));
17119
17120 /* keep our lock on the parent relation until commit */
17121 table_close(parent_rel, NoLock);
17122
17123 return address;
17124}

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

7645{
7646 HeapTuple tuple;
7647 HeapTuple conTup;
7648 Form_pg_attribute attTup;
7650 Relation attr_rel;
7651 ObjectAddress address;
7652
7653 /*
7654 * lookup the attribute
7655 */
7656 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7657
7658 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7659 if (!HeapTupleIsValid(tuple))
7660 ereport(ERROR,
7661 (errcode(ERRCODE_UNDEFINED_COLUMN),
7662 errmsg("column \"%s\" of relation \"%s\" does not exist",
7663 colName, RelationGetRelationName(rel))));
7664 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7665 attnum = attTup->attnum;
7666 ObjectAddressSubSet(address, RelationRelationId,
7667 RelationGetRelid(rel), attnum);
7668
7669 /* If the column is already nullable there's nothing to do. */
7670 if (!attTup->attnotnull)
7671 {
7672 table_close(attr_rel, RowExclusiveLock);
7673 return InvalidObjectAddress;
7674 }
7675
7676 /* Prevent them from altering a system attribute */
7677 if (attnum <= 0)
7678 ereport(ERROR,
7679 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7680 errmsg("cannot alter system column \"%s\"",
7681 colName)));
7682
7683 if (attTup->attidentity)
7684 ereport(ERROR,
7685 (errcode(ERRCODE_SYNTAX_ERROR),
7686 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7687 colName, RelationGetRelationName(rel))));
7688
7689 /*
7690 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7691 */
7692 if (rel->rd_rel->relispartition)
7693 {
7694 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7695 Relation parent = table_open(parentId, AccessShareLock);
7696 TupleDesc tupDesc = RelationGetDescr(parent);
7697 AttrNumber parent_attnum;
7698
7699 parent_attnum = get_attnum(parentId, colName);
7700 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7701 ereport(ERROR,
7702 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7703 errmsg("column \"%s\" is marked NOT NULL in parent table",
7704 colName)));
7706 }
7707
7708 /*
7709 * Find the constraint that makes this column NOT NULL, and drop it.
7710 * dropconstraint_internal() resets attnotnull.
7711 */
7713 if (conTup == NULL)
7714 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7715 colName, RelationGetRelationName(rel));
7716
7717 /* The normal case: we have a pg_constraint row, remove it */
7718 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7719 false, lockmode);
7720 heap_freetuple(conTup);
7721
7722 InvokeObjectPostAlterHook(RelationRelationId,
7723 RelationGetRelid(rel), attnum);
7724
7725 table_close(attr_rel, RowExclusiveLock);
7726
7727 return address;
7728}
bool attnotnull
Definition: pg_attribute.h:121
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 17625 of file tablecmds.c.

17626{
17627 Oid relid = RelationGetRelid(rel);
17628 Relation relationRelation;
17629 HeapTuple tuple;
17630
17631 if (!OidIsValid(rel->rd_rel->reloftype))
17632 ereport(ERROR,
17633 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17634 errmsg("\"%s\" is not a typed table",
17636
17637 /*
17638 * We don't bother to check ownership of the type --- ownership of the
17639 * table is presumed enough rights. No lock required on the type, either.
17640 */
17641
17642 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17644
17645 /* Clear pg_class.reloftype */
17646 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17647 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17648 if (!HeapTupleIsValid(tuple))
17649 elog(ERROR, "cache lookup failed for relation %u", relid);
17650 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17651 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17652
17653 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17654
17655 heap_freetuple(tuple);
17656 table_close(relationRelation, RowExclusiveLock);
17657}

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

16491{
16492 EnableDisableRule(rel, rulename, fires_when);
16493
16494 InvokeObjectPostAlterHook(RelationRelationId,
16495 RelationGetRelid(rel), 0);
16496}
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 16471 of file tablecmds.c.

16474{
16475 EnableDisableTrigger(rel, trigname, InvalidOid,
16476 fires_when, skip_system, recurse,
16477 lockmode);
16478
16479 InvokeObjectPostAlterHook(RelationRelationId,
16480 RelationGetRelid(rel), 0);
16481}
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 17901 of file tablecmds.c.

17902{
17903 Relation pg_class;
17904 Oid relid;
17905 HeapTuple tuple;
17906
17907 relid = RelationGetRelid(rel);
17908
17909 pg_class = table_open(RelationRelationId, RowExclusiveLock);
17910
17911 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17912
17913 if (!HeapTupleIsValid(tuple))
17914 elog(ERROR, "cache lookup failed for relation %u", relid);
17915
17916 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
17917 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17918
17919 InvokeObjectPostAlterHook(RelationRelationId,
17920 RelationGetRelid(rel), 0);
17921
17922 table_close(pg_class, RowExclusiveLock);
17923 heap_freetuple(tuple);
17924}

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

17931{
17932 Relation ftrel;
17933 ForeignServer *server;
17934 ForeignDataWrapper *fdw;
17935 HeapTuple tuple;
17936 bool isnull;
17937 Datum repl_val[Natts_pg_foreign_table];
17938 bool repl_null[Natts_pg_foreign_table];
17939 bool repl_repl[Natts_pg_foreign_table];
17940 Datum datum;
17941 Form_pg_foreign_table tableform;
17942
17943 if (options == NIL)
17944 return;
17945
17946 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
17947
17948 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
17949 ObjectIdGetDatum(rel->rd_id));
17950 if (!HeapTupleIsValid(tuple))
17951 ereport(ERROR,
17952 (errcode(ERRCODE_UNDEFINED_OBJECT),
17953 errmsg("foreign table \"%s\" does not exist",
17955 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
17956 server = GetForeignServer(tableform->ftserver);
17957 fdw = GetForeignDataWrapper(server->fdwid);
17958
17959 memset(repl_val, 0, sizeof(repl_val));
17960 memset(repl_null, false, sizeof(repl_null));
17961 memset(repl_repl, false, sizeof(repl_repl));
17962
17963 /* Extract the current options */
17964 datum = SysCacheGetAttr(FOREIGNTABLEREL,
17965 tuple,
17966 Anum_pg_foreign_table_ftoptions,
17967 &isnull);
17968 if (isnull)
17969 datum = PointerGetDatum(NULL);
17970
17971 /* Transform the options */
17972 datum = transformGenericOptions(ForeignTableRelationId,
17973 datum,
17974 options,
17975 fdw->fdwvalidator);
17976
17977 if (PointerIsValid(DatumGetPointer(datum)))
17978 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
17979 else
17980 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
17981
17982 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
17983
17984 /* Everything looks good - update the tuple */
17985
17986 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
17987 repl_val, repl_null, repl_repl);
17988
17989 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17990
17991 /*
17992 * Invalidate relcache so that all sessions will refresh any cached plans
17993 * that might depend on the old options.
17994 */
17996
17997 InvokeObjectPostAlterHook(ForeignTableRelationId,
17998 RelationGetRelid(rel), 0);
17999
18001
18002 heap_freetuple(tuple);
18003}

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

17758{
17759 Oid indexOid;
17760 Relation indexRel;
17761 int key;
17762
17763 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17764 {
17765 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17766 return;
17767 }
17768 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17769 {
17770 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17771 return;
17772 }
17773 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17774 {
17775 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17776 return;
17777 }
17778 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17779 {
17780 /* fallthrough */ ;
17781 }
17782 else
17783 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17784
17785 /* Check that the index exists */
17786 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17787 if (!OidIsValid(indexOid))
17788 ereport(ERROR,
17789 (errcode(ERRCODE_UNDEFINED_OBJECT),
17790 errmsg("index \"%s\" for table \"%s\" does not exist",
17791 stmt->name, RelationGetRelationName(rel))));
17792
17793 indexRel = index_open(indexOid, ShareLock);
17794
17795 /* Check that the index is on the relation we're altering. */
17796 if (indexRel->rd_index == NULL ||
17797 indexRel->rd_index->indrelid != RelationGetRelid(rel))
17798 ereport(ERROR,
17799 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17800 errmsg("\"%s\" is not an index for table \"%s\"",
17801 RelationGetRelationName(indexRel),
17803
17804 /*
17805 * The AM must support uniqueness, and the index must in fact be unique.
17806 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
17807 * exclusion), we can use that too.
17808 */
17809 if ((!indexRel->rd_indam->amcanunique ||
17810 !indexRel->rd_index->indisunique) &&
17811 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
17812 ereport(ERROR,
17813 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17814 errmsg("cannot use non-unique index \"%s\" as replica identity",
17815 RelationGetRelationName(indexRel))));
17816 /* Deferred indexes are not guaranteed to be always unique. */
17817 if (!indexRel->rd_index->indimmediate)
17818 ereport(ERROR,
17819 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17820 errmsg("cannot use non-immediate index \"%s\" as replica identity",
17821 RelationGetRelationName(indexRel))));
17822 /* Expression indexes aren't supported. */
17823 if (RelationGetIndexExpressions(indexRel) != NIL)
17824 ereport(ERROR,
17825 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17826 errmsg("cannot use expression index \"%s\" as replica identity",
17827 RelationGetRelationName(indexRel))));
17828 /* Predicate indexes aren't supported. */
17829 if (RelationGetIndexPredicate(indexRel) != NIL)
17830 ereport(ERROR,
17831 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17832 errmsg("cannot use partial index \"%s\" as replica identity",
17833 RelationGetRelationName(indexRel))));
17834
17835 /* Check index for nullable columns. */
17836 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17837 {
17838 int16 attno = indexRel->rd_index->indkey.values[key];
17839 Form_pg_attribute attr;
17840
17841 /*
17842 * Reject any other system columns. (Going forward, we'll disallow
17843 * indexes containing such columns in the first place, but they might
17844 * exist in older branches.)
17845 */
17846 if (attno <= 0)
17847 ereport(ERROR,
17848 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17849 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17850 RelationGetRelationName(indexRel), attno)));
17851
17852 attr = TupleDescAttr(rel->rd_att, attno - 1);
17853 if (!attr->attnotnull)
17854 ereport(ERROR,
17855 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17856 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17857 RelationGetRelationName(indexRel),
17858 NameStr(attr->attname))));
17859 }
17860
17861 /* This index is suitable for use as a replica identity. Mark it. */
17862 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17863
17864 index_close(indexRel, NoLock);
17865}
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:532
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5138
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5025
bool amcanunique
Definition: amapi.h:255
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:17669

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

15793{
15794 Relation pg_class;
15795 Oid oldAccessMethodId;
15796 HeapTuple tuple;
15797 Form_pg_class rd_rel;
15798 Oid reloid = RelationGetRelid(rel);
15799
15800 /*
15801 * Shouldn't be called on relations having storage; these are processed in
15802 * phase 3.
15803 */
15804 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15805
15806 /* Get a modifiable copy of the relation's pg_class row. */
15807 pg_class = table_open(RelationRelationId, RowExclusiveLock);
15808
15809 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15810 if (!HeapTupleIsValid(tuple))
15811 elog(ERROR, "cache lookup failed for relation %u", reloid);
15812 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15813
15814 /* Update the pg_class row. */
15815 oldAccessMethodId = rd_rel->relam;
15816 rd_rel->relam = newAccessMethodId;
15817
15818 /* Leave if no update required */
15819 if (rd_rel->relam == oldAccessMethodId)
15820 {
15821 heap_freetuple(tuple);
15822 table_close(pg_class, RowExclusiveLock);
15823 return;
15824 }
15825
15826 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15827
15828 /*
15829 * Update the dependency on the new access method. No dependency is added
15830 * if the new access method is InvalidOid (default case). Be very careful
15831 * that this has to compare the previous value stored in pg_class with the
15832 * new one.
15833 */
15834 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15835 {
15836 ObjectAddress relobj,
15837 referenced;
15838
15839 /*
15840 * New access method is defined and there was no dependency
15841 * previously, so record a new one.
15842 */
15843 ObjectAddressSet(relobj, RelationRelationId, reloid);
15844 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15845 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15846 }
15847 else if (OidIsValid(oldAccessMethodId) &&
15848 !OidIsValid(rd_rel->relam))
15849 {
15850 /*
15851 * There was an access method defined, and no new one, so just remove
15852 * the existing dependency.
15853 */
15854 deleteDependencyRecordsForClass(RelationRelationId, reloid,
15855 AccessMethodRelationId,
15857 }
15858 else
15859 {
15860 Assert(OidIsValid(oldAccessMethodId) &&
15861 OidIsValid(rd_rel->relam));
15862
15863 /* Both are valid, so update the dependency */
15864 changeDependencyFor(RelationRelationId, reloid,
15865 AccessMethodRelationId,
15866 oldAccessMethodId, rd_rel->relam);
15867 }
15868
15869 /* make the relam and dependency changes visible */
15871
15872 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15873
15874 heap_freetuple(tuple);
15875 table_close(pg_class, RowExclusiveLock);
15876}

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

18015{
18016 Relation attrel;
18017 HeapTuple tuple;
18018 Form_pg_attribute atttableform;
18020 char *compression;
18021 char cmethod;
18022 ObjectAddress address;
18023
18024 compression = strVal(newValue);
18025
18026 attrel = table_open(AttributeRelationId, RowExclusiveLock);
18027
18028 /* copy the cache entry so we can scribble on it below */
18029 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18030 if (!HeapTupleIsValid(tuple))
18031 ereport(ERROR,
18032 (errcode(ERRCODE_UNDEFINED_COLUMN),
18033 errmsg("column \"%s\" of relation \"%s\" does not exist",
18034 column, RelationGetRelationName(rel))));
18035
18036 /* prevent them from altering a system attribute */
18037 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18038 attnum = atttableform->attnum;
18039 if (attnum <= 0)
18040 ereport(ERROR,
18041 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18042 errmsg("cannot alter system column \"%s\"", column)));
18043
18044 /*
18045 * Check that column type is compressible, then get the attribute
18046 * compression method code
18047 */
18048 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18049
18050 /* update pg_attribute entry */
18051 atttableform->attcompression = cmethod;
18052 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18053
18054 InvokeObjectPostAlterHook(RelationRelationId,
18055 RelationGetRelid(rel),
18056 attnum);
18057
18058 /*
18059 * Apply the change to indexes as well (only for simple index columns,
18060 * matching behavior of index.c ConstructTupleDescriptor()).
18061 */
18062 SetIndexStorageProperties(rel, attrel, attnum,
18063 false, 0,
18064 true, cmethod,
18065 lockmode);
18066
18067 heap_freetuple(tuple);
18068
18070
18071 /* make changes visible */
18073
18074 ObjectAddressSubSet(address, RelationRelationId,
18075 RelationGetRelid(rel), attnum);
18076 return address;
18077}
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:21280
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:8989

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

8466{
8467 HeapTuple tuple;
8468 Form_pg_attribute attTup;
8470 char attgenerated;
8471 bool rewrite;
8472 Oid attrdefoid;
8473 ObjectAddress address;
8474 Expr *defval;
8476 RawColumnDefault *rawEnt;
8477
8478 tuple = SearchSysCacheAttName(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
8487 attnum = attTup->attnum;
8488 if (attnum <= 0)
8489 ereport(ERROR,
8490 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8491 errmsg("cannot alter system column \"%s\"",
8492 colName)));
8493
8494 attgenerated = attTup->attgenerated;
8495 if (!attgenerated)
8496 ereport(ERROR,
8497 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8498 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8499 colName, RelationGetRelationName(rel))));
8500
8501 /*
8502 * TODO: This could be done, just need to recheck any constraints
8503 * afterwards.
8504 */
8505 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8506 rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8507 ereport(ERROR,
8508 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8509 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables with check constraints"),
8510 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8511 colName, RelationGetRelationName(rel))));
8512
8513 /*
8514 * We need to prevent this because a change of expression could affect a
8515 * row filter and inject expressions that are not permitted in a row
8516 * filter. XXX We could try to have a more precise check to catch only
8517 * publications with row filters, or even re-verify the row filter
8518 * expressions.
8519 */
8520 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8522 ereport(ERROR,
8523 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8524 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables that are part of a publication"),
8525 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8526 colName, RelationGetRelationName(rel))));
8527
8528 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8529
8530 ReleaseSysCache(tuple);
8531
8532 if (rewrite)
8533 {
8534 /*
8535 * Clear all the missing values if we're rewriting the table, since
8536 * this renders them pointless.
8537 */
8539
8540 /* make sure we don't conflict with later attribute modifications */
8542
8543 /*
8544 * Find everything that depends on the column (constraints, indexes,
8545 * etc), and record enough information to let us recreate the objects
8546 * after rewrite.
8547 */
8549 }
8550
8551 /*
8552 * Drop the dependency records of the GENERATED expression, in particular
8553 * its INTERNAL dependency on the column, which would otherwise cause
8554 * dependency.c to refuse to perform the deletion.
8555 */
8556 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8557 if (!OidIsValid(attrdefoid))
8558 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8559 RelationGetRelid(rel), attnum);
8560 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8561
8562 /* Make above changes visible */
8564
8565 /*
8566 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8567 * safety, but at present we do not expect anything to depend on the
8568 * expression.
8569 */
8571 false, false);
8572
8573 /* Prepare to store the new expression, in the catalogs */
8574 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8575 rawEnt->attnum = attnum;
8576 rawEnt->raw_default = newExpr;
8577 rawEnt->generated = attgenerated;
8578
8579 /* Store the generated expression */
8581 false, true, false, NULL);
8582
8583 /* Make above new expression visible */
8585
8586 if (rewrite)
8587 {
8588 /* Prepare for table rewrite */
8589 defval = (Expr *) build_column_default(rel, attnum);
8590
8592 newval->attnum = attnum;
8593 newval->expr = expression_planner(defval);
8594 newval->is_generated = true;
8595
8596 tab->newvals = lappend(tab->newvals, newval);
8598 }
8599
8600 /* Drop any pg_statistic entry for the column */
8602
8603 InvokeObjectPostAlterHook(RelationRelationId,
8604 RelationGetRelid(rel), attnum);
8605
8606 ObjectAddressSubSet(address, RelationRelationId,
8607 RelationGetRelid(rel), attnum);
8608 return address;
8609}
List * GetRelationPublications(Oid relid)
uint16 num_check
Definition: tupdesc.h:44
TupleConstr * constr
Definition: tupdesc.h:135

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, and SearchSysCacheAttName().

Referenced by ATExecCmd().

◆ ATExecSetIdentity()

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

Definition at line 8233 of file tablecmds.c.

8235{
8237 DefElem *generatedEl = NULL;
8238 HeapTuple tuple;
8239 Form_pg_attribute attTup;
8241 Relation attrelation;
8242 ObjectAddress address;
8243 bool ispartitioned;
8244
8245 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8246 if (ispartitioned && !recurse)
8247 ereport(ERROR,
8248 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8249 errmsg("cannot change identity column of only the partitioned table"),
8250 errhint("Do not specify the ONLY keyword.")));
8251
8252 if (rel->rd_rel->relispartition && !recursing)
8253 ereport(ERROR,
8254 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8255 errmsg("cannot change identity column of a partition"));
8256
8257 foreach(option, castNode(List, def))
8258 {
8259 DefElem *defel = lfirst_node(DefElem, option);
8260
8261 if (strcmp(defel->defname, "generated") == 0)
8262 {
8263 if (generatedEl)
8264 ereport(ERROR,
8265 (errcode(ERRCODE_SYNTAX_ERROR),
8266 errmsg("conflicting or redundant options")));
8267 generatedEl = defel;
8268 }
8269 else
8270 elog(ERROR, "option \"%s\" not recognized",
8271 defel->defname);
8272 }
8273
8274 /*
8275 * Even if there is nothing to change here, we run all the checks. There
8276 * will be a subsequent ALTER SEQUENCE that relies on everything being
8277 * there.
8278 */
8279
8280 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8281 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8282 if (!HeapTupleIsValid(tuple))
8283 ereport(ERROR,
8284 (errcode(ERRCODE_UNDEFINED_COLUMN),
8285 errmsg("column \"%s\" of relation \"%s\" does not exist",
8286 colName, RelationGetRelationName(rel))));
8287
8288 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8289 attnum = attTup->attnum;
8290
8291 if (attnum <= 0)
8292 ereport(ERROR,
8293 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8294 errmsg("cannot alter system column \"%s\"",
8295 colName)));
8296
8297 if (!attTup->attidentity)
8298 ereport(ERROR,
8299 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8300 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8301 colName, RelationGetRelationName(rel))));
8302
8303 if (generatedEl)
8304 {
8305 attTup->attidentity = defGetInt32(generatedEl);
8306 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8307
8308 InvokeObjectPostAlterHook(RelationRelationId,
8309 RelationGetRelid(rel),
8310 attTup->attnum);
8311 ObjectAddressSubSet(address, RelationRelationId,
8312 RelationGetRelid(rel), attnum);
8313 }
8314 else
8315 address = InvalidObjectAddress;
8316
8317 heap_freetuple(tuple);
8318 table_close(attrelation, RowExclusiveLock);
8319
8320 /*
8321 * Recurse to propagate the identity change to partitions. Identity is not
8322 * inherited in regular inheritance children.
8323 */
8324 if (generatedEl && recurse && ispartitioned)
8325 {
8326 List *children;
8327 ListCell *lc;
8328
8329 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8330
8331 foreach(lc, children)
8332 {
8333 Relation childrel;
8334
8335 childrel = table_open(lfirst_oid(lc), NoLock);
8336 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8337 table_close(childrel, NoLock);
8338 }
8339 }
8340
8341 return address;
8342}
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 *  constrname,
char *  colName,
bool  recurse,
bool  recursing,
LOCKMODE  lockmode 
)
static

Definition at line 7801 of file tablecmds.c.

7803{
7804 HeapTuple tuple;
7806 ObjectAddress address;
7807 Constraint *constraint;
7808 CookedConstraint *ccon;
7809 List *cooked;
7810 bool is_no_inherit = false;
7811
7812 /* Guard against stack overflow due to overly deep inheritance tree. */
7814
7815 /* At top level, permission check was done in ATPrepCmd, else do it */
7816 if (recursing)
7817 {
7820 Assert(conName != NULL);
7821 }
7822
7823 attnum = get_attnum(RelationGetRelid(rel), colName);
7825 ereport(ERROR,
7826 (errcode(ERRCODE_UNDEFINED_COLUMN),
7827 errmsg("column \"%s\" of relation \"%s\" does not exist",
7828 colName, RelationGetRelationName(rel))));
7829
7830 /* Prevent them from altering a system attribute */
7831 if (attnum <= 0)
7832 ereport(ERROR,
7833 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7834 errmsg("cannot alter system column \"%s\"",
7835 colName)));
7836
7837 /* TODO: see transformColumnDefinition() */
7838 if (TupleDescAttr(RelationGetDescr(rel), attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
7839 ereport(ERROR,
7840 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7841 errmsg("not-null constraints are not supported on virtual generated columns"),
7842 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
7843 colName, RelationGetRelationName(rel))));
7844
7845 /* See if there's already a constraint */
7847 if (HeapTupleIsValid(tuple))
7848 {
7850 bool changed = false;
7851
7852 /*
7853 * Don't let a NO INHERIT constraint be changed into inherit.
7854 */
7855 if (conForm->connoinherit && recurse)
7856 ereport(ERROR,
7857 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7858 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7859 NameStr(conForm->conname),
7861
7862 /*
7863 * If we find an appropriate constraint, we're almost done, but just
7864 * need to change some properties on it: if we're recursing, increment
7865 * coninhcount; if not, set conislocal if not already set.
7866 */
7867 if (recursing)
7868 {
7869 if (pg_add_s16_overflow(conForm->coninhcount, 1,
7870 &conForm->coninhcount))
7871 ereport(ERROR,
7872 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7873 errmsg("too many inheritance parents"));
7874 changed = true;
7875 }
7876 else if (!conForm->conislocal)
7877 {
7878 conForm->conislocal = true;
7879 changed = true;
7880 }
7881
7882 if (changed)
7883 {
7884 Relation constr_rel;
7885
7886 constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7887
7888 CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7889 ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7890 table_close(constr_rel, RowExclusiveLock);
7891 }
7892
7893 if (changed)
7894 return address;
7895 else
7896 return InvalidObjectAddress;
7897 }
7898
7899 /*
7900 * If we're asked not to recurse, and children exist, raise an error for
7901 * partitioned tables. For inheritance, we act as if NO INHERIT had been
7902 * specified.
7903 */
7904 if (!recurse &&
7906 NoLock) != NIL)
7907 {
7908 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7909 ereport(ERROR,
7910 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7911 errmsg("constraint must be added to child tables too"),
7912 errhint("Do not specify the ONLY keyword."));
7913 else
7914 is_no_inherit = true;
7915 }
7916
7917 /*
7918 * No constraint exists; we must add one. First determine a name to use,
7919 * if we haven't already.
7920 */
7921 if (!recursing)
7922 {
7923 Assert(conName == NULL);
7925 colName, "not_null",
7927 NIL);
7928 }
7929
7930 constraint = makeNotNullConstraint(makeString(colName));
7931 constraint->is_no_inherit = is_no_inherit;
7932 constraint->conname = conName;
7933
7934 /* and do it */
7935 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7936 false, !recursing, false, NULL);
7937 ccon = linitial(cooked);
7938 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7939
7940 InvokeObjectPostAlterHook(RelationRelationId,
7941 RelationGetRelid(rel), attnum);
7942
7943 /* Mark pg_attribute.attnotnull for the column */
7944 set_attnotnull(wqueue, rel, attnum, lockmode);
7945
7946 /*
7947 * Recurse to propagate the constraint to children that don't have one.
7948 */
7949 if (recurse)
7950 {
7951 List *children;
7952
7954 lockmode);
7955
7956 foreach_oid(childoid, children)
7957 {
7958 Relation childrel = table_open(childoid, NoLock);
7959
7961
7962 ATExecSetNotNull(wqueue, childrel, conName, colName,
7963 recurse, true, lockmode);
7964 table_close(childrel, NoLock);
7965 }
7966 }
7967
7968 return address;
7969}
Constraint * makeNotNullConstraint(String *colname)
Definition: makefuncs.c:493
String * makeString(char *str)
Definition: value.c:63

References AddRelationNewConstraints(), Assert(), AT_AddConstraint, ATExecSetNotNull(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, attnum, CatalogTupleUpdate(), check_stack_depth(), ChooseConstraintName(), CommandCounterIncrement(), Constraint::conname, CookedConstraint::conoid, ereport, errcode(), errdetail(), 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, RelationGetDescr, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, set_attnotnull(), HeapTupleData::t_self, table_close(), table_open(), and TupleDescAttr().

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

◆ ATExecSetOptions()

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

Definition at line 8910 of file tablecmds.c.

8912{
8913 Relation attrelation;
8914 HeapTuple tuple,
8915 newtuple;
8916 Form_pg_attribute attrtuple;
8918 Datum datum,
8919 newOptions;
8920 bool isnull;
8921 ObjectAddress address;
8922 Datum repl_val[Natts_pg_attribute];
8923 bool repl_null[Natts_pg_attribute];
8924 bool repl_repl[Natts_pg_attribute];
8925
8926 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8927
8928 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8929
8930 if (!HeapTupleIsValid(tuple))
8931 ereport(ERROR,
8932 (errcode(ERRCODE_UNDEFINED_COLUMN),
8933 errmsg("column \"%s\" of relation \"%s\" does not exist",
8934 colName, RelationGetRelationName(rel))));
8935 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8936
8937 attnum = attrtuple->attnum;
8938 if (attnum <= 0)
8939 ereport(ERROR,
8940 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8941 errmsg("cannot alter system column \"%s\"",
8942 colName)));
8943
8944 /* Generate new proposed attoptions (text array) */
8945 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8946 &isnull);
8947 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8948 castNode(List, options), NULL, NULL,
8949 false, isReset);
8950 /* Validate new options */
8951 (void) attribute_reloptions(newOptions, true);
8952
8953 /* Build new tuple. */
8954 memset(repl_null, false, sizeof(repl_null));
8955 memset(repl_repl, false, sizeof(repl_repl));
8956 if (newOptions != (Datum) 0)
8957 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8958 else
8959 repl_null[Anum_pg_attribute_attoptions - 1] = true;
8960 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8961 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8962 repl_val, repl_null, repl_repl);
8963
8964 /* Update system catalog. */
8965 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8966
8967 InvokeObjectPostAlterHook(RelationRelationId,
8968 RelationGetRelid(rel),
8969 attrtuple->attnum);
8970 ObjectAddressSubSet(address, RelationRelationId,
8971 RelationGetRelid(rel), attnum);
8972
8973 heap_freetuple(newtuple);
8974
8975 ReleaseSysCache(tuple);
8976
8977 table_close(attrelation, RowExclusiveLock);
8978
8979 return address;
8980}
Datum transformRelOptions(Datum oldOptions, List *defList, const char *namspace, const char *const validnsps[], bool acceptOidsOff, bool isReset)
Definition: reloptions.c:1175
bytea * attribute_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2104

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

15914{
15915 Oid relid;
15916 Relation pgclass;
15917 HeapTuple tuple;
15918 HeapTuple newtuple;
15919 Datum datum;
15920 Datum newOptions;
15921 Datum repl_val[Natts_pg_class];
15922 bool repl_null[Natts_pg_class];
15923 bool repl_repl[Natts_pg_class];
15924 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
15925
15926 if (defList == NIL && operation != AT_ReplaceRelOptions)
15927 return; /* nothing to do */
15928
15929 pgclass = table_open(RelationRelationId, RowExclusiveLock);
15930
15931 /* Fetch heap tuple */
15932 relid = RelationGetRelid(rel);
15933 tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
15934 if (!HeapTupleIsValid(tuple))
15935 elog(ERROR, "cache lookup failed for relation %u", relid);
15936
15937 if (operation == AT_ReplaceRelOptions)
15938 {
15939 /*
15940 * If we're supposed to replace the reloptions list, we just pretend
15941 * there were none before.
15942 */
15943 datum = (Datum) 0;
15944 }
15945 else
15946 {
15947 bool isnull;
15948
15949 /* Get the old reloptions */
15950 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15951 &isnull);
15952 if (isnull)
15953 datum = (Datum) 0;
15954 }
15955
15956 /* Generate new proposed reloptions (text array) */
15957 newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
15958 operation == AT_ResetRelOptions);
15959
15960 /* Validate */
15961 switch (rel->rd_rel->relkind)
15962 {
15963 case RELKIND_RELATION:
15964 case RELKIND_MATVIEW:
15965 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
15966 break;
15967 case RELKIND_PARTITIONED_TABLE:
15968 (void) partitioned_table_reloptions(newOptions, true);
15969 break;
15970 case RELKIND_VIEW:
15971 (void) view_reloptions(newOptions, true);
15972 break;
15973 case RELKIND_INDEX:
15974 case RELKIND_PARTITIONED_INDEX:
15975 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
15976 break;
15977 case RELKIND_TOASTVALUE:
15978 /* fall through to error -- shouldn't ever get here */
15979 default:
15980 ereport(ERROR,
15981 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15982 errmsg("cannot set options for relation \"%s\"",
15984 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
15985 break;
15986 }
15987
15988 /* Special-case validation of view options */
15989 if (rel->rd_rel->relkind == RELKIND_VIEW)
15990 {
15991 Query *view_query = get_view_query(rel);
15992 List *view_options = untransformRelOptions(newOptions);
15993 ListCell *cell;
15994 bool check_option = false;
15995
15996 foreach(cell, view_options)
15997 {
15998 DefElem *defel = (DefElem *) lfirst(cell);
15999
16000 if (strcmp(defel->defname, "check_option") == 0)
16001 check_option = true;
16002 }
16003
16004 /*
16005 * If the check option is specified, look to see if the view is
16006 * actually auto-updatable or not.
16007 */
16008 if (check_option)
16009 {
16010 const char *view_updatable_error =
16011 view_query_is_auto_updatable(view_query, true);
16012
16013 if (view_updatable_error)
16014 ereport(ERROR,
16015 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16016 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16017 errhint("%s", _(view_updatable_error))));
16018 }
16019 }
16020
16021 /*
16022 * All we need do here is update the pg_class row; the new options will be
16023 * propagated into relcaches during post-commit cache inval.
16024 */
16025 memset(repl_val, 0, sizeof(repl_val));
16026 memset(repl_null, false, sizeof(repl_null));
16027 memset(repl_repl, false, sizeof(repl_repl));
16028
16029 if (newOptions != (Datum) 0)
16030 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16031 else
16032 repl_null[Anum_pg_class_reloptions - 1] = true;
16033
16034 repl_repl[Anum_pg_class_reloptions - 1] = true;
16035
16036 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16037 repl_val, repl_null, repl_repl);
16038
16039 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16040 UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16041
16042 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16043
16044 heap_freetuple(newtuple);
16045
16046 ReleaseSysCache(tuple);
16047
16048 /* repeat the whole exercise for the toast table, if there's one */
16049 if (OidIsValid(rel->rd_rel->reltoastrelid))
16050 {
16051 Relation toastrel;
16052 Oid toastid = rel->rd_rel->reltoastrelid;
16053
16054 toastrel = table_open(toastid, lockmode);
16055
16056 /* Fetch heap tuple */
16057 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16058 if (!HeapTupleIsValid(tuple))
16059 elog(ERROR, "cache lookup failed for relation %u", toastid);
16060
16061 if (operation == AT_ReplaceRelOptions)
16062 {
16063 /*
16064 * If we're supposed to replace the reloptions list, we just
16065 * pretend there were none before.
16066 */
16067 datum = (Datum) 0;
16068 }
16069 else
16070 {
16071 bool isnull;
16072
16073 /* Get the old reloptions */
16074 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16075 &isnull);
16076 if (isnull)
16077 datum = (Datum) 0;
16078 }
16079
16080 newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16081 false, operation == AT_ResetRelOptions);
16082
16083 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16084
16085 memset(repl_val, 0, sizeof(repl_val));
16086 memset(repl_null, false, sizeof(repl_null));
16087 memset(repl_repl, false, sizeof(repl_repl));
16088
16089 if (newOptions != (Datum) 0)
16090 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16091 else
16092 repl_null[Anum_pg_class_reloptions - 1] = true;
16093
16094 repl_repl[Anum_pg_class_reloptions - 1] = true;
16095
16096 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16097 repl_val, repl_null, repl_repl);
16098
16099 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16100
16101 InvokeObjectPostAlterHookArg(RelationRelationId,
16102 RelationGetRelid(toastrel), 0,
16103 InvalidOid, true);
16104
16105 heap_freetuple(newtuple);
16106
16107 ReleaseSysCache(tuple);
16108
16109 table_close(toastrel, NoLock);
16110 }
16111
16112 table_close(pgclass, RowExclusiveLock);
16113}
#define _(x)
Definition: elog.c:90
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:200
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1350
bytea * view_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2033
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
Definition: reloptions.c:2089
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2019
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
Definition: reloptions.c:2054
#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:301
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 17871 of file tablecmds.c.

17872{
17873 Relation pg_class;
17874 Oid relid;
17875 HeapTuple tuple;
17876
17877 relid = RelationGetRelid(rel);
17878
17879 /* Pull the record for this relation and update it */
17880 pg_class = table_open(RelationRelationId, RowExclusiveLock);
17881
17882 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17883
17884 if (!HeapTupleIsValid(tuple))
17885 elog(ERROR, "cache lookup failed for relation %u", relid);
17886
17887 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
17888 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17889
17890 InvokeObjectPostAlterHook(RelationRelationId,
17891 RelationGetRelid(rel), 0);
17892
17893 table_close(pg_class, RowExclusiveLock);
17894 heap_freetuple(tuple);
17895}

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

8766{
8767 int newtarget = 0;
8768 bool newtarget_default;
8769 Relation attrelation;
8770 HeapTuple tuple,
8771 newtuple;
8772 Form_pg_attribute attrtuple;
8774 ObjectAddress address;
8775 Datum repl_val[Natts_pg_attribute];
8776 bool repl_null[Natts_pg_attribute];
8777 bool repl_repl[Natts_pg_attribute];
8778
8779 /*
8780 * We allow referencing columns by numbers only for indexes, since table
8781 * column numbers could contain gaps if columns are later dropped.
8782 */
8783 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8784 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8785 !colName)
8786 ereport(ERROR,
8787 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8788 errmsg("cannot refer to non-index column by number")));
8789
8790 /* -1 was used in previous versions for the default setting */
8791 if (newValue && intVal(newValue) != -1)
8792 {
8793 newtarget = intVal(newValue);
8794 newtarget_default = false;
8795 }
8796 else
8797 newtarget_default = true;
8798
8799 if (!newtarget_default)
8800 {
8801 /*
8802 * Limit target to a sane range
8803 */
8804 if (newtarget < 0)
8805 {
8806 ereport(ERROR,
8807 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8808 errmsg("statistics target %d is too low",
8809 newtarget)));
8810 }
8811 else if (newtarget > MAX_STATISTICS_TARGET)
8812 {
8813 newtarget = MAX_STATISTICS_TARGET;
8815 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8816 errmsg("lowering statistics target to %d",
8817 newtarget)));
8818 }
8819 }
8820
8821 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8822
8823 if (colName)
8824 {
8825 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8826
8827 if (!HeapTupleIsValid(tuple))
8828 ereport(ERROR,
8829 (errcode(ERRCODE_UNDEFINED_COLUMN),
8830 errmsg("column \"%s\" of relation \"%s\" does not exist",
8831 colName, RelationGetRelationName(rel))));
8832 }
8833 else
8834 {
8835 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8836
8837 if (!HeapTupleIsValid(tuple))
8838 ereport(ERROR,
8839 (errcode(ERRCODE_UNDEFINED_COLUMN),
8840 errmsg("column number %d of relation \"%s\" does not exist",
8841 colNum, RelationGetRelationName(rel))));
8842 }
8843
8844 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8845
8846 attnum = attrtuple->attnum;
8847 if (attnum <= 0)
8848 ereport(ERROR,
8849 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8850 errmsg("cannot alter system column \"%s\"",
8851 colName)));
8852
8853 /*
8854 * Prevent this as long as the ANALYZE code skips virtual generated
8855 * columns.
8856 */
8857 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8858 ereport(ERROR,
8859 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8860 errmsg("cannot alter statistics on virtual generated column \"%s\"",
8861 colName)));
8862
8863 if (rel->rd_rel->relkind == RELKIND_INDEX ||
8864 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8865 {
8866 if (attnum > rel->rd_index->indnkeyatts)
8867 ereport(ERROR,
8868 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8869 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8870 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8871 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8872 ereport(ERROR,
8873 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8874 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8875 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8876 errhint("Alter statistics on table column instead.")));
8877 }
8878
8879 /* Build new tuple. */
8880 memset(repl_null, false, sizeof(repl_null));
8881 memset(repl_repl, false, sizeof(repl_repl));
8882 if (!newtarget_default)
8883 repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8884 else
8885 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8886 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8887 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8888 repl_val, repl_null, repl_repl);
8889 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8890
8891 InvokeObjectPostAlterHook(RelationRelationId,
8892 RelationGetRelid(rel),
8893 attrtuple->attnum);
8894 ObjectAddressSubSet(address, RelationRelationId,
8895 RelationGetRelid(rel), attnum);
8896
8897 heap_freetuple(newtuple);
8898
8899 ReleaseSysCache(tuple);
8900
8901 table_close(attrelation, RowExclusiveLock);
8902
8903 return address;
8904}
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 9052 of file tablecmds.c.

9053{
9054 Relation attrelation;
9055 HeapTuple tuple;
9056 Form_pg_attribute attrtuple;
9058 ObjectAddress address;
9059
9060 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9061
9062 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9063
9064 if (!HeapTupleIsValid(tuple))
9065 ereport(ERROR,
9066 (errcode(ERRCODE_UNDEFINED_COLUMN),
9067 errmsg("column \"%s\" of relation \"%s\" does not exist",
9068 colName, RelationGetRelationName(rel))));
9069 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9070
9071 attnum = attrtuple->attnum;
9072 if (attnum <= 0)
9073 ereport(ERROR,
9074 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9075 errmsg("cannot alter system column \"%s\"",
9076 colName)));
9077
9078 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9079
9080 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9081
9082 InvokeObjectPostAlterHook(RelationRelationId,
9083 RelationGetRelid(rel),
9084 attrtuple->attnum);
9085
9086 /*
9087 * Apply the change to indexes as well (only for simple index columns,
9088 * matching behavior of index.c ConstructTupleDescriptor()).
9089 */
9090 SetIndexStorageProperties(rel, attrelation, attnum,
9091 true, attrtuple->attstorage,
9092 false, 0,
9093 lockmode);
9094
9095 heap_freetuple(tuple);
9096
9097 table_close(attrelation, RowExclusiveLock);
9098
9099 ObjectAddressSubSet(address, RelationRelationId,
9100 RelationGetRelid(rel), attnum);
9101 return address;
9102}
static char GetAttributeStorage(Oid atttypid, const char *storagemode)
Definition: tablecmds.c:21318

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

16121{
16122 Relation rel;
16123 Oid reltoastrelid;
16124 RelFileNumber newrelfilenumber;
16125 RelFileLocator newrlocator;
16126 List *reltoastidxids = NIL;
16127 ListCell *lc;
16128
16129 /*
16130 * Need lock here in case we are recursing to toast table or index
16131 */
16132 rel = relation_open(tableOid, lockmode);
16133
16134 /* Check first if relation can be moved to new tablespace */
16135 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16136 {
16137 InvokeObjectPostAlterHook(RelationRelationId,
16138 RelationGetRelid(rel), 0);
16139 relation_close(rel, NoLock);
16140 return;
16141 }
16142
16143 reltoastrelid = rel->rd_rel->reltoastrelid;
16144 /* Fetch the list of indexes on toast relation if necessary */
16145 if (OidIsValid(reltoastrelid))
16146 {
16147 Relation toastRel = relation_open(reltoastrelid, lockmode);
16148
16149 reltoastidxids = RelationGetIndexList(toastRel);
16150 relation_close(toastRel, lockmode);
16151 }
16152
16153 /*
16154 * Relfilenumbers are not unique in databases across tablespaces, so we
16155 * need to allocate a new one in the new tablespace.
16156 */
16157 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16158 rel->rd_rel->relpersistence);
16159
16160 /* Open old and new relation */
16161 newrlocator = rel->rd_locator;
16162 newrlocator.relNumber = newrelfilenumber;
16163 newrlocator.spcOid = newTableSpace;
16164
16165 /* hand off to AM to actually create new rel storage and copy the data */
16166 if (rel->rd_rel->relkind == RELKIND_INDEX)
16167 {
16168 index_copy_data(rel, newrlocator);
16169 }
16170 else
16171 {
16172 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16173 table_relation_copy_data(rel, &newrlocator);
16174 }
16175
16176 /*
16177 * Update the pg_class row.
16178 *
16179 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16180 * executed on pg_class or its indexes (the above copy wouldn't contain
16181 * the updated pg_class entry), but that's forbidden with
16182 * CheckRelationTableSpaceMove().
16183 */
16184 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16185
16186 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16187
16189
16190 relation_close(rel, NoLock);
16191
16192 /* Make sure the reltablespace change is visible */
16194
16195 /* Move associated toast relation and/or indexes, too */
16196 if (OidIsValid(reltoastrelid))
16197 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16198 foreach(lc, reltoastidxids)
16199 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16200
16201 /* Clean up */
16202 list_free(reltoastidxids);
16203}
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition: catalog.c:528
void RelationAssumeNewRelfilelocator(Relation relation)
Definition: relcache.c:3928
Oid RelFileNumber
Definition: relpath.h:25
RelFileNumber relNumber
static void table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
Definition: tableam.h:1621
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:16120
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3654
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3711
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:16414

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

16214{
16215 /*
16216 * Shouldn't be called on relations having storage; these are processed in
16217 * phase 3.
16218 */
16219 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16220
16221 /* check if relation can be moved to its new tablespace */
16222 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16223 {
16224 InvokeObjectPostAlterHook(RelationRelationId,
16225 RelationGetRelid(rel),
16226 0);
16227 return;
16228 }
16229
16230 /* Update can be done, so change reltablespace */
16231 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16232
16233 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16234
16235 /* Make sure the reltablespace change is visible */
16237}

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

12331{
12332 Relation conrel;
12333 SysScanDesc scan;
12334 ScanKeyData skey[3];
12335 HeapTuple tuple;
12337 ObjectAddress address;
12338
12339 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12340
12341 /*
12342 * Find and check the target constraint
12343 */
12344 ScanKeyInit(&skey[0],
12345 Anum_pg_constraint_conrelid,
12346 BTEqualStrategyNumber, F_OIDEQ,
12348 ScanKeyInit(&skey[1],
12349 Anum_pg_constraint_contypid,
12350 BTEqualStrategyNumber, F_OIDEQ,
12352 ScanKeyInit(&skey[2],
12353 Anum_pg_constraint_conname,
12354 BTEqualStrategyNumber, F_NAMEEQ,
12355 CStringGetDatum(constrName));
12356 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12357 true, NULL, 3, skey);
12358
12359 /* There can be at most one matching row */
12360 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12361 ereport(ERROR,
12362 (errcode(ERRCODE_UNDEFINED_OBJECT),
12363 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12364 constrName, RelationGetRelationName(rel))));
12365
12366 con = (Form_pg_constraint) GETSTRUCT(tuple);
12367 if (con->contype != CONSTRAINT_FOREIGN &&
12368 con->contype != CONSTRAINT_CHECK)
12369 ereport(ERROR,
12370 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12371 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12372 constrName, RelationGetRelationName(rel))));
12373
12374 if (!con->conenforced)
12375 ereport(ERROR,
12376 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12377 errmsg("cannot validate NOT ENFORCED constraint")));
12378
12379 if (!con->convalidated)
12380 {
12381 if (con->contype == CONSTRAINT_FOREIGN)
12382 {
12383 QueueFKConstraintValidation(wqueue, conrel, rel, tuple, lockmode);
12384 }
12385 else if (con->contype == CONSTRAINT_CHECK)
12386 {
12387 QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12388 tuple, recurse, recursing, lockmode);
12389 }
12390
12391 ObjectAddressSet(address, ConstraintRelationId, con->oid);
12392 }
12393 else
12394 address = InvalidObjectAddress; /* already validated */
12395
12396 systable_endscan(scan);
12397
12399
12400 return address;
12401}
static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12411
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:12511

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

Referenced by ATExecCmd(), and QueueCheckConstraintValidation().

◆ ATGetQueueEntry()

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

Definition at line 6465 of file tablecmds.c.

6466{
6467 Oid relid = RelationGetRelid(rel);
6468 AlteredTableInfo *tab;
6469 ListCell *ltab;
6470
6471 foreach(ltab, *wqueue)
6472 {
6473 tab = (AlteredTableInfo *) lfirst(ltab);
6474 if (tab->relid == relid)
6475 return tab;
6476 }
6477
6478 /*
6479 * Not there, so add it. Note that we make a copy of the relation's
6480 * existing descriptor before anything interesting can happen to it.
6481 */
6482 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6483 tab->relid = relid;
6484 tab->rel = NULL; /* set later */
6485 tab->relkind = rel->rd_rel->relkind;
6488 tab->chgAccessMethod = false;
6490 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6491 tab->chgPersistence = false;
6492
6493 *wqueue = lappend(*wqueue, tab);
6494
6495 return tab;
6496}
char newrelpersistence
Definition: tablecmds.c:197
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:322

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(), ATPostAlterTypeParse(), ATPrepCmd(), DetachAddConstraintIfNeeded(), QueueCheckConstraintValidation(), QueueFKConstraintValidation(), 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 5672 of file tablecmds.c.

5675{
5676 AlterTableCmd *newcmd = NULL;
5678 List *beforeStmts;
5679 List *afterStmts;
5680 ListCell *lc;
5681
5682 /* Gin up an AlterTableStmt with just this subcommand and this table */
5683 atstmt->relation =
5686 -1);
5687 atstmt->relation->inh = recurse;
5688 atstmt->cmds = list_make1(cmd);
5689 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5690 atstmt->missing_ok = false;
5691
5692 /* Transform the AlterTableStmt */
5694 atstmt,
5695 context->queryString,
5696 &beforeStmts,
5697 &afterStmts);
5698
5699 /* Execute any statements that should happen before these subcommand(s) */
5700 foreach(lc, beforeStmts)
5701 {
5702 Node *stmt = (Node *) lfirst(lc);
5703
5706 }
5707
5708 /* Examine the transformed subcommands and schedule them appropriately */
5709 foreach(lc, atstmt->cmds)
5710 {
5712 AlterTablePass pass;
5713
5714 /*
5715 * This switch need only cover the subcommand types that can be added
5716 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5717 * executing the subcommand immediately, as a substitute for the
5718 * original subcommand. (Note, however, that this does cause
5719 * AT_AddConstraint subcommands to be rescheduled into later passes,
5720 * which is important for index and foreign key constraints.)
5721 *
5722 * We assume we needn't do any phase-1 checks for added subcommands.
5723 */
5724 switch (cmd2->subtype)
5725 {
5726 case AT_AddIndex:
5727 pass = AT_PASS_ADD_INDEX;
5728 break;
5731 break;
5732 case AT_AddConstraint:
5733 /* Recursion occurs during execution phase */
5734 if (recurse)
5735 cmd2->recurse = true;
5736 switch (castNode(Constraint, cmd2->def)->contype)
5737 {
5738 case CONSTR_NOTNULL:
5739 pass = AT_PASS_COL_ATTRS;
5740 break;
5741 case CONSTR_PRIMARY:
5742 case CONSTR_UNIQUE:
5743 case CONSTR_EXCLUSION:
5745 break;
5746 default:
5748 break;
5749 }
5750 break;
5752 /* This command never recurses */
5753 /* No command-specific prep needed */
5754 pass = AT_PASS_MISC;
5755 break;
5756 default:
5757 pass = cur_pass;
5758 break;
5759 }
5760
5761 if (pass < cur_pass)
5762 {
5763 /* Cannot schedule into a pass we already finished */
5764 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5765 pass);
5766 }
5767 else if (pass > cur_pass)
5768 {
5769 /* OK, queue it up for later */
5770 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5771 }
5772 else
5773 {
5774 /*
5775 * We should see at most one subcommand for the current pass,
5776 * which is the transformed version of the original subcommand.
5777 */
5778 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5779 {
5780 /* Found the transformed version of our subcommand */
5781 newcmd = cmd2;
5782 }
5783 else
5784 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5785 pass);
5786 }
5787 }
5788
5789 /* Queue up any after-statements to happen at the end */
5790 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5791
5792 return newcmd;
5793}
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:2396
ObjectType objtype
Definition: parsenodes.h:2398
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 14726 of file tablecmds.c.

14727{
14728 ObjectAddress obj;
14729 ObjectAddresses *objects;
14730 ListCell *def_item;
14731 ListCell *oid_item;
14732
14733 /*
14734 * Collect all the constraints and indexes to drop so we can process them
14735 * in a single call. That way we don't have to worry about dependencies
14736 * among them.
14737 */
14738 objects = new_object_addresses();
14739
14740 /*
14741 * Re-parse the index and constraint definitions, and attach them to the
14742 * appropriate work queue entries. We do this before dropping because in
14743 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14744 * lock on the table the constraint is attached to, and we need to get
14745 * that before reparsing/dropping.
14746 *
14747 * We can't rely on the output of deparsing to tell us which relation to
14748 * operate on, because concurrent activity might have made the name
14749 * resolve differently. Instead, we've got to use the OID of the
14750 * constraint or index we're processing to figure out which relation to
14751 * operate on.
14752 */
14753 forboth(oid_item, tab->changedConstraintOids,
14754 def_item, tab->changedConstraintDefs)
14755 {
14756 Oid oldId = lfirst_oid(oid_item);
14757 HeapTuple tup;
14759 Oid relid;
14760 Oid confrelid;
14761 char contype;
14762 bool conislocal;
14763
14764 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14765 if (!HeapTupleIsValid(tup)) /* should not happen */
14766 elog(ERROR, "cache lookup failed for constraint %u", oldId);
14767 con = (Form_pg_constraint) GETSTRUCT(tup);
14768 if (OidIsValid(con->conrelid))
14769 relid = con->conrelid;
14770 else
14771 {
14772 /* must be a domain constraint */
14773 relid = get_typ_typrelid(getBaseType(con->contypid));
14774 if (!OidIsValid(relid))
14775 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
14776 }
14777 confrelid = con->confrelid;
14778 contype = con->contype;
14779 conislocal = con->conislocal;
14780 ReleaseSysCache(tup);
14781
14782 ObjectAddressSet(obj, ConstraintRelationId, oldId);
14783 add_exact_object_address(&obj, objects);
14784
14785 /*
14786 * If the constraint is inherited (only), we don't want to inject a
14787 * new definition here; it'll get recreated when
14788 * ATAddCheckNNConstraint recurses from adding the parent table's
14789 * constraint. But we had to carry the info this far so that we can
14790 * drop the constraint below.
14791 */
14792 if (!conislocal)
14793 continue;
14794
14795 /*
14796 * When rebuilding an FK constraint that references the table we're
14797 * modifying, we might not yet have any lock on the FK's table, so get
14798 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14799 * step, so there's no value in asking for anything weaker.
14800 */
14801 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
14803
14804 ATPostAlterTypeParse(oldId, relid, confrelid,
14805 (char *) lfirst(def_item),
14806 wqueue, lockmode, tab->rewrite);
14807 }
14808 forboth(oid_item, tab->changedIndexOids,
14809 def_item, tab->changedIndexDefs)
14810 {
14811 Oid oldId = lfirst_oid(oid_item);
14812 Oid relid;
14813
14814 relid = IndexGetRelation(oldId, false);
14815 ATPostAlterTypeParse(oldId, relid, InvalidOid,
14816 (char *) lfirst(def_item),
14817 wqueue, lockmode, tab->rewrite);
14818
14819 ObjectAddressSet(obj, RelationRelationId, oldId);
14820 add_exact_object_address(&obj, objects);
14821 }
14822
14823 /* add dependencies for new statistics */
14824 forboth(oid_item, tab->changedStatisticsOids,
14825 def_item, tab->changedStatisticsDefs)
14826 {
14827 Oid oldId = lfirst_oid(oid_item);
14828 Oid relid;
14829
14830 relid = StatisticsGetRelation(oldId, false);
14831 ATPostAlterTypeParse(oldId, relid, InvalidOid,
14832 (char *) lfirst(def_item),
14833 wqueue, lockmode, tab->rewrite);
14834
14835 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
14836 add_exact_object_address(&obj, objects);
14837 }
14838
14839 /*
14840 * Queue up command to restore replica identity index marking
14841 */
14842 if (tab->replicaIdentityIndex)
14843 {
14846
14847 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
14848 subcmd->name = tab->replicaIdentityIndex;
14850 cmd->def = (Node *) subcmd;
14851
14852 /* do it after indexes and constraints */
14855 }
14856
14857 /*
14858 * Queue up command to restore marking of index used for cluster.
14859 */
14860 if (tab->clusterOnIndex)
14861 {
14863
14864 cmd->subtype = AT_ClusterOn;
14865 cmd->name = tab->clusterOnIndex;
14866
14867 /* do it after indexes and constraints */
14870 }
14871
14872 /*
14873 * It should be okay to use DROP_RESTRICT here, since nothing else should
14874 * be depending on these objects.
14875 */
14877
14878 free_object_addresses(objects);
14879
14880 /*
14881 * The objects will get recreated during subsequent passes over the work
14882 * queue.
14883 */
14884}
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3583
Oid get_typ_typrelid(Oid typid)
Definition: lsyscache.c:2814
#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:14895

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

14897{
14898 List *raw_parsetree_list;
14899 List *querytree_list;
14900 ListCell *list_item;
14901 Relation rel;
14902
14903 /*
14904 * We expect that we will get only ALTER TABLE and CREATE INDEX
14905 * statements. Hence, there is no need to pass them through
14906 * parse_analyze_*() or the rewriter, but instead we need to pass them
14907 * through parse_utilcmd.c to make them ready for execution.
14908 */
14909 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
14910 querytree_list = NIL;
14911 foreach(list_item, raw_parsetree_list)
14912 {
14913 RawStmt *rs = lfirst_node(RawStmt, list_item);
14914 Node *stmt = rs->stmt;
14915
14916 if (IsA(stmt, IndexStmt))
14917 querytree_list = lappend(querytree_list,
14918 transformIndexStmt(oldRelId,
14919 (IndexStmt *) stmt,
14920 cmd));
14921 else if (IsA(stmt, AlterTableStmt))
14922 {
14923 List *beforeStmts;
14924 List *afterStmts;
14925
14926 stmt = (Node *) transformAlterTableStmt(oldRelId,
14927 (AlterTableStmt *) stmt,
14928 cmd,
14929 &beforeStmts,
14930 &afterStmts);
14931 querytree_list = list_concat(querytree_list, beforeStmts);
14932 querytree_list = lappend(querytree_list, stmt);
14933 querytree_list = list_concat(querytree_list, afterStmts);
14934 }
14935 else if (IsA(stmt, CreateStatsStmt))
14936 querytree_list = lappend(querytree_list,
14937 transformStatsStmt(oldRelId,
14939 cmd));
14940 else
14941 querytree_list = lappend(querytree_list, stmt);
14942 }
14943
14944 /* Caller should already have acquired whatever lock we need. */
14945 rel = relation_open(oldRelId, NoLock);
14946
14947 /*
14948 * Attach each generated command to the proper place in the work queue.
14949 * Note this could result in creation of entirely new work-queue entries.
14950 *
14951 * Also note that we have to tweak the command subtypes, because it turns
14952 * out that re-creation of indexes and constraints has to act a bit
14953 * differently from initial creation.
14954 */
14955 foreach(list_item, querytree_list)
14956 {
14957 Node *stm = (Node *) lfirst(list_item);
14958 AlteredTableInfo *tab;
14959
14960 tab = ATGetQueueEntry(wqueue, rel);
14961
14962 if (IsA(stm, IndexStmt))
14963 {
14964 IndexStmt *stmt = (IndexStmt *) stm;
14965 AlterTableCmd *newcmd;
14966
14967 if (!rewrite)
14968 TryReuseIndex(oldId, stmt);
14969 stmt->reset_default_tblspc = true;
14970 /* keep the index's comment */
14971 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14972
14973 newcmd = makeNode(AlterTableCmd);
14974 newcmd->subtype = AT_ReAddIndex;
14975 newcmd->def = (Node *) stmt;
14977 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14978 }
14979 else if (IsA(stm, AlterTableStmt))
14980 {
14982 ListCell *lcmd;
14983
14984 foreach(lcmd, stmt->cmds)
14985 {
14987
14988 if (cmd->subtype == AT_AddIndex)
14989 {
14990 IndexStmt *indstmt;
14991 Oid indoid;
14992
14993 indstmt = castNode(IndexStmt, cmd->def);
14994 indoid = get_constraint_index(oldId);
14995
14996 if (!rewrite)
14997 TryReuseIndex(indoid, indstmt);
14998 /* keep any comment on the index */
14999 indstmt->idxcomment = GetComment(indoid,
15000 RelationRelationId, 0);
15001 indstmt->reset_default_tblspc = true;
15002
15003 cmd->subtype = AT_ReAddIndex;
15005 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15006
15007 /* recreate any comment on the constraint */
15010 oldId,
15011 rel,
15012 NIL,
15013 indstmt->idxname);
15014 }
15015 else if (cmd->subtype == AT_AddConstraint)
15016 {
15017 Constraint *con = castNode(Constraint, cmd->def);
15018
15019 con->old_pktable_oid = refRelId;
15020 /* rewriting neither side of a FK */
15021 if (con->contype == CONSTR_FOREIGN &&
15022 !rewrite && tab->rewrite == 0)
15023 TryReuseForeignKey(oldId, con);
15024 con->reset_default_tblspc = true;
15028
15029 /*
15030 * Recreate any comment on the constraint. If we have
15031 * recreated a primary key, then transformTableConstraint
15032 * has added an unnamed not-null constraint here; skip
15033 * this in that case.
15034 */
15035 if (con->conname)
15038 oldId,
15039 rel,
15040 NIL,
15041 con->conname);
15042 else
15043 Assert(con->contype == CONSTR_NOTNULL);
15044 }
15045 else
15046 elog(ERROR, "unexpected statement subtype: %d",
15047 (int) cmd->subtype);
15048 }
15049 }
15050 else if (IsA(stm, AlterDomainStmt))
15051 {
15053
15054 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
15055 {
15056 Constraint *con = castNode(Constraint, stmt->def);
15058
15060 cmd->def = (Node *) stmt;
15063
15064 /* recreate any comment on the constraint */
15067 oldId,
15068 NULL,
15069 stmt->typeName,
15070 con->conname);
15071 }
15072 else
15073 elog(ERROR, "unexpected statement subtype: %d",
15074 (int) stmt->subtype);
15075 }
15076 else if (IsA(stm, CreateStatsStmt))
15077 {
15079 AlterTableCmd *newcmd;
15080
15081 /* keep the statistics object's comment */
15082 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15083
15084 newcmd = makeNode(AlterTableCmd);
15085 newcmd->subtype = AT_ReAddStatistics;
15086 newcmd->def = (Node *) stmt;
15087 tab->subcmds[AT_PASS_MISC] =
15088 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15089 }
15090 else
15091 elog(ERROR, "unexpected statement type: %d",
15092 (int) nodeTag(stm));
15093 }
15094
15095 relation_close(rel, NoLock);
15096}
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:1148
#define nodeTag(nodeptr)
Definition: nodes.h:135
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:2840
bool reset_default_tblspc
Definition: parsenodes.h:3464
char * idxname
Definition: parsenodes.h:3438
char * idxcomment
Definition: parsenodes.h:3448
Node * stmt
Definition: parsenodes.h:2066
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:15153
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:15182
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:15109

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

7099{
7100 if (rel->rd_rel->reloftype && !recursing)
7101 ereport(ERROR,
7102 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7103 errmsg("cannot add column to typed table")));
7104
7105 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7106 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7107
7108 if (recurse && !is_view)
7109 cmd->recurse = true;
7110}
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6794

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

16507{
16508 if (child_rel->rd_rel->reloftype)
16509 ereport(ERROR,
16510 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16511 errmsg("cannot change inheritance of typed table")));
16512
16513 if (child_rel->rd_rel->relispartition)
16514 ereport(ERROR,
16515 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16516 errmsg("cannot change inheritance of a partition")));
16517
16518 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16519 ereport(ERROR,
16520 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16521 errmsg("cannot change inheritance of partitioned table")));
16522}

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

9343{
9344 ListCell *lc;
9345 Constraint *pkconstr;
9346
9347 pkconstr = castNode(Constraint, cmd->def);
9348 if (pkconstr->contype != CONSTR_PRIMARY)
9349 return;
9350
9351 /*
9352 * If not recursing, we must ensure that all children have a NOT NULL
9353 * constraint on the columns, and error out if not.
9354 */
9355 if (!recurse)
9356 {
9357 List *children;
9358
9360 lockmode);
9361 foreach_oid(childrelid, children)
9362 {
9363 foreach(lc, pkconstr->keys)
9364 {
9365 HeapTuple tup;
9366 Form_pg_attribute attrForm;
9367 char *attname = strVal(lfirst(lc));
9368
9369 tup = SearchSysCacheAttName(childrelid, attname);
9370 if (!tup)
9371 elog(ERROR, "cache lookup failed for attribute %s of relation %u",
9372 attname, childrelid);
9373 attrForm = (Form_pg_attribute) GETSTRUCT(tup);
9374 if (!attrForm->attnotnull)
9375 ereport(ERROR,
9376 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9377 attname, get_rel_name(childrelid)));
9378 ReleaseSysCache(tup);
9379 }
9380 }
9381 }
9382
9383 /* Insert not-null constraints in the queue for the PK columns */
9384 foreach(lc, pkconstr->keys)
9385 {
9386 AlterTableCmd *newcmd;
9387 Constraint *nnconstr;
9388
9389 nnconstr = makeNotNullConstraint(lfirst(lc));
9390
9391 newcmd = makeNode(AlterTableCmd);
9392 newcmd->subtype = AT_AddConstraint;
9393 newcmd->recurse = true;
9394 newcmd->def = (Node *) nnconstr;
9395
9396 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9397 }
9398}
NameData attname
Definition: pg_attribute.h:41
List * keys
Definition: parsenodes.h:2830

References AT_AddConstraint, ATPrepCmd(), attname, castNode, CONSTR_PRIMARY, Constraint::contype, AlterTableCmd::def, elog, ereport, errmsg(), ERROR, find_inheritance_children(), foreach_oid, get_rel_name(), GETSTRUCT(), Constraint::keys, lfirst, makeNode, makeNotNullConstraint(), AlterTableCmd::recurse, RelationGetRelid, ReleaseSysCache(), SearchSysCacheAttName(), strVal, and AlterTableCmd::subtype.

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

13671{
13672 char *colName = cmd->name;
13673 ColumnDef *def = (ColumnDef *) cmd->def;
13674 TypeName *typeName = def->typeName;
13675 Node *transform = def->cooked_default;
13676 HeapTuple tuple;
13677 Form_pg_attribute attTup;
13679 Oid targettype;
13680 int32 targettypmod;
13681 Oid targetcollid;
13683 ParseState *pstate = make_parsestate(NULL);
13684 AclResult aclresult;
13685 bool is_expr;
13686
13687 pstate->p_sourcetext = context->queryString;
13688
13689 if (rel->rd_rel->reloftype && !recursing)
13690 ereport(ERROR,
13691 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13692 errmsg("cannot alter column type of typed table"),
13693 parser_errposition(pstate, def->location)));
13694
13695 /* lookup the attribute so we can check inheritance status */
13696 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13697 if (!HeapTupleIsValid(tuple))
13698 ereport(ERROR,
13699 (errcode(ERRCODE_UNDEFINED_COLUMN),
13700 errmsg("column \"%s\" of relation \"%s\" does not exist",
13701 colName, RelationGetRelationName(rel)),
13702 parser_errposition(pstate, def->location)));
13703 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
13704 attnum = attTup->attnum;
13705
13706 /* Can't alter a system attribute */
13707 if (attnum <= 0)
13708 ereport(ERROR,
13709 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13710 errmsg("cannot alter system column \"%s\"", colName),
13711 parser_errposition(pstate, def->location)));
13712
13713 /*
13714 * Cannot specify USING when altering type of a generated column, because
13715 * that would violate the generation expression.
13716 */
13717 if (attTup->attgenerated && def->cooked_default)
13718 ereport(ERROR,
13719 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
13720 errmsg("cannot specify USING when altering type of generated column"),
13721 errdetail("Column \"%s\" is a generated column.", colName),
13722 parser_errposition(pstate, def->location)));
13723
13724 /*
13725 * Don't alter inherited columns. At outer level, there had better not be
13726 * any inherited definition; when recursing, we assume this was checked at
13727 * the parent level (see below).
13728 */
13729 if (attTup->attinhcount > 0 && !recursing)
13730 ereport(ERROR,
13731 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13732 errmsg("cannot alter inherited column \"%s\"", colName),
13733 parser_errposition(pstate, def->location)));
13734
13735 /* Don't alter columns used in the partition key */
13736 if (has_partition_attrs(rel,
13738 &is_expr))
13739 ereport(ERROR,
13740 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13741 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13742 colName, RelationGetRelationName(rel)),
13743 parser_errposition(pstate, def->location)));
13744
13745 /* Look up the target type */
13746 typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
13747
13748 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
13749 if (aclresult != ACLCHECK_OK)
13750 aclcheck_error_type(aclresult, targettype);
13751
13752 /* And the collation */
13753 targetcollid = GetColumnDefCollation(pstate, def, targettype);
13754
13755 /* make sure datatype is legal for a column */
13756 CheckAttributeType(colName, targettype, targetcollid,
13757 list_make1_oid(rel->rd_rel->reltype),
13758 0);
13759
13760 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
13761 {
13762 /* do nothing */
13763 }
13764 else if (tab->relkind == RELKIND_RELATION ||
13765 tab->relkind == RELKIND_PARTITIONED_TABLE)
13766 {
13767 /*
13768 * Set up an expression to transform the old data value to the new
13769 * type. If a USING option was given, use the expression as
13770 * transformed by transformAlterTableStmt, else just take the old
13771 * value and try to coerce it. We do this first so that type
13772 * incompatibility can be detected before we waste effort, and because
13773 * we need the expression to be parsed against the original table row
13774 * type.
13775 */
13776 if (!transform)
13777 {
13778 transform = (Node *) makeVar(1, attnum,
13779 attTup->atttypid, attTup->atttypmod,
13780 attTup->attcollation,
13781 0);
13782 }
13783
13784 transform = coerce_to_target_type(pstate,
13785 transform, exprType(transform),
13786 targettype, targettypmod,
13789 -1);
13790 if (transform == NULL)
13791 {
13792 /* error text depends on whether USING was specified or not */
13793 if (def->cooked_default != NULL)
13794 ereport(ERROR,
13795 (errcode(ERRCODE_DATATYPE_MISMATCH),
13796 errmsg("result of USING clause for column \"%s\""
13797 " cannot be cast automatically to type %s",
13798 colName, format_type_be(targettype)),
13799 errhint("You might need to add an explicit cast.")));
13800 else
13801 ereport(ERROR,
13802 (errcode(ERRCODE_DATATYPE_MISMATCH),
13803 errmsg("column \"%s\" cannot be cast automatically to type %s",
13804 colName, format_type_be(targettype)),
13805 !attTup->attgenerated ?
13806 /* translator: USING is SQL, don't translate it */
13807 errhint("You might need to specify \"USING %s::%s\".",
13808 quote_identifier(colName),
13809 format_type_with_typemod(targettype,
13810 targettypmod)) : 0));
13811 }
13812
13813 /* Fix collations after all else */
13814 assign_expr_collations(pstate, transform);
13815
13816 /* Plan the expr now so we can accurately assess the need to rewrite. */
13817 transform = (Node *) expression_planner((Expr *) transform);
13818
13819 /*
13820 * Add a work queue item to make ATRewriteTable update the column
13821 * contents.
13822 */
13824 newval->attnum = attnum;
13825 newval->expr = (Expr *) transform;
13826 newval->is_generated = false;
13827
13828 tab->newvals = lappend(tab->newvals, newval);
13829 if (ATColumnChangeRequiresRewrite(transform, attnum))
13831 }
13832 else if (transform)
13833 ereport(ERROR,
13834 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13835 errmsg("\"%s\" is not a table",
13837
13838 if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
13839 {
13840 /*
13841 * For relations or columns without storage, do this check now.
13842 * Regular tables will check it later when the table is being
13843 * rewritten.
13844 */
13845 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13846 }
13847
13848 ReleaseSysCache(tuple);
13849
13850 /*
13851 * Recurse manually by queueing a new command for each child, if
13852 * necessary. We cannot apply ATSimpleRecursion here because we need to
13853 * remap attribute numbers in the USING expression, if any.
13854 *
13855 * If we are told not to recurse, there had better not be any child
13856 * tables; else the alter would put them out of step.
13857 */
13858 if (recurse)
13859 {
13860 Oid relid = RelationGetRelid(rel);
13861 List *child_oids,
13862 *child_numparents;
13863 ListCell *lo,
13864 *li;
13865
13866 child_oids = find_all_inheritors(relid, lockmode,
13867 &child_numparents);
13868
13869 /*
13870 * find_all_inheritors does the recursive search of the inheritance
13871 * hierarchy, so all we have to do is process all of the relids in the
13872 * list that it returns.
13873 */
13874 forboth(lo, child_oids, li, child_numparents)
13875 {
13876 Oid childrelid = lfirst_oid(lo);
13877 int numparents = lfirst_int(li);
13878 Relation childrel;
13879 HeapTuple childtuple;
13880 Form_pg_attribute childattTup;
13881
13882 if (childrelid == relid)
13883 continue;
13884
13885 /* find_all_inheritors already got lock */
13886 childrel = relation_open(childrelid, NoLock);
13887 CheckAlterTableIsSafe(childrel);
13888
13889 /*
13890 * Verify that the child doesn't have any inherited definitions of
13891 * this column that came from outside this inheritance hierarchy.
13892 * (renameatt makes a similar test, though in a different way
13893 * because of its different recursion mechanism.)
13894 */
13895 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
13896 colName);
13897 if (!HeapTupleIsValid(childtuple))
13898 ereport(ERROR,
13899 (errcode(ERRCODE_UNDEFINED_COLUMN),
13900 errmsg("column \"%s\" of relation \"%s\" does not exist",
13901 colName, RelationGetRelationName(childrel))));
13902 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
13903
13904 if (childattTup->attinhcount > numparents)
13905 ereport(ERROR,
13906 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13907 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13908 colName, RelationGetRelationName(childrel))));
13909
13910 ReleaseSysCache(childtuple);
13911
13912 /*
13913 * Remap the attribute numbers. If no USING expression was
13914 * specified, there is no need for this step.
13915 */
13916 if (def->cooked_default)
13917 {
13918 AttrMap *attmap;
13919 bool found_whole_row;
13920
13921 /* create a copy to scribble on */
13922 cmd = copyObject(cmd);
13923
13924 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13925 RelationGetDescr(rel),
13926 false);
13927 ((ColumnDef *) cmd->def)->cooked_default =
13929 1, 0,
13930 attmap,
13931 InvalidOid, &found_whole_row);
13932 if (found_whole_row)
13933 ereport(ERROR,
13934 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13935 errmsg("cannot convert whole-row table reference"),
13936 errdetail("USING expression contains a whole-row table reference.")));
13937 pfree(attmap);
13938 }
13939 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
13940 relation_close(childrel, NoLock);
13941 }
13942 }
13943 else if (!recursing &&
13945 ereport(ERROR,
13946 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13947 errmsg("type of inherited column \"%s\" must be changed in child tables too",
13948 colName)));
13949
13950 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
13951 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
13952}
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition: aclchk.c:2941
#define AT_REWRITE_COLUMN_REWRITE
Definition: event_trigger.h:42
char * format_type_with_typemod(Oid type_oid, int32 typemod)
Definition: format_type.c:362
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
void assign_expr_collations(ParseState *pstate, Node *expr)
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
#define ACL_USAGE
Definition: parsenodes.h:84
#define lfirst_int(lc)
Definition: pg_list.h:173
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:13019
Node * cooked_default
Definition: parsenodes.h:747
ParseLoc location
Definition: parsenodes.h:756
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
Definition: tablecmds.c:13969
void find_composite_type_dependencies(Oid typeOid, Relation origRelation, const char *origTypeName)
Definition: tablecmds.c:6839

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

18089{
18090 Relation pg_constraint;
18091 HeapTuple tuple;
18092 SysScanDesc scan;
18093 ScanKeyData skey[1];
18094
18095 /*
18096 * Disallow changing status for a temp table. Also verify whether we can
18097 * get away with doing nothing; in such cases we don't need to run the
18098 * checks below, either.
18099 */
18100 switch (rel->rd_rel->relpersistence)
18101 {
18102 case RELPERSISTENCE_TEMP:
18103 ereport(ERROR,
18104 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18105 errmsg("cannot change logged status of table \"%s\" because it is temporary",
18107 errtable(rel)));
18108 break;
18109 case RELPERSISTENCE_PERMANENT:
18110 if (toLogged)
18111 /* nothing to do */
18112 return;
18113 break;
18114 case RELPERSISTENCE_UNLOGGED:
18115 if (!toLogged)
18116 /* nothing to do */
18117 return;
18118 break;
18119 }
18120
18121 /*
18122 * Check that the table is not part of any publication when changing to
18123 * UNLOGGED, as UNLOGGED tables can't be published.
18124 */
18125 if (!toLogged &&
18127 ereport(ERROR,
18128 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18129 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18131 errdetail("Unlogged relations cannot be replicated.")));
18132
18133 /*
18134 * Check existing foreign key constraints to preserve the invariant that
18135 * permanent tables cannot reference unlogged ones. Self-referencing
18136 * foreign keys can safely be ignored.
18137 */
18138 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18139
18140 /*
18141 * Scan conrelid if changing to permanent, else confrelid. This also
18142 * determines whether a useful index exists.
18143 */
18144 ScanKeyInit(&skey[0],
18145 toLogged ? Anum_pg_constraint_conrelid :
18146 Anum_pg_constraint_confrelid,
18147 BTEqualStrategyNumber, F_OIDEQ,
18149 scan = systable_beginscan(pg_constraint,
18150 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18151 true, NULL, 1, skey);
18152
18153 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18154 {
18156
18157 if (con->contype == CONSTRAINT_FOREIGN)
18158 {
18159 Oid foreignrelid;
18160 Relation foreignrel;
18161
18162 /* the opposite end of what we used as scankey */
18163 foreignrelid = toLogged ? con->confrelid : con->conrelid;
18164
18165 /* ignore if self-referencing */
18166 if (RelationGetRelid(rel) == foreignrelid)
18167 continue;
18168
18169 foreignrel = relation_open(foreignrelid, AccessShareLock);
18170
18171 if (toLogged)
18172 {
18173 if (!RelationIsPermanent(foreignrel))
18174 ereport(ERROR,
18175 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18176 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18178 RelationGetRelationName(foreignrel)),
18179 errtableconstraint(rel, NameStr(con->conname))));
18180 }
18181 else
18182 {
18183 if (RelationIsPermanent(foreignrel))
18184 ereport(ERROR,
18185 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18186 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18188 RelationGetRelationName(foreignrel)),
18189 errtableconstraint(rel, NameStr(con->conname))));
18190 }
18191
18192 relation_close(foreignrel, AccessShareLock);
18193 }
18194 }
18195
18196 systable_endscan(scan);
18197
18198 table_close(pg_constraint, AccessShareLock);
18199
18200 /* force rewrite if necessary; see comment in ATRewriteTables */
18202 if (toLogged)
18203 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18204 else
18205 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18206 tab->chgPersistence = true;
18207}
#define AT_REWRITE_ALTER_PERSISTENCE
Definition: event_trigger.h:40
int errtableconstraint(Relation rel, const char *conname)
Definition: relcache.c:6031
int errtable(Relation rel)
Definition: relcache.c:5977

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

4869{
4870 AlteredTableInfo *tab;
4872
4873 /* Find or create work queue entry for this table */
4874 tab = ATGetQueueEntry(wqueue, rel);
4875
4876 /*
4877 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4878 * partitions that are pending detach.
4879 */
4880 if (rel->rd_rel->relispartition &&
4883 ereport(ERROR,
4884 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4885 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4887 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4888
4889 /*
4890 * Copy the original subcommand for each table, so we can scribble on it.
4891 * This avoids conflicts when different child tables need to make
4892 * different parse transformations (for example, the same column may have
4893 * different column numbers in different children).
4894 */
4895 cmd = copyObject(cmd);
4896
4897 /*
4898 * Do permissions and relkind checking, recursion to child tables if
4899 * needed, and any additional phase-1 processing needed. (But beware of
4900 * adding any processing that looks at table details that another
4901 * subcommand could change. In some cases we reject multiple subcommands
4902 * that could try to change the same state in contrary ways.)
4903 */
4904 switch (cmd->subtype)
4905 {
4906 case AT_AddColumn: /* ADD COLUMN */
4907 ATSimplePermissions(cmd->subtype, rel,
4910 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4911 lockmode, context);
4912 /* Recursion occurs during execution phase */
4913 pass = AT_PASS_ADD_COL;
4914 break;
4915 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4917 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4918 lockmode, context);
4919 /* Recursion occurs during execution phase */
4920 pass = AT_PASS_ADD_COL;
4921 break;
4922 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4923
4924 /*
4925 * We allow defaults on views so that INSERT into a view can have
4926 * default-ish behavior. This works because the rewriter
4927 * substitutes default values into INSERTs before it expands
4928 * rules.
4929 */
4930 ATSimplePermissions(cmd->subtype, rel,
4933 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4934 /* No command-specific prep needed */
4936 break;
4937 case AT_CookedColumnDefault: /* add a pre-cooked default */
4938 /* This is currently used only in CREATE TABLE */
4939 /* (so the permission check really isn't necessary) */
4940 ATSimplePermissions(cmd->subtype, rel,
4942 /* This command never recurses */
4944 break;
4945 case AT_AddIdentity:
4946 ATSimplePermissions(cmd->subtype, rel,
4949 /* Set up recursion for phase 2; no other prep needed */
4950 if (recurse)
4951 cmd->recurse = true;
4953 break;
4954 case AT_SetIdentity:
4955 ATSimplePermissions(cmd->subtype, rel,
4958 /* Set up recursion for phase 2; no other prep needed */
4959 if (recurse)
4960 cmd->recurse = true;
4961 /* This should run after AddIdentity, so do it in MISC pass */
4962 pass = AT_PASS_MISC;
4963 break;
4964 case AT_DropIdentity:
4965 ATSimplePermissions(cmd->subtype, rel,
4968 /* Set up recursion for phase 2; no other prep needed */
4969 if (recurse)
4970 cmd->recurse = true;
4971 pass = AT_PASS_DROP;
4972 break;
4973 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4974 ATSimplePermissions(cmd->subtype, rel,
4976 /* Set up recursion for phase 2; no other prep needed */
4977 if (recurse)
4978 cmd->recurse = true;
4979 pass = AT_PASS_DROP;
4980 break;
4981 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4982 ATSimplePermissions(cmd->subtype, rel,
4984 /* Set up recursion for phase 2; no other prep needed */
4985 if (recurse)
4986 cmd->recurse = true;
4987 pass = AT_PASS_COL_ATTRS;
4988 break;
4989 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4990 ATSimplePermissions(cmd->subtype, rel,
4992 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4994 break;
4995 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4996 ATSimplePermissions(cmd->subtype, rel,
4998 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4999 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5000 pass = AT_PASS_DROP;
5001 break;
5002 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5003 ATSimplePermissions(cmd->subtype, rel,
5006 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5007 /* No command-specific prep needed */
5008 pass = AT_PASS_MISC;
5009 break;
5010 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5011 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5012 ATSimplePermissions(cmd->subtype, rel,
5015 /* This command never recurses */
5016 pass = AT_PASS_MISC;
5017 break;
5018 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5019 ATSimplePermissions(cmd->subtype, rel,
5022 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5023 /* No command-specific prep needed */
5024 pass = AT_PASS_MISC;
5025 break;
5026 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5027 ATSimplePermissions(cmd->subtype, rel,
5029 /* This command never recurses */
5030 /* No command-specific prep needed */
5031 pass = AT_PASS_MISC;
5032 break;
5033 case AT_DropColumn: /* DROP COLUMN */
5034 ATSimplePermissions(cmd->subtype, rel,
5037 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5038 lockmode, context);
5039 /* Recursion occurs during execution phase */
5040 pass = AT_PASS_DROP;
5041 break;
5042 case AT_AddIndex: /* ADD INDEX */
5044 /* This command never recurses */
5045 /* No command-specific prep needed */
5046 pass = AT_PASS_ADD_INDEX;
5047 break;
5048 case AT_AddConstraint: /* ADD CONSTRAINT */
5049 ATSimplePermissions(cmd->subtype, rel,
5051 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5052 if (recurse)
5053 {
5054 /* recurses at exec time; lock descendants and set flag */
5055 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5056 cmd->recurse = true;
5057 }
5058 pass = AT_PASS_ADD_CONSTR;
5059 break;
5060 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5062 /* This command never recurses */
5063 /* No command-specific prep needed */
5065 break;
5066 case AT_DropConstraint: /* DROP CONSTRAINT */
5067 ATSimplePermissions(cmd->subtype, rel,
5069 ATCheckPartitionsNotInUse(rel, lockmode);
5070 /* Other recursion occurs during execution phase */
5071 /* No command-specific prep needed except saving recurse flag */
5072 if (recurse)
5073 cmd->recurse = true;
5074 pass = AT_PASS_DROP;
5075 break;
5076 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5077 ATSimplePermissions(cmd->subtype, rel,
5080 /* See comments for ATPrepAlterColumnType */
5081 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5082 AT_PASS_UNSET, context);
5083 Assert(cmd != NULL);
5084 /* Performs own recursion */
5085 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5086 lockmode, context);
5087 pass = AT_PASS_ALTER_TYPE;
5088 break;
5091 /* This command never recurses */
5092 /* No command-specific prep needed */
5093 pass = AT_PASS_MISC;
5094 break;
5095 case AT_ChangeOwner: /* ALTER OWNER */
5096 /* This command never recurses */
5097 /* No command-specific prep needed */
5098 pass = AT_PASS_MISC;
5099 break;
5100 case AT_ClusterOn: /* CLUSTER ON */
5101 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5102 ATSimplePermissions(cmd->subtype, rel,
5104 /* These commands never recurse */
5105 /* No command-specific prep needed */
5106 pass = AT_PASS_MISC;
5107 break;
5108 case AT_SetLogged: /* SET LOGGED */
5109 case AT_SetUnLogged: /* SET UNLOGGED */
5111 if (tab->chgPersistence)
5112 ereport(ERROR,
5113 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5114 errmsg("cannot change persistence setting twice")));
5115 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5116 pass = AT_PASS_MISC;
5117 break;
5118 case AT_DropOids: /* SET WITHOUT OIDS */
5119 ATSimplePermissions(cmd->subtype, rel,
5121 pass = AT_PASS_DROP;
5122 break;
5123 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5124 ATSimplePermissions(cmd->subtype, rel,
5126
5127 /* check if another access method change was already requested */
5128 if (tab->chgAccessMethod)
5129 ereport(ERROR,
5130 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5131 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5132
5133 ATPrepSetAccessMethod(tab, rel, cmd->name);
5134 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5135 break;
5136 case AT_SetTableSpace: /* SET TABLESPACE */
5139 /* This command never recurses */
5140 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5141 pass = AT_PASS_MISC; /* doesn't actually matter */
5142 break;
5143 case AT_SetRelOptions: /* SET (...) */
5144 case AT_ResetRelOptions: /* RESET (...) */
5145 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5146 ATSimplePermissions(cmd->subtype, rel,
5149 /* This command never recurses */
5150 /* No command-specific prep needed */
5151 pass = AT_PASS_MISC;
5152 break;
5153 case AT_AddInherit: /* INHERIT */
5154 ATSimplePermissions(cmd->subtype, rel,
5156 /* This command never recurses */
5157 ATPrepAddInherit(rel);
5158 pass = AT_PASS_MISC;
5159 break;
5160 case AT_DropInherit: /* NO INHERIT */
5161 ATSimplePermissions(cmd->subtype, rel,
5163 /* This command never recurses */
5164 /* No command-specific prep needed */
5165 pass = AT_PASS_MISC;
5166 break;
5167 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5168 ATSimplePermissions(cmd->subtype, rel,
5170 /* Recursion occurs during execution phase */
5171 if (recurse)
5172 cmd->recurse = true;
5173 pass = AT_PASS_MISC;
5174 break;
5175 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5176 ATSimplePermissions(cmd->subtype, rel,
5178 /* Recursion occurs during execution phase */
5179 /* No command-specific prep needed except saving recurse flag */
5180 if (recurse)
5181 cmd->recurse = true;
5182 pass = AT_PASS_MISC;
5183 break;
5184 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5185 ATSimplePermissions(cmd->subtype, rel,
5187 pass = AT_PASS_MISC;
5188 /* This command never recurses */
5189 /* No command-specific prep needed */
5190 break;
5191 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5194 case AT_EnableTrigAll:
5195 case AT_EnableTrigUser:
5196 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5197 case AT_DisableTrigAll:
5198 case AT_DisableTrigUser:
5199 ATSimplePermissions(cmd->subtype, rel,
5201 /* Set up recursion for phase 2; no other prep needed */
5202 if (recurse)
5203 cmd->recurse = true;
5204 pass = AT_PASS_MISC;
5205 break;
5206 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5209 case AT_DisableRule:
5210 case AT_AddOf: /* OF */
5211 case AT_DropOf: /* NOT OF */
5216 ATSimplePermissions(cmd->subtype, rel,
5218 /* These commands never recurse */
5219 /* No command-specific prep needed */
5220 pass = AT_PASS_MISC;
5221 break;
5222 case AT_GenericOptions:
5224 /* No command-specific prep needed */
5225 pass = AT_PASS_MISC;
5226 break;
5227 case AT_AttachPartition:
5228 ATSimplePermissions(cmd->subtype, rel,
5230 /* No command-specific prep needed */
5231 pass = AT_PASS_MISC;
5232 break;
5233 case AT_DetachPartition:
5235 /* No command-specific prep needed */
5236 pass = AT_PASS_MISC;
5237 break;
5240 /* No command-specific prep needed */
5241 pass = AT_PASS_MISC;
5242 break;
5243 default: /* oops */
5244 elog(ERROR, "unrecognized alter table type: %d",
5245 (int) cmd->subtype);
5246 pass = AT_PASS_UNSET; /* keep compiler quiet */
5247 break;
5248 }
5249 Assert(pass > AT_PASS_UNSET);
5250
5251 /* Add the subcommand to the appropriate list for phase 2 */
5252 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5253}
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:15882
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:6764
#define ATT_INDEX
Definition: tablecmds.c:331
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:8615
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9115
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:7096
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6719
#define ATT_PARTITIONED_INDEX
Definition: tablecmds.c:334
static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
Definition: tablecmds.c:18088
static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9340
#define ATT_VIEW
Definition: tablecmds.c:329
static void ATPrepAddInherit(Relation child_rel)
Definition: tablecmds.c:16506
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:13666
#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:15758

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

9118{
9119 if (rel->rd_rel->reloftype && !recursing)
9120 ereport(ERROR,
9121 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9122 errmsg("cannot drop column from typed table")));
9123
9124 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9125 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9126
9127 if (recurse)
9128 cmd->recurse = true;
9129}

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

8616{
8617 /*
8618 * Reject ONLY if there are child tables. We could implement this, but it
8619 * is a bit complicated. GENERATED clauses must be attached to the column
8620 * definition and cannot be added later like DEFAULT, so if a child table
8621 * has a generation expression that the parent does not have, the child
8622 * column will necessarily be an attislocal column. So to implement ONLY
8623 * here, we'd need extra code to update attislocal of the direct child
8624 * tables, somewhat similar to how DROP COLUMN does it, so that the
8625 * resulting state can be properly dumped and restored.
8626 */
8627 if (!recurse &&
8629 ereport(ERROR,
8630 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8631 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8632
8633 /*
8634 * Cannot drop generation expression from inherited columns.
8635 */
8636 if (!recursing)
8637 {
8638 HeapTuple tuple;
8639 Form_pg_attribute attTup;
8640
8642 if (!HeapTupleIsValid(tuple))
8643 ereport(ERROR,
8644 (errcode(ERRCODE_UNDEFINED_COLUMN),
8645 errmsg("column \"%s\" of relation \"%s\" does not exist",
8646 cmd->name, RelationGetRelationName(rel))));
8647
8648 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8649
8650 if (attTup->attinhcount > 0)
8651 ereport(ERROR,
8652 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8653 errmsg("cannot drop generation expression from inherited column")));
8654 }
8655}

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

15759{
15760 Oid amoid;
15761
15762 /*
15763 * Look up the access method name and check that it differs from the
15764 * table's current AM. If DEFAULT was specified for a partitioned table
15765 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15766 */
15767 if (amname != NULL)
15768 amoid = get_table_am_oid(amname, false);
15769 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15770 amoid = InvalidOid;
15771 else
15773
15774 /* if it's a match, phase 3 doesn't need to do anything */
15775 if (rel->rd_rel->relam == amoid)
15776 return;
15777
15778 /* Save info for Phase 3 to do the real work */
15780 tab->newAccessMethod = amoid;
15781 tab->chgAccessMethod = true;
15782}
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 15882 of file tablecmds.c.

15883{
15884 Oid tablespaceId;
15885
15886 /* Check that the tablespace exists */
15887 tablespaceId = get_tablespace_oid(tablespacename, false);
15888
15889 /* Check permissions except when moving to database's default */
15890 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
15891 {
15892 AclResult aclresult;
15893
15894 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
15895 if (aclresult != ACLCHECK_OK)
15896 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
15897 }
15898
15899 /* Save info for Phase 3 to do the real work */
15900 if (OidIsValid(tab->newTableSpace))
15901 ereport(ERROR,
15902 (errcode(ERRCODE_SYNTAX_ERROR),
15903 errmsg("cannot have multiple SET TABLESPACE subcommands")));
15904
15905 tab->newTableSpace = tablespaceId;
15906}

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

5265{
5266 ListCell *ltab;
5267
5268 /*
5269 * We process all the tables "in parallel", one pass at a time. This is
5270 * needed because we may have to propagate work from one table to another
5271 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5272 * re-adding of the foreign key constraint to the other table). Work can
5273 * only be propagated into later passes, however.
5274 */
5275 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5276 {
5277 /* Go through each table that needs to be processed */
5278 foreach(ltab, *wqueue)
5279 {
5280 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5281 List *subcmds = tab->subcmds[pass];
5282 ListCell *lcmd;
5283
5284 if (subcmds == NIL)
5285 continue;
5286
5287 /*
5288 * Open the relation and store it in tab. This allows subroutines
5289 * close and reopen, if necessary. Appropriate lock was obtained
5290 * by phase 1, needn't get it again.
5291 */
5292 tab->rel = relation_open(tab->relid, NoLock);
5293
5294 foreach(lcmd, subcmds)
5295 ATExecCmd(wqueue, tab,
5297 lockmode, pass, context);
5298
5299 /*
5300 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5301 * (this is not done in ATExecAlterColumnType since it should be
5302 * done only once if multiple columns of a table are altered).
5303 */
5304 if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5305 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5306
5307 if (tab->rel)
5308 {
5309 relation_close(tab->rel, NoLock);
5310 tab->rel = NULL;
5311 }
5312 }
5313 }
5314
5315 /* Check to see if a toast table must be added. */
5316 foreach(ltab, *wqueue)
5317 {
5318 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5319
5320 /*
5321 * If the table is source table of ATTACH PARTITION command, we did
5322 * not modify anything about it that will change its toasting
5323 * requirement, so no need to check.
5324 */
5325 if (((tab->relkind == RELKIND_RELATION ||
5326 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5327 tab->partition_constraint == NULL) ||
5328 tab->relkind == RELKIND_MATVIEW)
5329 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5330 }
5331}
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:14726
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5337
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 6087 of file tablecmds.c.

6088{
6089 Relation oldrel;
6090 Relation newrel;
6091 TupleDesc oldTupDesc;
6092 TupleDesc newTupDesc;
6093 bool needscan = false;
6094 List *notnull_attrs;
6095 int i;
6096 ListCell *l;
6097 EState *estate;
6098 CommandId mycid;
6099 BulkInsertState bistate;
6100 int ti_options;
6101 ExprState *partqualstate = NULL;
6102
6103 /*
6104 * Open the relation(s). We have surely already locked the existing
6105 * table.
6106 */
6107 oldrel = table_open(tab->relid, NoLock);
6108 oldTupDesc = tab->oldDesc;
6109 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6110
6111 if (OidIsValid(OIDNewHeap))
6112 {
6114 false));
6115 newrel = table_open(OIDNewHeap, NoLock);
6116 }
6117 else
6118 newrel = NULL;
6119
6120 /*
6121 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6122 * is empty, so don't bother using it.
6123 */
6124 if (newrel)
6125 {
6126 mycid = GetCurrentCommandId(true);
6127 bistate = GetBulkInsertState();
6128 ti_options = TABLE_INSERT_SKIP_FSM;
6129 }
6130 else
6131 {
6132 /* keep compiler quiet about using these uninitialized */
6133 mycid = 0;
6134 bistate = NULL;
6135 ti_options = 0;
6136 }
6137
6138 /*
6139 * Generate the constraint and default execution states
6140 */
6141
6142 estate = CreateExecutorState();
6143
6144 /* Build the needed expression execution states */
6145 foreach(l, tab->constraints)
6146 {
6147 NewConstraint *con = lfirst(l);
6148
6149 switch (con->contype)
6150 {
6151 case CONSTR_CHECK:
6152 needscan = true;
6153 con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6154 break;
6155 case CONSTR_FOREIGN:
6156 /* Nothing to do here */
6157 break;
6158 default:
6159 elog(ERROR, "unrecognized constraint type: %d",
6160 (int) con->contype);
6161 }
6162 }
6163
6164 /* Build expression execution states for partition check quals */
6165 if (tab->partition_constraint)
6166 {
6167 needscan = true;
6168 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6169 }
6170
6171 foreach(l, tab->newvals)
6172 {
6173 NewColumnValue *ex = lfirst(l);
6174
6175 /* expr already planned */
6176 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6177 }
6178
6179 notnull_attrs = NIL;
6180 if (newrel || tab->verify_new_notnull)
6181 {
6182 /*
6183 * If we are rebuilding the tuples OR if we added any new but not
6184 * verified not-null constraints, check all not-null constraints. This
6185 * is a bit of overkill but it minimizes risk of bugs.
6186 */
6187 for (i = 0; i < newTupDesc->natts; i++)
6188 {
6189 Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6190
6191 if (attr->attnotnull && !attr->attisdropped)
6192 notnull_attrs = lappend_int(notnull_attrs, attr->attnum);
6193 }
6194 if (notnull_attrs)
6195 needscan = true;
6196 }
6197
6198 if (newrel || needscan)
6199 {
6200 ExprContext *econtext;
6201 TupleTableSlot *oldslot;
6202 TupleTableSlot *newslot;
6203 TableScanDesc scan;
6204 MemoryContext oldCxt;
6205 List *dropped_attrs = NIL;
6206 ListCell *lc;
6207 Snapshot snapshot;
6208
6209 if (newrel)
6211 (errmsg_internal("rewriting table \"%s\"",
6212 RelationGetRelationName(oldrel))));
6213 else
6215 (errmsg_internal("verifying table \"%s\"",
6216 RelationGetRelationName(oldrel))));
6217
6218 if (newrel)
6219 {
6220 /*
6221 * All predicate locks on the tuples or pages are about to be made
6222 * invalid, because we move tuples around. Promote them to
6223 * relation locks.
6224 */
6226 }
6227
6228 econtext = GetPerTupleExprContext(estate);
6229
6230 /*
6231 * Create necessary tuple slots. When rewriting, two slots are needed,
6232 * otherwise one suffices. In the case where one slot suffices, we
6233 * need to use the new tuple descriptor, otherwise some constraints
6234 * can't be evaluated. Note that even when the tuple layout is the
6235 * same and no rewrite is required, the tupDescs might not be
6236 * (consider ADD COLUMN without a default).
6237 */
6238 if (tab->rewrite)
6239 {
6240 Assert(newrel != NULL);
6241 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6242 table_slot_callbacks(oldrel));
6243 newslot = MakeSingleTupleTableSlot(newTupDesc,
6244 table_slot_callbacks(newrel));
6245
6246 /*
6247 * Set all columns in the new slot to NULL initially, to ensure
6248 * columns added as part of the rewrite are initialized to NULL.
6249 * That is necessary as tab->newvals will not contain an
6250 * expression for columns with a NULL default, e.g. when adding a
6251 * column without a default together with a column with a default
6252 * requiring an actual rewrite.
6253 */
6254 ExecStoreAllNullTuple(newslot);
6255 }
6256 else
6257 {
6258 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6259 table_slot_callbacks(oldrel));
6260 newslot = NULL;
6261 }
6262
6263 /*
6264 * Any attributes that are dropped according to the new tuple
6265 * descriptor can be set to NULL. We precompute the list of dropped
6266 * attributes to avoid needing to do so in the per-tuple loop.
6267 */
6268 for (i = 0; i < newTupDesc->natts; i++)
6269 {
6270 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6271 dropped_attrs = lappend_int(dropped_attrs, i);
6272 }
6273
6274 /*
6275 * Scan through the rows, generating a new row if needed and then
6276 * checking all the constraints.
6277 */
6278 snapshot = RegisterSnapshot(GetLatestSnapshot());
6279 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6280
6281 /*
6282 * Switch to per-tuple memory context and reset it for each tuple
6283 * produced, so we don't leak memory.
6284 */
6286
6287 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6288 {
6289 TupleTableSlot *insertslot;
6290
6291 if (tab->rewrite > 0)
6292 {
6293 /* Extract data from old tuple */
6294 slot_getallattrs(oldslot);
6295 ExecClearTuple(newslot);
6296
6297 /* copy attributes */
6298 memcpy(newslot->tts_values, oldslot->tts_values,
6299 sizeof(Datum) * oldslot->tts_nvalid);
6300 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6301 sizeof(bool) * oldslot->tts_nvalid);
6302
6303 /* Set dropped attributes to null in new tuple */
6304 foreach(lc, dropped_attrs)
6305 newslot->tts_isnull[lfirst_int(lc)] = true;
6306
6307 /*
6308 * Constraints and GENERATED expressions might reference the
6309 * tableoid column, so fill tts_tableOid with the desired
6310 * value. (We must do this each time, because it gets
6311 * overwritten with newrel's OID during storing.)
6312 */
6313 newslot->tts_tableOid = RelationGetRelid(oldrel);
6314
6315 /*
6316 * Process supplied expressions to replace selected columns.
6317 *
6318 * First, evaluate expressions whose inputs come from the old
6319 * tuple.
6320 */
6321 econtext->ecxt_scantuple = oldslot;
6322
6323 foreach(l, tab->newvals)
6324 {
6325 NewColumnValue *ex = lfirst(l);
6326
6327 if (ex->is_generated)
6328 continue;
6329
6330 newslot->tts_values[ex->attnum - 1]
6331 = ExecEvalExpr(ex->exprstate,
6332 econtext,
6333 &newslot->tts_isnull[ex->attnum - 1]);
6334 }
6335
6336 ExecStoreVirtualTuple(newslot);
6337
6338 /*
6339 * Now, evaluate any expressions whose inputs come from the
6340 * new tuple. We assume these columns won't reference each
6341 * other, so that there's no ordering dependency.
6342 */
6343 econtext->ecxt_scantuple = newslot;
6344
6345 foreach(l, tab->newvals)
6346 {
6347 NewColumnValue *ex = lfirst(l);
6348
6349 if (!ex->is_generated)
6350 continue;
6351
6352 newslot->tts_values[ex->attnum - 1]
6353 = ExecEvalExpr(ex->exprstate,
6354 econtext,
6355 &newslot->tts_isnull[ex->attnum - 1]);
6356 }
6357
6358 insertslot = newslot;
6359 }
6360 else
6361 {
6362 /*
6363 * If there's no rewrite, old and new table are guaranteed to
6364 * have the same AM, so we can just use the old slot to verify
6365 * new constraints etc.
6366 */
6367 insertslot = oldslot;
6368 }
6369
6370 /* Now check any constraints on the possibly-changed tuple */
6371 econtext->ecxt_scantuple = insertslot;
6372
6373 foreach_int(attn, notnull_attrs)
6374 {
6375 if (slot_attisnull(insertslot, attn))
6376 {
6377 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6378
6379 ereport(ERROR,
6380 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6381 errmsg("column \"%s\" of relation \"%s\" contains null values",
6382 NameStr(attr->attname),
6383 RelationGetRelationName(oldrel)),
6384 errtablecol(oldrel, attn)));
6385 }
6386 }
6387
6388 foreach(l, tab->constraints)
6389 {
6390 NewConstraint *con = lfirst(l);
6391
6392 switch (con->contype)
6393 {
6394 case CONSTR_CHECK:
6395 if (!ExecCheck(con->qualstate, econtext))
6396 ereport(ERROR,
6397 (errcode(ERRCODE_CHECK_VIOLATION),
6398 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6399 con->name,
6400 RelationGetRelationName(oldrel)),
6401 errtableconstraint(oldrel, con->name)));
6402 break;
6403 case CONSTR_NOTNULL:
6404 case CONSTR_FOREIGN:
6405 /* Nothing to do here */
6406 break;
6407 default:
6408 elog(ERROR, "unrecognized constraint type: %d",
6409 (int) con->contype);
6410 }
6411 }
6412
6413 if (partqualstate && !ExecCheck(partqualstate, econtext))
6414 {
6415 if (tab->validate_default)
6416 ereport(ERROR,
6417 (errcode(ERRCODE_CHECK_VIOLATION),
6418 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6419 RelationGetRelationName(oldrel)),
6420 errtable(oldrel)));
6421 else
6422 ereport(ERROR,
6423 (errcode(ERRCODE_CHECK_VIOLATION),
6424 errmsg("partition constraint of relation \"%s\" is violated by some row",
6425 RelationGetRelationName(oldrel)),
6426 errtable(oldrel)));
6427 }
6428
6429 /* Write the tuple out to the new relation */
6430 if (newrel)
6431 table_tuple_insert(newrel, insertslot, mycid,
6432 ti_options, bistate);
6433
6434 ResetExprContext(econtext);
6435
6437 }
6438
6439 MemoryContextSwitchTo(oldCxt);
6440 table_endscan(scan);
6441 UnregisterSnapshot(snapshot);
6442
6444 if (newslot)
6446 }
6447
6448 FreeExecutorState(estate);
6449
6450 table_close(oldrel, NoLock);
6451 if (newrel)
6452 {
6453 FreeBulkInsertState(bistate);
6454
6455 table_finish_bulk_insert(newrel, ti_options);
6456
6457 table_close(newrel, NoLock);
6458 }
6459}
uint32 CommandId
Definition: c.h:637
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1157
#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
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1425
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1441
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1739
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1763
#define ResetExprContext(econtext)
Definition: executor.h:631
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:642
BulkInsertState GetBulkInsertState(void)
Definition: heapam.c:2022
void FreeBulkInsertState(BulkInsertState bistate)
Definition: heapam.c:2039
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:3113
int errtablecol(Relation rel, int attnum)
Definition: relcache.c:5994
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
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:268
AttrNumber attnum
Definition: tablecmds.c:236
bool is_generated
Definition: tablecmds.c:239
ExprState * exprstate
Definition: tablecmds.c:238
ExprState * qualstate
Definition: tablecmds.c:223
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:877
#define TABLE_INSERT_SKIP_FSM
Definition: tableam.h:260
static void table_finish_bulk_insert(Relation rel, int options)
Definition: tableam.h:1565
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, int options, struct BulkInsertStateData *bistate)
Definition: tableam.h:1372
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1025
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:454
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:368
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition: tuptable.h:381
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:829

References AccessExclusiveLock, Assert(), NewColumnValue::attnum, CHECK_FOR_INTERRUPTS, CheckRelationOidLockedByMe(), 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(), ExecCheck(), ExecClearTuple(), ExecDropSingleTupleTableSlot(), ExecEvalExpr(), ExecInitExpr(), ExecPrepareExpr(), ExecStoreAllNullTuple(), ExecStoreVirtualTuple(), expand_generated_columns_in_expr(), NewColumnValue::expr, NewColumnValue::exprstate, foreach_int, ForwardScanDirection, FreeBulkInsertState(), FreeExecutorState(), GetBulkInsertState(), GetCurrentCommandId(), GetLatestSnapshot(), GetPerTupleExprContext, GetPerTupleMemoryContext, i, NewColumnValue::is_generated, lappend_int(), lfirst, lfirst_int, 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(), 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 5799 of file tablecmds.c.

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

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

6643{
6644 int actual_target;
6645
6646 switch (rel->rd_rel->relkind)
6647 {
6648 case RELKIND_RELATION:
6649 actual_target = ATT_TABLE;
6650 break;
6651 case RELKIND_PARTITIONED_TABLE:
6652 actual_target = ATT_PARTITIONED_TABLE;
6653 break;
6654 case RELKIND_VIEW:
6655 actual_target = ATT_VIEW;
6656 break;
6657 case RELKIND_MATVIEW:
6658 actual_target = ATT_MATVIEW;
6659 break;
6660 case RELKIND_INDEX:
6661 actual_target = ATT_INDEX;
6662 break;
6663 case RELKIND_PARTITIONED_INDEX:
6664 actual_target = ATT_PARTITIONED_INDEX;
6665 break;
6666 case RELKIND_COMPOSITE_TYPE:
6667 actual_target = ATT_COMPOSITE_TYPE;
6668 break;
6669 case RELKIND_FOREIGN_TABLE:
6670 actual_target = ATT_FOREIGN_TABLE;
6671 break;
6672 case RELKIND_SEQUENCE:
6673 actual_target = ATT_SEQUENCE;
6674 break;
6675 default:
6676 actual_target = 0;
6677 break;
6678 }
6679
6680 /* Wrong target type? */
6681 if ((actual_target & allowed_targets) == 0)
6682 {
6683 const char *action_str = alter_table_type_to_string(cmdtype);
6684
6685 if (action_str)
6686 ereport(ERROR,
6687 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6688 /* translator: %s is a group of some SQL keywords */
6689 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6690 action_str, RelationGetRelationName(rel)),
6691 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6692 else
6693 /* internal error? */
6694 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6696 }
6697
6698 /* Permissions checks */
6699 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6702
6704 ereport(ERROR,
6705 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6706 errmsg("permission denied: \"%s\" is a system catalog",
6708}
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition: tablecmds.c:6499

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

6722{
6723 /*
6724 * Propagate to children, if desired and if there are (or might be) any
6725 * children.
6726 */
6727 if (recurse && rel->rd_rel->relhassubclass)
6728 {
6729 Oid relid = RelationGetRelid(rel);
6730 ListCell *child;
6731 List *children;
6732
6733 children = find_all_inheritors(relid, lockmode, NULL);
6734
6735 /*
6736 * find_all_inheritors does the recursive search of the inheritance
6737 * hierarchy, so all we have to do is process all of the relids in the
6738 * list that it returns.
6739 */
6740 foreach(child, children)
6741 {
6742 Oid childrelid = lfirst_oid(child);
6743 Relation childrel;
6744
6745 if (childrelid == relid)
6746 continue;
6747 /* find_all_inheritors already got lock */
6748 childrel = relation_open(childrelid, NoLock);
6749 CheckAlterTableIsSafe(childrel);
6750 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6751 relation_close(childrel, NoLock);
6752 }
6753 }
6754}

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

19829{
19830 List *idxes;
19831 List *attachRelIdxs;
19832 Relation *attachrelIdxRels;
19833 IndexInfo **attachInfos;
19834 ListCell *cell;
19835 MemoryContext cxt;
19836 MemoryContext oldcxt;
19837
19839 "AttachPartitionEnsureIndexes",
19841 oldcxt = MemoryContextSwitchTo(cxt);
19842
19843 idxes = RelationGetIndexList(rel);
19844 attachRelIdxs = RelationGetIndexList(attachrel);
19845 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19846 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19847
19848 /* Build arrays of all existing indexes and their IndexInfos */
19849 foreach_oid(cldIdxId, attachRelIdxs)
19850 {
19851 int i = foreach_current_index(cldIdxId);
19852
19853 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19854 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19855 }
19856
19857 /*
19858 * If we're attaching a foreign table, we must fail if any of the indexes
19859 * is a constraint index; otherwise, there's nothing to do here. Do this
19860 * before starting work, to avoid wasting the effort of building a few
19861 * non-unique indexes before coming across a unique one.
19862 */
19863 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19864 {
19865 foreach(cell, idxes)
19866 {
19867 Oid idx = lfirst_oid(cell);
19869
19870 if (idxRel->rd_index->indisunique ||
19871 idxRel->rd_index->indisprimary)
19872 ereport(ERROR,
19873 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19874 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19875 RelationGetRelationName(attachrel),
19877 errdetail("Partitioned table \"%s\" contains unique indexes.",
19880 }
19881
19882 goto out;
19883 }
19884
19885 /*
19886 * For each index on the partitioned table, find a matching one in the
19887 * partition-to-be; if one is not found, create one.
19888 */
19889 foreach(cell, idxes)
19890 {
19891 Oid idx = lfirst_oid(cell);
19893 IndexInfo *info;
19894 AttrMap *attmap;
19895 bool found = false;
19896 Oid constraintOid;
19897
19898 /*
19899 * Ignore indexes in the partitioned table other than partitioned
19900 * indexes.
19901 */
19902 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
19903 {
19905 continue;
19906 }
19907
19908 /* construct an indexinfo to compare existing indexes against */
19909 info = BuildIndexInfo(idxRel);
19910 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
19911 RelationGetDescr(rel),
19912 false);
19914
19915 /*
19916 * Scan the list of existing indexes in the partition-to-be, and mark
19917 * the first matching, valid, unattached one we find, if any, as
19918 * partition of the parent index. If we find one, we're done.
19919 */
19920 for (int i = 0; i < list_length(attachRelIdxs); i++)
19921 {
19922 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
19923 Oid cldConstrOid = InvalidOid;
19924
19925 /* does this index have a parent? if so, can't use it */
19926 if (attachrelIdxRels[i]->rd_rel->relispartition)
19927 continue;
19928
19929 /* If this index is invalid, can't use it */
19930 if (!attachrelIdxRels[i]->rd_index->indisvalid)
19931 continue;
19932
19933 if (CompareIndexInfo(attachInfos[i], info,
19934 attachrelIdxRels[i]->rd_indcollation,
19935 idxRel->rd_indcollation,
19936 attachrelIdxRels[i]->rd_opfamily,
19937 idxRel->rd_opfamily,
19938 attmap))
19939 {
19940 /*
19941 * If this index is being created in the parent because of a
19942 * constraint, then the child needs to have a constraint also,
19943 * so look for one. If there is no such constraint, this
19944 * index is no good, so keep looking.
19945 */
19946 if (OidIsValid(constraintOid))
19947 {
19948 cldConstrOid =
19950 cldIdxId);
19951 /* no dice */
19952 if (!OidIsValid(cldConstrOid))
19953 continue;
19954
19955 /* Ensure they're both the same type of constraint */
19956 if (get_constraint_type(constraintOid) !=
19957 get_constraint_type(cldConstrOid))
19958 continue;
19959 }
19960
19961 /* bingo. */
19962 IndexSetParentIndex(attachrelIdxRels[i], idx);
19963 if (OidIsValid(constraintOid))
19964 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
19965 RelationGetRelid(attachrel));
19966 found = true;
19967
19969 break;
19970 }
19971 }
19972
19973 /*
19974 * If no suitable index was found in the partition-to-be, create one
19975 * now. Note that if this is a PK, not-null constraints must already
19976 * exist.
19977 */
19978 if (!found)
19979 {
19980 IndexStmt *stmt;
19981 Oid conOid;
19982
19984 idxRel, attmap,
19985 &conOid);
19987 RelationGetRelid(idxRel),
19988 conOid,
19989 -1,
19990 true, false, false, false, false);
19991 }
19992
19994 }
19995
19996out:
19997 /* Clean up. */
19998 for (int i = 0; i < list_length(attachRelIdxs); i++)
19999 index_close(attachrelIdxRels[i], AccessShareLock);
20000 MemoryContextSwitchTo(oldcxt);
20002}
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
char get_constraint_type(Oid conoid)
Definition: lsyscache.c:1178
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 11539 of file tablecmds.c.

11546{
11547 HeapTuple parentConstrTup;
11548 Form_pg_constraint parentConstr;
11549 HeapTuple partcontup;
11550 Form_pg_constraint partConstr;
11551 bool queueValidation;
11552 Oid partConstrFrelid;
11553 Oid partConstrRelid;
11554 Oid insertTriggerOid,
11555 updateTriggerOid;
11556
11557 /* Fetch the parent constraint tuple */
11558 parentConstrTup = SearchSysCache1(CONSTROID,
11559 ObjectIdGetDatum(parentConstrOid));
11560 if (!HeapTupleIsValid(parentConstrTup))
11561 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11562 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11563
11564 /* Fetch the child constraint tuple */
11565 partcontup = SearchSysCache1(CONSTROID,
11566 ObjectIdGetDatum(partConstrOid));
11567 if (!HeapTupleIsValid(partcontup))
11568 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11569 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11570 partConstrFrelid = partConstr->confrelid;
11571 partConstrRelid = partConstr->conrelid;
11572
11573 /*
11574 * If the referenced table is partitioned, then the partition we're
11575 * attaching now has extra pg_constraint rows and action triggers that are
11576 * no longer needed. Remove those.
11577 */
11578 if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11579 {
11580 Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11581
11582 RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11583 partConstrRelid);
11584
11585 table_close(pg_constraint, RowShareLock);
11586 }
11587
11588 /*
11589 * Will we need to validate this constraint? A valid parent constraint
11590 * implies that all child constraints have been validated, so if this one
11591 * isn't, we must trigger phase 3 validation.
11592 */
11593 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11594
11595 ReleaseSysCache(partcontup);
11596 ReleaseSysCache(parentConstrTup);
11597
11598 /*
11599 * The action triggers in the new partition become redundant -- the parent
11600 * table already has equivalent ones, and those will be able to reach the
11601 * partition. Remove the ones in the partition. We identify them because
11602 * they have our constraint OID, as well as being on the referenced rel.
11603 */
11604 DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11605 partConstrRelid);
11606
11607 ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11608 RelationGetRelid(partition));
11609
11610 /*
11611 * Like the constraint, attach partition's "check" triggers to the
11612 * corresponding parent triggers.
11613 */
11615 partConstrOid, partConstrFrelid, partConstrRelid,
11616 &insertTriggerOid, &updateTriggerOid);
11617 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11618 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11619 RelationGetRelid(partition));
11620 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11621 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11622 RelationGetRelid(partition));
11623
11624 /*
11625 * We updated this pg_constraint row above to set its parent; validating
11626 * it will cause its convalidated flag to change, so we need CCI here. In
11627 * addition, we need it unconditionally for the rare case where the parent
11628 * table has *two* identical constraints; when reaching this function for
11629 * the second one, we must have made our changes visible, otherwise we
11630 * would try to attach both to this one.
11631 */
11633
11634 /* If validation is needed, put it in the queue now. */
11635 if (queueValidation)
11636 {
11637 Relation conrel;
11638
11639 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11640
11641 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11642 if (!HeapTupleIsValid(partcontup))
11643 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11644
11645 /* Use the same lock as for AT_ValidateConstraint */
11646 QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
11648 ReleaseSysCache(partcontup);
11650 }
11651}
static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)
Definition: tablecmds.c:11738
static void RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
Definition: tablecmds.c:11660
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:11850
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 6794 of file tablecmds.c.

6796{
6797 ListCell *child;
6798 List *children;
6799
6800 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6801
6802 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6804 cmd->behavior);
6805
6806 foreach(child, children)
6807 {
6808 Oid childrelid = lfirst_oid(child);
6809 Relation childrel;
6810
6811 childrel = relation_open(childrelid, lockmode);
6812 CheckAlterTableIsSafe(childrel);
6813 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6814 relation_close(childrel, NoLock);
6815 }
6816}
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition: tablecmds.c:6997

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

1342{
1343 int natts;
1345 ListCell *l;
1346 TupleDesc desc;
1347 char *attname;
1348 Oid atttypid;
1349 int32 atttypmod;
1350 Oid attcollation;
1351 int attdim;
1352
1353 /*
1354 * allocate a new tuple descriptor
1355 */
1356 natts = list_length(columns);
1357 desc = CreateTemplateTupleDesc(natts);
1358
1359 attnum = 0;
1360
1361 foreach(l, columns)
1362 {
1363 ColumnDef *entry = lfirst(l);
1364 AclResult aclresult;
1366
1367 /*
1368 * for each entry in the list, get the name and type information from
1369 * the list and have TupleDescInitEntry fill in the attribute
1370 * information we need.
1371 */
1372 attnum++;
1373
1374 attname = entry->colname;
1375 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1376
1377 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1378 if (aclresult != ACLCHECK_OK)
1379 aclcheck_error_type(aclresult, atttypid);
1380
1381 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1382 attdim = list_length(entry->typeName->arrayBounds);
1383 if (attdim > PG_INT16_MAX)
1384 ereport(ERROR,
1385 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1386 errmsg("too many array dimensions"));
1387
1388 if (entry->typeName->setof)
1389 ereport(ERROR,
1390 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1391 errmsg("column \"%s\" cannot be declared SETOF",
1392 attname)));
1393
1395 atttypid, atttypmod, attdim);
1396 att = TupleDescAttr(desc, attnum - 1);
1397
1398 /* Override TupleDescInitEntry's settings as requested */
1399 TupleDescInitEntryCollation(desc, attnum, attcollation);
1400
1401 /* Fill in additional stuff not handled by TupleDescInitEntry */
1402 att->attnotnull = entry->is_not_null;
1403 att->attislocal = entry->is_local;
1404 att->attinhcount = entry->inhcount;
1405 att->attidentity = entry->identity;
1406 att->attgenerated = entry->generated;
1407 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1408 if (entry->storage)
1409 att->attstorage = entry->storage;
1410 else if (entry->storage_name)
1411 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1412
1414 }
1415
1416 return desc;
1417}
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:164
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition: tupdesc.c:107
void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid)
Definition: tupdesc.c:985
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:801

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

15581{
15582 Relation attRelation;
15583 SysScanDesc scan;
15584 ScanKeyData key[1];
15585 HeapTuple attributeTuple;
15586
15587 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15588 ScanKeyInit(&key[0],
15589 Anum_pg_attribute_attrelid,
15590 BTEqualStrategyNumber, F_OIDEQ,
15591 ObjectIdGetDatum(relationOid));
15592 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15593 true, NULL, 1, key);
15594 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15595 {
15596 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15597 Datum repl_val[Natts_pg_attribute];
15598 bool repl_null[Natts_pg_attribute];
15599 bool repl_repl[Natts_pg_attribute];
15600 Acl *newAcl;
15601 Datum aclDatum;
15602 bool isNull;
15603 HeapTuple newtuple;
15604
15605 /* Ignore dropped columns */
15606 if (att->attisdropped)
15607 continue;
15608
15609 aclDatum = heap_getattr(attributeTuple,
15610 Anum_pg_attribute_attacl,
15611 RelationGetDescr(attRelation),
15612 &isNull);
15613 /* Null ACLs do not require changes */
15614 if (isNull)
15615 continue;
15616
15617 memset(repl_null, false, sizeof(repl_null));
15618 memset(repl_repl, false, sizeof(repl_repl));
15619
15620 newAcl = aclnewowner(DatumGetAclP(aclDatum),
15621 oldOwnerId, newOwnerId);
15622 repl_repl[Anum_pg_attribute_attacl - 1] = true;
15623 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15624
15625 newtuple = heap_modify_tuple(attributeTuple,
15626 RelationGetDescr(attRelation),
15627 repl_val, repl_null, repl_repl);
15628
15629 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15630
15631 heap_freetuple(newtuple);
15632 }
15633 systable_endscan(scan);
15634 table_close(attRelation, RowExclusiveLock);
15635}

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

15646{
15647 Relation depRel;
15648 SysScanDesc scan;
15649 ScanKeyData key[2];
15650 HeapTuple tup;
15651
15652 /*
15653 * SERIAL sequences are those having an auto dependency on one of the
15654 * table's columns (we don't care *which* column, exactly).
15655 */
15656 depRel = table_open(DependRelationId, AccessShareLock);
15657
15658 ScanKeyInit(&key[0],
15659 Anum_pg_depend_refclassid,
15660 BTEqualStrategyNumber, F_OIDEQ,
15661 ObjectIdGetDatum(RelationRelationId));
15662 ScanKeyInit(&key[1],
15663 Anum_pg_depend_refobjid,
15664 BTEqualStrategyNumber, F_OIDEQ,
15665 ObjectIdGetDatum(relationOid));
15666 /* we leave refobjsubid unspecified */
15667
15668 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15669 NULL, 2, key);
15670
15671 while (HeapTupleIsValid(tup = systable_getnext(scan)))
15672 {
15673 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15674 Relation seqRel;
15675
15676 /* skip dependencies other than auto dependencies on columns */
15677 if (depForm->refobjsubid == 0 ||
15678 depForm->classid != RelationRelationId ||
15679 depForm->objsubid != 0 ||
15680 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15681 continue;
15682
15683 /* Use relation_open just in case it's an index */
15684 seqRel = relation_open(depForm->objid, lockmode);
15685
15686 /* skip non-sequence relations */
15687 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15688 {
15689 /* No need to keep the lock */
15690 relation_close(seqRel, lockmode);
15691 continue;
15692 }
15693
15694 /* We don't need to close the sequence while we alter it. */
15695 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15696
15697 /* Now we can close it. Keep the lock till end of transaction. */
15698 relation_close(seqRel, NoLock);
15699 }
15700
15701 systable_endscan(scan);
15702
15704}

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

7549{
7550 HeapTuple attTuple;
7551 int attnum;
7552
7553 /*
7554 * this test is deliberately not attisdropped-aware, since if one tries to
7555 * add a column matching a dropped column name, it's gonna fail anyway.
7556 */
7557 attTuple = SearchSysCache2(ATTNAME,
7559 PointerGetDatum(colname));
7560 if (!HeapTupleIsValid(attTuple))
7561 return true;
7562
7563 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7564 ReleaseSysCache(attTuple);
7565
7566 /*
7567 * We throw a different error message for conflicts with system column
7568 * names, since they are normally not shown and the user might otherwise
7569 * be confused about the reason for the conflict.
7570 */
7571 if (attnum <= 0)
7572 ereport(ERROR,
7573 (errcode(ERRCODE_DUPLICATE_COLUMN),
7574 errmsg("column name \"%s\" conflicts with a system column name",
7575 colname)));
7576 else
7577 {
7578 if (if_not_exists)
7579 {
7581 (errcode(ERRCODE_DUPLICATE_COLUMN),
7582 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7583 colname, RelationGetRelationName(rel))));
7584 return false;
7585 }
7586
7587 ereport(ERROR,
7588 (errcode(ERRCODE_DUPLICATE_COLUMN),
7589 errmsg("column \"%s\" of relation \"%s\" already exists",
7590 colname, RelationGetRelationName(rel))));
7591 }
7592
7593 return true;
7594}
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 7046 of file tablecmds.c.

7047{
7048 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7049 bool typeOk = false;
7050
7051 if (typ->typtype == TYPTYPE_COMPOSITE)
7052 {
7053 Relation typeRelation;
7054
7055 Assert(OidIsValid(typ->typrelid));
7056 typeRelation = relation_open(typ->typrelid, AccessShareLock);
7057 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7058
7059 /*
7060 * Close the parent rel, but keep our AccessShareLock on it until xact
7061 * commit. That will prevent someone else from deleting or ALTERing
7062 * the type before the typed table creation/conversion commits.
7063 */
7064 relation_close(typeRelation, NoLock);
7065
7066 if (!typeOk)
7067 ereport(ERROR,
7068 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7069 errmsg("type %s is the row type of another table",
7070 format_type_be(typ->oid)),
7071 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7072 }
7073 else
7074 ereport(ERROR,
7075 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7076 errmsg("type %s is not a composite type",
7077 format_type_be(typ->oid))));
7078}

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

4411{
4412 /*
4413 * Don't allow ALTER on temp tables of other backends. Their local buffer
4414 * manager is not going to cope if we need to change the table's contents.
4415 * Even if we don't, there may be optimizations that assume temp tables
4416 * aren't subject to such interference.
4417 */
4418 if (RELATION_IS_OTHER_TEMP(rel))
4419 ereport(ERROR,
4420 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4421 errmsg("cannot alter temporary tables of other sessions")));
4422
4423 /*
4424 * Also check for active uses of the relation in the current transaction,
4425 * including open scans and pending AFTER trigger events.
4426 */
4427 CheckTableNotInUse(rel, "ALTER TABLE");
4428}
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4377

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

12957{
12958 Oid roleid = GetUserId();
12959 AclResult aclresult;
12960 int i;
12961
12962 /* Okay if we have relation-level REFERENCES permission */
12963 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12965 if (aclresult == ACLCHECK_OK)
12966 return;
12967 /* Else we must have REFERENCES on each column */
12968 for (i = 0; i < natts; i++)
12969 {
12970 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12971 roleid, ACL_REFERENCES);
12972 if (aclresult != ACLCHECK_OK)
12973 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12975 }
12976}
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3836
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4007
#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 3654 of file tablecmds.c.

3655{
3656 Oid oldTableSpaceId;
3657
3658 /*
3659 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3660 * stored as 0.
3661 */
3662 oldTableSpaceId = rel->rd_rel->reltablespace;
3663 if (newTableSpaceId == oldTableSpaceId ||
3664 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3665 return false;
3666
3667 /*
3668 * We cannot support moving mapped relations into different tablespaces.
3669 * (In particular this eliminates all shared catalogs.)
3670 */
3671 if (RelationIsMapped(rel))
3672 ereport(ERROR,
3673 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3674 errmsg("cannot move system relation \"%s\"",
3676
3677 /* Cannot move a non-shared relation into pg_global */
3678 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3679 ereport(ERROR,
3680 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3681 errmsg("only shared relations can be placed in pg_global tablespace")));
3682
3683 /*
3684 * Do not allow moving temp tables of other backends ... their local
3685 * buffer manager is not going to cope.
3686 */
3687 if (RELATION_IS_OTHER_TEMP(rel))
3688 ereport(ERROR,
3689 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3690 errmsg("cannot move temporary tables of other sessions")));
3691
3692 return true;
3693}
#define RelationIsMapped(relation)
Definition: rel.h:562

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

4378{
4379 int expected_refcnt;
4380
4381 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4382 if (rel->rd_refcnt != expected_refcnt)
4383 ereport(ERROR,
4384 (errcode(ERRCODE_OBJECT_IN_USE),
4385 /* translator: first %s is a SQL command, eg ALTER TABLE */
4386 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4388
4389 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4390 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4392 ereport(ERROR,
4393 (errcode(ERRCODE_OBJECT_IN_USE),
4394 /* translator: first %s is a SQL command, eg ALTER TABLE */
4395 errmsg("cannot %s \"%s\" because it has pending trigger events",
4397}
int rd_refcnt
Definition: rel.h:59
bool rd_isnailed
Definition: rel.h:62
bool AfterTriggerPendingOnRel(Oid relid)
Definition: trigger.c:6024

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

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

◆ ChooseForeignKeyConstraintNameAddition()

static char * ChooseForeignKeyConstraintNameAddition ( List colnames)
static

Definition at line 9660 of file tablecmds.c.

9661{
9662 char buf[NAMEDATALEN * 2];
9663 int buflen = 0;
9664 ListCell *lc;
9665
9666 buf[0] = '\0';
9667 foreach(lc, colnames)
9668 {
9669 const char *name = strVal(lfirst(lc));
9670
9671 if (buflen > 0)
9672 buf[buflen++] = '_'; /* insert _ between names */
9673
9674 /*
9675 * At this point we have buflen <= NAMEDATALEN. name should be less
9676 * than NAMEDATALEN already, but use strlcpy for paranoia.
9677 */
9678 strlcpy(buf + buflen, name, NAMEDATALEN);
9679 buflen += strlen(buf + buflen);
9680 if (buflen >= NAMEDATALEN)
9681 break;
9682 }
9683 return pstrdup(buf);
9684}
static char * buf
Definition: pg_test_fsync.c:72
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45

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

Referenced by addFkConstraint(), and ATExecAddConstraint().

◆ CloneFkReferenced()

static void CloneFkReferenced ( Relation  parentRel,
Relation  partitionRel 
)
static

Definition at line 11005 of file tablecmds.c.

11006{
11007 Relation pg_constraint;
11008 AttrMap *attmap;
11009 ListCell *cell;
11010 SysScanDesc scan;
11011 ScanKeyData key[2];
11012 HeapTuple tuple;
11013 List *clone = NIL;
11014 Relation trigrel;
11015
11016 /*
11017 * Search for any constraints where this partition's parent is in the
11018 * referenced side. However, we must not clone any constraint whose
11019 * parent constraint is also going to be cloned, to avoid duplicates. So
11020 * do it in two steps: first construct the list of constraints to clone,
11021 * then go over that list cloning those whose parents are not in the list.
11022 * (We must not rely on the parent being seen first, since the catalog
11023 * scan could return children first.)
11024 */
11025 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11026 ScanKeyInit(&key[0],
11027 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11028 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11029 ScanKeyInit(&key[1],
11030 Anum_pg_constraint_contype, BTEqualStrategyNumber,
11031 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11032 /* This is a seqscan, as we don't have a usable index ... */
11033 scan = systable_beginscan(pg_constraint, InvalidOid, true,
11034 NULL, 2, key);
11035 while ((tuple = systable_getnext(scan)) != NULL)
11036 {
11037 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11038
11039 clone = lappend_oid(clone, constrForm->oid);
11040 }
11041 systable_endscan(scan);
11042 table_close(pg_constraint, RowShareLock);
11043
11044 /*
11045 * Triggers of the foreign keys will be manipulated a bunch of times in
11046 * the loop below. To avoid repeatedly opening/closing the trigger
11047 * catalog relation, we open it here and pass it to the subroutines called
11048 * below.
11049 */
11050 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11051
11052 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11053 RelationGetDescr(parentRel),
11054 false);
11055 foreach(cell, clone)
11056 {
11057 Oid constrOid = lfirst_oid(cell);
11058 Form_pg_constraint constrForm;
11059 Relation fkRel;
11060 Oid indexOid;
11061 Oid partIndexId;
11062 int numfks;
11063 AttrNumber conkey[INDEX_MAX_KEYS];
11064 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11065 AttrNumber confkey[INDEX_MAX_KEYS];
11066 Oid conpfeqop[INDEX_MAX_KEYS];
11067 Oid conppeqop[INDEX_MAX_KEYS];
11068 Oid conffeqop[INDEX_MAX_KEYS];
11069 int numfkdelsetcols;
11070 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11071 Constraint *fkconstraint;
11072 ObjectAddress address;
11073 Oid deleteTriggerOid,
11074 updateTriggerOid;
11075
11076 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11077 if (!HeapTupleIsValid(tuple))
11078 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11079 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11080
11081 /*
11082 * As explained above: don't try to clone a constraint for which we're
11083 * going to clone the parent.
11084 */
11085 if (list_member_oid(clone, constrForm->conparentid))
11086 {
11087 ReleaseSysCache(tuple);
11088 continue;
11089 }
11090
11091 /*
11092 * Don't clone self-referencing foreign keys, which can be in the
11093 * partitioned table or in the partition-to-be.
11094 */
11095 if (constrForm->conrelid == RelationGetRelid(parentRel) ||
11096 constrForm->conrelid == RelationGetRelid(partitionRel))
11097 {
11098 ReleaseSysCache(tuple);
11099 continue;
11100 }
11101
11102 /* We need the same lock level that CreateTrigger will acquire */
11103 fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11104
11105 indexOid = constrForm->conindid;
11107 &numfks,
11108 conkey,
11109 confkey,
11110 conpfeqop,
11111 conppeqop,
11112 conffeqop,
11113 &numfkdelsetcols,
11114 confdelsetcols);
11115
11116 for (int i = 0; i < numfks; i++)
11117 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11118
11119 fkconstraint = makeNode(Constraint);
11120 fkconstraint->contype = CONSTRAINT_FOREIGN;
11121 fkconstraint->conname = NameStr(constrForm->conname);
11122 fkconstraint->deferrable = constrForm->condeferrable;
11123 fkconstraint->initdeferred = constrForm->condeferred;
11124 fkconstraint->location = -1;
11125 fkconstraint->pktable = NULL;
11126 /* ->fk_attrs determined below */
11127 fkconstraint->pk_attrs = NIL;
11128 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11129 fkconstraint->fk_upd_action = constrForm->confupdtype;
11130 fkconstraint->fk_del_action = constrForm->confdeltype;
11131 fkconstraint->fk_del_set_cols = NIL;
11132 fkconstraint->old_conpfeqop = NIL;
11133 fkconstraint->old_pktable_oid = InvalidOid;
11134 fkconstraint->skip_validation = false;
11135 fkconstraint->initially_valid = true;
11136
11137 /* set up colnames that are used to generate the constraint name */
11138 for (int i = 0; i < numfks; i++)
11139 {
11141
11142 att = TupleDescAttr(RelationGetDescr(fkRel),
11143 conkey[i] - 1);
11144 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11145 makeString(NameStr(att->attname)));
11146 }
11147
11148 /*
11149 * Add the new foreign key constraint pointing to the new partition.
11150 * Because this new partition appears in the referenced side of the
11151 * constraint, we don't need to set up for Phase 3 check.
11152 */
11153 partIndexId = index_get_partition(partitionRel, indexOid);
11154 if (!OidIsValid(partIndexId))
11155 elog(ERROR, "index for %u not found in partition %s",
11156 indexOid, RelationGetRelationName(partitionRel));
11157
11158 /*
11159 * Get the "action" triggers belonging to the constraint to pass as
11160 * parent OIDs for similar triggers that will be created on the
11161 * partition in addFkRecurseReferenced().
11162 */
11163 GetForeignKeyActionTriggers(trigrel, constrOid,
11164 constrForm->confrelid, constrForm->conrelid,
11165 &deleteTriggerOid, &updateTriggerOid);
11166
11167 /* Add this constraint ... */
11169 fkconstraint->conname, fkconstraint, fkRel,
11170 partitionRel, partIndexId, constrOid,
11171 numfks, mapped_confkey,
11172 conkey, conpfeqop, conppeqop, conffeqop,
11173 numfkdelsetcols, confdelsetcols, false,
11174 constrForm->conperiod);
11175 /* ... and recurse */
11176 addFkRecurseReferenced(fkconstraint,
11177 fkRel,
11178 partitionRel,
11179 partIndexId,
11180 address.objectId,
11181 numfks,
11182 mapped_confkey,
11183 conkey,
11184 conpfeqop,
11185 conppeqop,
11186 conffeqop,
11187 numfkdelsetcols,
11188 confdelsetcols,
11189 true,
11190 deleteTriggerOid,
11191 updateTriggerOid,
11192 constrForm->conperiod);
11193
11194 table_close(fkRel, NoLock);
11195 ReleaseSysCache(tuple);
11196 }
11197
11198 table_close(trigrel, RowExclusiveLock);
11199}
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:2859
static void GetForeignKeyActionTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:11789

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

11216{
11217 AttrMap *attmap;
11218 List *partFKs;
11219 List *clone = NIL;
11220 ListCell *cell;
11221 Relation trigrel;
11222
11223 /* obtain a list of constraints that we need to clone */
11224 foreach(cell, RelationGetFKeyList(parentRel))
11225 {
11226 ForeignKeyCacheInfo *fk = lfirst(cell);
11227
11228 /*
11229 * Refuse to attach a table as partition that this partitioned table
11230 * already has a foreign key to. This isn't useful schema, which is
11231 * proven by the fact that there have been no user complaints that
11232 * it's already impossible to achieve this in the opposite direction,
11233 * i.e., creating a foreign key that references a partition. This
11234 * restriction allows us to dodge some complexities around
11235 * pg_constraint and pg_trigger row creations that would be needed
11236 * during ATTACH/DETACH for this kind of relationship.
11237 */
11238 if (fk->confrelid == RelationGetRelid(partRel))
11239 ereport(ERROR,
11240 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11241 errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11242 RelationGetRelationName(partRel),
11244
11245 clone = lappend_oid(clone, fk->conoid);
11246 }
11247
11248 /*
11249 * Silently do nothing if there's nothing to do. In particular, this
11250 * avoids throwing a spurious error for foreign tables.
11251 */
11252 if (clone == NIL)
11253 return;
11254
11255 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11256 ereport(ERROR,
11257 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11258 errmsg("foreign key constraints are not supported on foreign tables")));
11259
11260 /*
11261 * Triggers of the foreign keys will be manipulated a bunch of times in
11262 * the loop below. To avoid repeatedly opening/closing the trigger
11263 * catalog relation, we open it here and pass it to the subroutines called
11264 * below.
11265 */
11266 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11267
11268 /*
11269 * The constraint key may differ, if the columns in the partition are
11270 * different. This map is used to convert them.
11271 */
11272 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11273 RelationGetDescr(parentRel),
11274 false);
11275
11276 partFKs = copyObject(RelationGetFKeyList(partRel));
11277
11278 foreach(cell, clone)
11279 {
11280 Oid parentConstrOid = lfirst_oid(cell);
11281 Form_pg_constraint constrForm;
11282 Relation pkrel;
11283 HeapTuple tuple;
11284 int numfks;
11285 AttrNumber conkey[INDEX_MAX_KEYS];
11286 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11287 AttrNumber confkey[INDEX_MAX_KEYS];
11288 Oid conpfeqop[INDEX_MAX_KEYS];
11289 Oid conppeqop[INDEX_MAX_KEYS];
11290 Oid conffeqop[INDEX_MAX_KEYS];
11291 int numfkdelsetcols;
11292 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11293 Constraint *fkconstraint;
11294 bool attached;
11295 Oid indexOid;
11296 ObjectAddress address;
11297 ListCell *lc;
11298 Oid insertTriggerOid,
11299 updateTriggerOid;
11300 bool with_period;
11301
11302 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11303 if (!HeapTupleIsValid(tuple))
11304 elog(ERROR, "cache lookup failed for constraint %u",
11305 parentConstrOid);
11306 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11307
11308 /* Don't clone constraints whose parents are being cloned */
11309 if (list_member_oid(clone, constrForm->conparentid))
11310 {
11311 ReleaseSysCache(tuple);
11312 continue;
11313 }
11314
11315 /*
11316 * Need to prevent concurrent deletions. If pkrel is a partitioned
11317 * relation, that means to lock all partitions.
11318 */
11319 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11320 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11322 ShareRowExclusiveLock, NULL);
11323
11324 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11325 conpfeqop, conppeqop, conffeqop,
11326 &numfkdelsetcols, confdelsetcols);
11327 for (int i = 0; i < numfks; i++)
11328 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11329
11330 /*
11331 * Get the "check" triggers belonging to the constraint to pass as
11332 * parent OIDs for similar triggers that will be created on the
11333 * partition in addFkRecurseReferencing(). They are also passed to
11334 * tryAttachPartitionForeignKey() below to simply assign as parents to
11335 * the partition's existing "check" triggers, that is, if the
11336 * corresponding constraints is deemed attachable to the parent
11337 * constraint.
11338 */
11339 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11340 constrForm->confrelid, constrForm->conrelid,
11341 &insertTriggerOid, &updateTriggerOid);
11342
11343 /*
11344 * Before creating a new constraint, see whether any existing FKs are
11345 * fit for the purpose. If one is, attach the parent constraint to
11346 * it, and don't clone anything. This way we avoid the expensive
11347 * verification step and don't end up with a duplicate FK, and we
11348 * don't need to recurse to partitions for this constraint.
11349 */
11350 attached = false;
11351 foreach(lc, partFKs)
11352 {
11354
11356 fk,
11357 partRel,
11358 parentConstrOid,
11359 numfks,
11360 mapped_conkey,
11361 confkey,
11362 conpfeqop,
11363 insertTriggerOid,
11364 updateTriggerOid,
11365 trigrel))
11366 {
11367 attached = true;
11368 table_close(pkrel, NoLock);
11369 break;
11370 }
11371 }
11372 if (attached)
11373 {
11374 ReleaseSysCache(tuple);
11375 continue;
11376 }
11377
11378 /* No dice. Set up to create our own constraint */
11379 fkconstraint = makeNode(Constraint);
11380 fkconstraint->contype = CONSTRAINT_FOREIGN;
11381 /* ->conname determined below */
11382 fkconstraint->deferrable = constrForm->condeferrable;
11383 fkconstraint->initdeferred = constrForm->condeferred;
11384 fkconstraint->location = -1;
11385 fkconstraint->pktable = NULL;
11386 /* ->fk_attrs determined below */
11387 fkconstraint->pk_attrs = NIL;
11388 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11389 fkconstraint->fk_upd_action = constrForm->confupdtype;
11390 fkconstraint->fk_del_action = constrForm->confdeltype;
11391 fkconstraint->fk_del_set_cols = NIL;
11392 fkconstraint->old_conpfeqop = NIL;
11393 fkconstraint->old_pktable_oid = InvalidOid;
11394 fkconstraint->skip_validation = false;
11395 fkconstraint->initially_valid = constrForm->convalidated;
11396 for (int i = 0; i < numfks; i++)
11397 {
11399
11400 att = TupleDescAttr(RelationGetDescr(partRel),
11401 mapped_conkey[i] - 1);
11402 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11403 makeString(NameStr(att->attname)));
11404 }
11405
11406 indexOid = constrForm->conindid;
11407 with_period = constrForm->conperiod;
11408
11409 /* Create the pg_constraint entry at this level */
11411 NameStr(constrForm->conname), fkconstraint,
11412 partRel, pkrel, indexOid, parentConstrOid,
11413 numfks, confkey,
11414 mapped_conkey, conpfeqop,
11415 conppeqop, conffeqop,
11416 numfkdelsetcols, confdelsetcols,
11417 false, with_period);
11418
11419 /* Done with the cloned constraint's tuple */
11420 ReleaseSysCache(tuple);
11421
11422 /* Create the check triggers, and recurse to partitions, if any */
11424 fkconstraint,
11425 partRel,
11426 pkrel,
11427 indexOid,
11428 address.objectId,
11429 numfks,
11430 confkey,
11431 mapped_conkey,
11432 conpfeqop,
11433 conppeqop,
11434 conffeqop,
11435 numfkdelsetcols,
11436 confdelsetcols,
11437 false, /* no old check exists */
11439 insertTriggerOid,
11440 updateTriggerOid,
11441 with_period);
11442 table_close(pkrel, NoLock);
11443 }
11444
11445 table_close(trigrel, RowExclusiveLock);
11446}

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, INDEX_MAX_KEYS, Constraint::initdeferred, Constraint::initially_valid, InvalidOid, 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 10974 of file tablecmds.c.

10976{
10977 /* This only works for declarative partitioning */
10978 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10979
10980 /*
10981 * Clone constraints for which the parent is on the referenced side.
10982 */
10983 CloneFkReferenced(parentRel, partitionRel);
10984
10985 /*
10986 * Now clone constraints where the parent is on the referencing side.
10987 */
10988 CloneFkReferencing(wqueue, parentRel, partitionRel);
10989}
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11215
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11005

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

20011{
20012 Relation pg_trigger;
20014 SysScanDesc scan;
20015 HeapTuple tuple;
20016 MemoryContext perTupCxt;
20017
20018 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20019 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20020 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20021 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20022 true, NULL, 1, &key);
20023
20025 "clone trig", ALLOCSET_SMALL_SIZES);
20026
20027 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20028 {
20029 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20030 CreateTrigStmt *trigStmt;
20031 Node *qual = NULL;
20032 Datum value;
20033 bool isnull;
20034 List *cols = NIL;
20035 List *trigargs = NIL;
20036 MemoryContext oldcxt;
20037
20038 /*
20039 * Ignore statement-level triggers; those are not cloned.
20040 */
20041 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20042 continue;
20043
20044 /*
20045 * Don't clone internal triggers, because the constraint cloning code
20046 * will.
20047 */
20048 if (trigForm->tgisinternal)
20049 continue;
20050
20051 /*
20052 * Complain if we find an unexpected trigger type.
20053 */
20054 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20055 !TRIGGER_FOR_AFTER(trigForm->tgtype))
20056 elog(ERROR, "unexpected trigger \"%s\" found",
20057 NameStr(trigForm->tgname));
20058
20059 /* Use short-lived context for CREATE TRIGGER */
20060 oldcxt = MemoryContextSwitchTo(perTupCxt);
20061
20062 /*
20063 * If there is a WHEN clause, generate a 'cooked' version of it that's
20064 * appropriate for the partition.
20065 */
20066 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20067 RelationGetDescr(pg_trigger), &isnull);
20068 if (!isnull)
20069 {
20071 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20072 partition, parent);
20073 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20074 partition, parent);
20075 }
20076
20077 /*
20078 * If there is a column list, transform it to a list of column names.
20079 * Note we don't need to map this list in any way ...
20080 */
20081 if (trigForm->tgattr.dim1 > 0)
20082 {
20083 int i;
20084
20085 for (i = 0; i < trigForm->tgattr.dim1; i++)
20086 {
20088
20089 col = TupleDescAttr(parent->rd_att,
20090 trigForm->tgattr.values[i] - 1);
20091 cols = lappend(cols,
20092 makeString(pstrdup(NameStr(col->attname))));
20093 }
20094 }
20095
20096 /* Reconstruct trigger arguments list. */
20097 if (trigForm->tgnargs > 0)
20098 {
20099 char *p;
20100
20101 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20102 RelationGetDescr(pg_trigger), &isnull);
20103 if (isnull)
20104 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20105 NameStr(trigForm->tgname), RelationGetRelationName(partition));
20106
20107 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20108
20109 for (int i = 0; i < trigForm->tgnargs; i++)
20110 {
20111 trigargs = lappend(trigargs, makeString(pstrdup(p)));
20112 p += strlen(p) + 1;
20113 }
20114 }
20115
20116 trigStmt = makeNode(CreateTrigStmt);
20117 trigStmt->replace = false;
20118 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20119 trigStmt->trigname = NameStr(trigForm->tgname);
20120 trigStmt->relation = NULL;
20121 trigStmt->funcname = NULL; /* passed separately */
20122 trigStmt->args = trigargs;
20123 trigStmt->row = true;
20124 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20125 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20126 trigStmt->columns = cols;
20127 trigStmt->whenClause = NULL; /* passed separately */
20128 trigStmt->transitionRels = NIL; /* not supported at present */
20129 trigStmt->deferrable = trigForm->tgdeferrable;
20130 trigStmt->initdeferred = trigForm->tginitdeferred;
20131 trigStmt->constrrel = NULL; /* passed separately */
20132
20133 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20134 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20135 trigForm->tgfoid, trigForm->oid, qual,
20136 false, true, trigForm->tgenabled);
20137
20138 MemoryContextSwitchTo(oldcxt);
20139 MemoryContextReset(perTupCxt);
20140 }
20141
20142 MemoryContextDelete(perTupCxt);
20143
20144 systable_endscan(scan);
20145 table_close(pg_trigger, RowExclusiveLock);
20146}
#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:3103
List * transitionRels
Definition: parsenodes.h:3105
RangeVar * constrrel
Definition: parsenodes.h:3109
RangeVar * relation
Definition: parsenodes.h:3094
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 19052 of file tablecmds.c.

19055{
19056 int attn;
19057 ListCell *lc;
19058 Oid am_oid;
19059
19060 attn = 0;
19061 foreach(lc, partParams)
19062 {
19064 Oid atttype;
19065 Oid attcollation;
19066
19067 if (pelem->name != NULL)
19068 {
19069 /* Simple attribute reference */
19070 HeapTuple atttuple;
19071 Form_pg_attribute attform;
19072
19074 pelem->name);
19075 if (!HeapTupleIsValid(atttuple))
19076 ereport(ERROR,
19077 (errcode(ERRCODE_UNDEFINED_COLUMN),
19078 errmsg("column \"%s\" named in partition key does not exist",
19079 pelem->name),
19080 parser_errposition(pstate, pelem->location)));
19081 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19082
19083 if (attform->attnum <= 0)
19084 ereport(ERROR,
19085 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19086 errmsg("cannot use system column \"%s\" in partition key",
19087 pelem->name),
19088 parser_errposition(pstate, pelem->location)));
19089
19090 /*
19091 * Stored generated columns cannot work: They are computed after
19092 * BEFORE triggers, but partition routing is done before all
19093 * triggers. Maybe virtual generated columns could be made to
19094 * work, but then they would need to be handled as an expression
19095 * below.
19096 */
19097 if (attform->attgenerated)
19098 ereport(ERROR,
19099 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19100 errmsg("cannot use generated column in partition key"),
19101 errdetail("Column \"%s\" is a generated column.",
19102 pelem->name),
19103 parser_errposition(pstate, pelem->location)));
19104
19105 partattrs[attn] = attform->attnum;
19106 atttype = attform->atttypid;
19107 attcollation = attform->attcollation;
19108 ReleaseSysCache(atttuple);
19109 }
19110 else
19111 {
19112 /* Expression */
19113 Node *expr = pelem->expr;
19114 char partattname[16];
19115
19116 Assert(expr != NULL);
19117 atttype = exprType(expr);
19118 attcollation = exprCollation(expr);
19119
19120 /*
19121 * The expression must be of a storable type (e.g., not RECORD).
19122 * The test is the same as for whether a table column is of a safe
19123 * type (which is why we needn't check for the non-expression
19124 * case).
19125 */
19126 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19127 CheckAttributeType(partattname,
19128 atttype, attcollation,
19130
19131 /*
19132 * Strip any top-level COLLATE clause. This ensures that we treat
19133 * "x COLLATE y" and "(x COLLATE y)" alike.
19134 */
19135 while (IsA(expr, CollateExpr))
19136 expr = (Node *) ((CollateExpr *) expr)->arg;
19137
19138 if (IsA(expr, Var) &&
19139 ((Var *) expr)->varattno > 0)
19140 {
19141 /*
19142 * User wrote "(column)" or "(column COLLATE something)".
19143 * Treat it like simple attribute anyway.
19144 */
19145 partattrs[attn] = ((Var *) expr)->varattno;
19146 }
19147 else
19148 {
19149 Bitmapset *expr_attrs = NULL;
19150 int i;
19151
19152 partattrs[attn] = 0; /* marks the column as expression */
19153 *partexprs = lappend(*partexprs, expr);
19154
19155 /*
19156 * transformPartitionSpec() should have already rejected
19157 * subqueries, aggregates, window functions, and SRFs, based
19158 * on the EXPR_KIND_ for partition expressions.
19159 */
19160
19161 /*
19162 * Cannot allow system column references, since that would
19163 * make partition routing impossible: their values won't be
19164 * known yet when we need to do that.
19165 */
19166 pull_varattnos(expr, 1, &expr_attrs);
19167 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
19168 {
19170 expr_attrs))
19171 ereport(ERROR,
19172 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19173 errmsg("partition key expressions cannot contain system column references")));
19174 }
19175
19176 /*
19177 * Stored generated columns cannot work: They are computed
19178 * after BEFORE triggers, but partition routing is done before
19179 * all triggers. Virtual generated columns could probably
19180 * work, but it would require more work elsewhere (for example
19181 * SET EXPRESSION would need to check whether the column is
19182 * used in partition keys). Seems safer to prohibit for now.
19183 */
19184 i = -1;
19185 while ((i = bms_next_member(expr_attrs, i)) >= 0)
19186 {
19188
19189 if (attno > 0 &&
19190 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19191 ereport(ERROR,
19192 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19193 errmsg("cannot use generated column in partition key"),
19194 errdetail("Column \"%s\" is a generated column.",
19195 get_attname(RelationGetRelid(rel), attno, false)),
19196 parser_errposition(pstate, pelem->location)));
19197 }
19198
19199 /*
19200 * Preprocess the expression before checking for mutability.
19201 * This is essential for the reasons described in
19202 * contain_mutable_functions_after_planning. However, we call
19203 * expression_planner for ourselves rather than using that
19204 * function, because if constant-folding reduces the
19205 * expression to a constant, we'd like to know that so we can
19206 * complain below.
19207 *
19208 * Like contain_mutable_functions_after_planning, assume that
19209 * expression_planner won't scribble on its input, so this
19210 * won't affect the partexprs entry we saved above.
19211 */
19212 expr = (Node *) expression_planner((Expr *) expr);
19213
19214 /*
19215 * Partition expressions cannot contain mutable functions,
19216 * because a given row must always map to the same partition
19217 * as long as there is no change in the partition boundary
19218 * structure.
19219 */
19220 if (contain_mutable_functions(expr))
19221 ereport(ERROR,
19222 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19223 errmsg("functions in partition key expression must be marked IMMUTABLE")));
19224
19225 /*
19226 * While it is not exactly *wrong* for a partition expression
19227 * to be a constant, it seems better to reject such keys.
19228 */
19229 if (IsA(expr, Const))
19230 ereport(ERROR,
19231 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19232 errmsg("cannot use constant expression as partition key")));
19233 }
19234 }
19235
19236 /*
19237 * Apply collation override if any
19238 */
19239 if (pelem->collation)
19240 attcollation = get_collation_oid(pelem->collation, false);
19241
19242 /*
19243 * Check we have a collation iff it's a collatable type. The only
19244 * expected failures here are (1) COLLATE applied to a noncollatable
19245 * type, or (2) partition expression had an unresolved collation. But
19246 * we might as well code this to be a complete consistency check.
19247 */
19248 if (type_is_collatable(atttype))
19249 {
19250 if (!OidIsValid(attcollation))
19251 ereport(ERROR,
19252 (errcode(ERRCODE_INDETERMINATE_COLLATION),
19253 errmsg("could not determine which collation to use for partition expression"),
19254 errhint("Use the COLLATE clause to set the collation explicitly.")));
19255 }
19256 else
19257 {
19258 if (OidIsValid(attcollation))
19259 ereport(ERROR,
19260 (errcode(ERRCODE_DATATYPE_MISMATCH),
19261 errmsg("collations are not supported by type %s",
19262 format_type_be(atttype))));
19263 }
19264
19265 partcollation[attn] = attcollation;
19266
19267 /*
19268 * Identify the appropriate operator class. For list and range
19269 * partitioning, we use a btree operator class; hash partitioning uses
19270 * a hash operator class.
19271 */
19272 if (strategy == PARTITION_STRATEGY_HASH)
19273 am_oid = HASH_AM_OID;
19274 else
19275 am_oid = BTREE_AM_OID;
19276
19277 if (!pelem->opclass)
19278 {
19279 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
19280
19281 if (!OidIsValid(partopclass[attn]))
19282 {
19283 if (strategy == PARTITION_STRATEGY_HASH)
19284 ereport(ERROR,
19285 (errcode(ERRCODE_UNDEFINED_OBJECT),
19286 errmsg("data type %s has no default operator class for access method \"%s\"",
19287 format_type_be(atttype), "hash"),
19288 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
19289 else
19290 ereport(ERROR,
19291 (errcode(ERRCODE_UNDEFINED_OBJECT),
19292 errmsg("data type %s has no default operator class for access method \"%s\"",
19293 format_type_be(atttype), "btree"),
19294 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19295 }
19296 }
19297 else
19298 partopclass[attn] = ResolveOpClass(pelem->opclass,
19299 atttype,
19300 am_oid == HASH_AM_OID ? "hash" : "btree",
19301 am_oid);
19302
19303 attn++;
19304 }
19305}
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:369
#define CHKATYPE_IS_PARTKEY
Definition: heap.h:25
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2343
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition: indexcmds.c:2258
bool type_is_collatable(Oid typid)
Definition: lsyscache.c:3164
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 19369 of file tablecmds.c.

19370{
19371 List *existConstraint = list_copy(provenConstraint);
19372 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19373 int num_check,
19374 i;
19375
19376 num_check = (constr != NULL) ? constr->num_check : 0;
19377 for (i = 0; i < num_check; i++)
19378 {
19379 Node *cexpr;
19380
19381 /*
19382 * If this constraint hasn't been fully validated yet, we must ignore
19383 * it here.
19384 */
19385 if (!constr->check[i].ccvalid)
19386 continue;
19387
19388 /*
19389 * NOT ENFORCED constraints are always marked as invalid, which should
19390 * have been ignored.
19391 */
19392 Assert(constr->check[i].ccenforced);
19393
19394 cexpr = stringToNode(constr->check[i].ccbin);
19395
19396 /*
19397 * Run each expression through const-simplification and
19398 * canonicalization. It is necessary, because we will be comparing it
19399 * to similarly-processed partition constraint expressions, and may
19400 * fail to detect valid matches without this.
19401 */
19402 cexpr = eval_const_expressions(NULL, cexpr);
19403 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
19404
19405 existConstraint = list_concat(existConstraint,
19406 make_ands_implicit((Expr *) cexpr));
19407 }
19408
19409 /*
19410 * Try to make the proof. Since we are comparing CHECK constraints, we
19411 * need to use weak implication, i.e., we assume existConstraint is
19412 * not-false and try to prove the same for testConstraint.
19413 *
19414 * Note that predicate_implied_by assumes its first argument is known
19415 * immutable. That should always be true for both NOT NULL and partition
19416 * constraints, so we don't test it here.
19417 */
19418 return predicate_implied_by(testConstraint, existConstraint, true);
19419}
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 16738 of file tablecmds.c.

16739{
16742
16743 if (acon->condeferrable != bcon->condeferrable ||
16744 acon->condeferred != bcon->condeferred ||
16745 strcmp(decompile_conbin(a, tupleDesc),
16746 decompile_conbin(b, tupleDesc)) != 0)
16747 return false;
16748 else
16749 return true;
16750}
int b
Definition: isn.c:71
int a
Definition: isn.c:70
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:16710

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

13088{
13089 ObjectAddress trigAddress;
13090 CreateTrigStmt *fk_trigger;
13091
13092 /*
13093 * Note: for a self-referential FK (referencing and referenced tables are
13094 * the same), it is important that the ON UPDATE action fires before the
13095 * CHECK action, since both triggers will fire on the same row during an
13096 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13097 * state of the row. Triggers fire in name order, so we ensure this by
13098 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13099 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13100 */
13101 fk_trigger = makeNode(CreateTrigStmt);
13102 fk_trigger->replace = false;
13103 fk_trigger->isconstraint = true;
13104 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13105 fk_trigger->relation = NULL;
13106
13107 /* Either ON INSERT or ON UPDATE */
13108 if (on_insert)
13109 {
13110 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13111 fk_trigger->events = TRIGGER_TYPE_INSERT;
13112 }
13113 else
13114 {
13115 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13116 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13117 }
13118
13119 fk_trigger->args = NIL;
13120 fk_trigger->row = true;
13121 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13122 fk_trigger->columns = NIL;
13123 fk_trigger->whenClause = NULL;
13124 fk_trigger->transitionRels = NIL;
13125 fk_trigger->deferrable = fkconstraint->deferrable;
13126 fk_trigger->initdeferred = fkconstraint->initdeferred;
13127 fk_trigger->constrrel = NULL;
13128
13129 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13130 constraintOid, indexOid, InvalidOid,
13131 parentTrigOid, NULL, true, false);
13132
13133 /* Make changes-so-far visible */
13135
13136 return trigAddress.objectId;
13137}
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 ( Relation  rel,
Oid  refRelOid,
Constraint fkconstraint,
Oid  constraintOid,
Oid  indexOid,
Oid  parentDelTrigger,
Oid  parentUpdTrigger,
Oid deleteTrigOid,
Oid updateTrigOid 
)
static

Definition at line 13148 of file tablecmds.c.

13152{
13153 CreateTrigStmt *fk_trigger;
13154 ObjectAddress trigAddress;
13155
13156 /*
13157 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13158 * DELETE action on the referenced table.
13159 */
13160 fk_trigger = makeNode(CreateTrigStmt);
13161 fk_trigger->replace = false;
13162 fk_trigger->isconstraint = true;
13163 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13164 fk_trigger->relation = NULL;
13165 fk_trigger->args = NIL;
13166 fk_trigger->row = true;
13167 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13168 fk_trigger->events = TRIGGER_TYPE_DELETE;
13169 fk_trigger->columns = NIL;
13170 fk_trigger->whenClause = NULL;
13171 fk_trigger->transitionRels = NIL;
13172 fk_trigger->constrrel = NULL;
13173
13174 switch (fkconstraint->fk_del_action)
13175 {
13177 fk_trigger->deferrable = fkconstraint->deferrable;
13178 fk_trigger->initdeferred = fkconstraint->initdeferred;
13179 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13180 break;
13182 fk_trigger->deferrable = false;
13183 fk_trigger->initdeferred = false;
13184 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13185 break;
13187 fk_trigger->deferrable = false;
13188 fk_trigger->initdeferred = false;
13189 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13190 break;
13192 fk_trigger->deferrable = false;
13193 fk_trigger->initdeferred = false;
13194 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13195 break;
13197 fk_trigger->deferrable = false;
13198 fk_trigger->initdeferred = false;
13199 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13200 break;
13201 default:
13202 elog(ERROR, "unrecognized FK action type: %d",
13203 (int) fkconstraint->fk_del_action);
13204 break;
13205 }
13206
13207 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
13208 RelationGetRelid(rel),
13209 constraintOid, indexOid, InvalidOid,
13210 parentDelTrigger, NULL, true, false);
13211 if (deleteTrigOid)
13212 *deleteTrigOid = trigAddress.objectId;
13213
13214 /* Make changes-so-far visible */
13216
13217 /*
13218 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13219 * UPDATE action on the referenced table.
13220 */
13221 fk_trigger = makeNode(CreateTrigStmt);
13222 fk_trigger->replace = false;
13223 fk_trigger->isconstraint = true;
13224 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13225 fk_trigger->relation = NULL;
13226 fk_trigger->args = NIL;
13227 fk_trigger->row = true;
13228 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13229 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13230 fk_trigger->columns = NIL;
13231 fk_trigger->whenClause = NULL;
13232 fk_trigger->transitionRels = NIL;
13233 fk_trigger->constrrel = NULL;
13234
13235 switch (fkconstraint->fk_upd_action)
13236 {
13238 fk_trigger->deferrable = fkconstraint->deferrable;
13239 fk_trigger->initdeferred = fkconstraint->initdeferred;
13240 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13241 break;
13243 fk_trigger->deferrable = false;
13244 fk_trigger->initdeferred = false;
13245 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13246 break;
13248 fk_trigger->deferrable = false;
13249 fk_trigger->initdeferred = false;
13250 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13251 break;
13253 fk_trigger->deferrable = false;
13254 fk_trigger->initdeferred = false;
13255 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13256 break;
13258 fk_trigger->deferrable = false;
13259 fk_trigger->initdeferred = false;
13260 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13261 break;
13262 default:
13263 elog(ERROR, "unrecognized FK action type: %d",
13264 (int) fkconstraint->fk_upd_action);
13265 break;
13266 }
13267
13268 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
13269 RelationGetRelid(rel),
13270 constraintOid, indexOid, InvalidOid,
13271 parentUpdTrigger, NULL, true, false);
13272 if (updateTrigOid)
13273 *updateTrigOid = trigAddress.objectId;
13274}
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2801

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, RelationGetRelid, CreateTrigStmt::replace, CreateTrigStmt::row, SystemFuncName(), CreateTrigStmt::timing, CreateTrigStmt::transitionRels, CreateTrigStmt::trigname, and CreateTrigStmt::whenClause.

Referenced by addFkRecurseReferenced().

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

13290{
13291 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13292 constraintOid, indexOid,
13293 parentInsTrigger, true);
13294 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13295 constraintOid, indexOid,
13296 parentUpdTrigger, false);
13297}
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:13085

References CreateFKCheckTrigger().

Referenced by addFkRecurseReferencing().

◆ CreateInheritance()

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

Definition at line 16641 of file tablecmds.c.

16642{
16643 Relation catalogRelation;
16644 SysScanDesc scan;
16646 HeapTuple inheritsTuple;
16647 int32 inhseqno;
16648
16649 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16650 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16651
16652 /*
16653 * Check for duplicates in the list of parents, and determine the highest
16654 * inhseqno already present; we'll use the next one for the new parent.
16655 * Also, if proposed child is a partition, it cannot already be
16656 * inheriting.
16657 *
16658 * Note: we do not reject the case where the child already inherits from
16659 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16660 */
16662 Anum_pg_inherits_inhrelid,
16663 BTEqualStrategyNumber, F_OIDEQ,
16665 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16666 true, NULL, 1, &key);
16667
16668 /* inhseqno sequences start at 1 */
16669 inhseqno = 0;
16670 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16671 {
16672 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16673
16674 if (inh->inhparent == RelationGetRelid(parent_rel))
16675 ereport(ERROR,
16676 (errcode(ERRCODE_DUPLICATE_TABLE),
16677 errmsg("relation \"%s\" would be inherited from more than once",
16678 RelationGetRelationName(parent_rel))));
16679
16680 if (inh->inhseqno > inhseqno)
16681 inhseqno = inh->inhseqno;
16682 }
16683 systable_endscan(scan);
16684
16685 /* Match up the columns and bump attinhcount as needed */
16686 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16687
16688 /* Match up the constraints and bump coninhcount as needed */
16689 MergeConstraintsIntoExisting(child_rel, parent_rel);
16690
16691 /*
16692 * OK, it looks valid. Make the catalog entries that show inheritance.
16693 */
16695 RelationGetRelid(parent_rel),
16696 inhseqno + 1,
16697 catalogRelation,
16698 parent_rel->rd_rel->relkind ==
16699 RELKIND_PARTITIONED_TABLE);
16700
16701 /* Now we're done with pg_inherits */
16702 table_close(catalogRelation, RowExclusiveLock);
16703}
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:16767
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3526
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:16905

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

16711{
16713 bool isnull;
16714 Datum attr;
16715 Datum expr;
16716
16717 con = (Form_pg_constraint) GETSTRUCT(contup);
16718 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16719 if (isnull)
16720 elog(ERROR, "null conbin for constraint %u", con->oid);
16721
16722 expr = DirectFunctionCall2(pg_get_expr, attr,
16723 ObjectIdGetDatum(con->conrelid));
16724 return TextDatumGetCString(expr);
16725}
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:643
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 735 of file tablecmds.c.

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

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

20701{
20702 List *constraintExpr;
20703
20704 constraintExpr = RelationGetPartitionQual(partRel);
20705 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20706
20707 /*
20708 * Avoid adding a new constraint if the needed constraint is implied by an
20709 * existing constraint
20710 */
20711 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20712 {
20713 AlteredTableInfo *tab;
20714 Constraint *n;
20715
20716 tab = ATGetQueueEntry(wqueue, partRel);
20717
20718 /* Add constraint on partition, equivalent to the partition constraint */
20719 n = makeNode(Constraint);
20720 n->contype = CONSTR_CHECK;
20721 n->conname = NULL;
20722 n->location = -1;
20723 n->is_no_inherit = false;
20724 n->raw_expr = NULL;
20725 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20726 n->is_enforced = true;
20727 n->initially_valid = true;
20728 n->skip_validation = true;
20729 /* It's a re-add, since it nominally already exists */
20730 ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20731 true, false, true, ShareUpdateExclusiveLock);
20732 }
20733}
char * nodeToString(const void *obj)
Definition: outfuncs.c:794
bool is_enforced
Definition: parsenodes.h:2819
char * cooked_expr
Definition: parsenodes.h:2825
Node * raw_expr
Definition: parsenodes.h:2823
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:19316

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

20340{
20341 Relation classRel;
20342 List *fks;
20343 ListCell *cell;
20344 List *indexes;
20345 Datum new_val[Natts_pg_class];
20346 bool new_null[Natts_pg_class],
20347 new_repl[Natts_pg_class];
20348 HeapTuple tuple,
20349 newtuple;
20350 Relation trigrel = NULL;
20351 List *fkoids = NIL;
20352
20353 if (concurrent)
20354 {
20355 /*
20356 * We can remove the pg_inherits row now. (In the non-concurrent case,
20357 * this was already done).
20358 */
20359 RemoveInheritance(partRel, rel, true);
20360 }
20361
20362 /* Drop any triggers that were cloned on creation/attach. */
20364
20365 /*
20366 * Detach any foreign keys that are inherited. This includes creating
20367 * additional action triggers.
20368 */
20369 fks = copyObject(RelationGetFKeyList(partRel));
20370 if (fks != NIL)
20371 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
20372
20373 /*
20374 * It's possible that the partition being detached has a foreign key that
20375 * references a partitioned table. When that happens, there are multiple
20376 * pg_constraint rows for the partition: one points to the partitioned
20377 * table itself, while the others point to each of its partitions. Only
20378 * the topmost one is to be considered here; the child constraints must be
20379 * left alone, because conceptually those aren't coming from our parent
20380 * partitioned table, but from this partition itself.
20381 *
20382 * We implement this by collecting all the constraint OIDs in a first scan
20383 * of the FK array, and skipping in the loop below those constraints whose
20384 * parents are listed here.
20385 */
20387 fkoids = lappend_oid(fkoids, fk->conoid);
20388
20389 foreach(cell, fks)
20390 {
20391 ForeignKeyCacheInfo *fk = lfirst(cell);
20392 HeapTuple contup;
20393 Form_pg_constraint conform;
20394 Oid insertTriggerOid,
20395 updateTriggerOid;
20396
20397 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
20398 if (!HeapTupleIsValid(contup))
20399 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
20400 conform = (Form_pg_constraint) GETSTRUCT(contup);
20401
20402 /*
20403 * Consider only inherited foreign keys, and only if their parents
20404 * aren't in the list.
20405 */
20406 if (conform->contype != CONSTRAINT_FOREIGN ||
20407 !OidIsValid(conform->conparentid) ||
20408 list_member_oid(fkoids, conform->conparentid))
20409 {
20410 ReleaseSysCache(contup);
20411 continue;
20412 }
20413
20414 /*
20415 * The constraint on this table must be marked no longer a child of
20416 * the parent's constraint, as do its check triggers.
20417 */
20419
20420 /*
20421 * Also, look up the partition's "check" triggers corresponding to the
20422 * constraint being detached and detach them from the parent triggers.
20423 */
20425 fk->conoid, fk->confrelid, fk->conrelid,
20426 &insertTriggerOid, &updateTriggerOid);
20427 Assert(OidIsValid(insertTriggerOid));
20428 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
20429 RelationGetRelid(partRel));
20430 Assert(OidIsValid(updateTriggerOid));
20431 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
20432 RelationGetRelid(partRel));
20433
20434 /*
20435 * Lastly, create the action triggers on the referenced table, using
20436 * addFkRecurseReferenced, which requires some elaborate setup (so put
20437 * it in a separate block). While at it, if the table is partitioned,
20438 * that function will recurse to create the pg_constraint rows and
20439 * action triggers for each partition.
20440 *
20441 * Note there's no need to do addFkConstraint() here, because the
20442 * pg_constraint row already exists.
20443 */
20444 {
20445 Constraint *fkconstraint;
20446 int numfks;
20447 AttrNumber conkey[INDEX_MAX_KEYS];
20448 AttrNumber confkey[INDEX_MAX_KEYS];
20449 Oid conpfeqop[INDEX_MAX_KEYS];
20450 Oid conppeqop[INDEX_MAX_KEYS];
20451 Oid conffeqop[INDEX_MAX_KEYS];
20452 int numfkdelsetcols;
20453 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
20454 Relation refdRel;
20455
20457 &numfks,
20458 conkey,
20459 confkey,
20460 conpfeqop,
20461 conppeqop,
20462 conffeqop,
20463 &numfkdelsetcols,
20464 confdelsetcols);
20465
20466 /* Create a synthetic node we'll use throughout */
20467 fkconstraint = makeNode(Constraint);
20468 fkconstraint->contype = CONSTRAINT_FOREIGN;
20469 fkconstraint->conname = pstrdup(NameStr(conform->conname));
20470 fkconstraint->deferrable = conform->condeferrable;
20471 fkconstraint->initdeferred = conform->condeferred;
20472 fkconstraint->skip_validation = true;
20473 fkconstraint->initially_valid = true;
20474 /* a few irrelevant fields omitted here */
20475 fkconstraint->pktable = NULL;
20476 fkconstraint->fk_attrs = NIL;
20477 fkconstraint->pk_attrs = NIL;
20478 fkconstraint->fk_matchtype = conform->confmatchtype;
20479 fkconstraint->fk_upd_action = conform->confupdtype;
20480 fkconstraint->fk_del_action = conform->confdeltype;
20481 fkconstraint->fk_del_set_cols = NIL;
20482 fkconstraint->old_conpfeqop = NIL;
20483 fkconstraint->old_pktable_oid = InvalidOid;
20484 fkconstraint->location = -1;
20485
20486 /* set up colnames, used to generate the constraint name */
20487 for (int i = 0; i < numfks; i++)
20488 {
20490
20491 att = TupleDescAttr(RelationGetDescr(partRel),
20492 conkey[i] - 1);
20493
20494 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
20495 makeString(NameStr(att->attname)));
20496 }
20497
20499
20500 addFkRecurseReferenced(fkconstraint, partRel,
20501 refdRel,
20502 conform->conindid,
20503 fk->conoid,
20504 numfks,
20505 confkey,
20506 conkey,
20507 conpfeqop,
20508 conppeqop,
20509 conffeqop,
20510 numfkdelsetcols,
20511 confdelsetcols,
20512 true,
20514 conform->conperiod);
20515 table_close(refdRel, NoLock); /* keep lock till end of xact */
20516 }
20517
20518 ReleaseSysCache(contup);
20519 }
20520 list_free_deep(fks);
20521 if (trigrel)
20522 table_close(trigrel, RowExclusiveLock);
20523
20524 /*
20525 * Any sub-constraints that are in the referenced-side of a larger
20526 * constraint have to be removed. This partition is no longer part of the
20527 * key space of the constraint.
20528 */
20529 foreach(cell, GetParentedForeignKeyRefs(partRel))
20530 {
20531 Oid constrOid = lfirst_oid(cell);
20532 ObjectAddress constraint;
20533
20535 deleteDependencyRecordsForClass(ConstraintRelationId,
20536 constrOid,
20537 ConstraintRelationId,
20540
20541 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
20542 performDeletion(&constraint, DROP_RESTRICT, 0);
20543 }
20544
20545 /* Now we can detach indexes */
20546 indexes = RelationGetIndexList(partRel);
20547 foreach(cell, indexes)
20548 {
20549 Oid idxid = lfirst_oid(cell);
20550 Oid parentidx;
20551 Relation idx;
20552 Oid constrOid;
20553 Oid parentConstrOid;
20554
20555 if (!has_superclass(idxid))
20556 continue;
20557
20558 parentidx = get_partition_parent(idxid, false);
20559 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
20560
20563
20564 /*
20565 * If there's a constraint associated with the index, detach it too.
20566 * Careful: it is possible for a constraint index in a partition to be
20567 * the child of a non-constraint index, so verify whether the parent
20568 * index does actually have a constraint.
20569 */
20571 idxid);
20573 parentidx);
20574 if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
20576
20578 }
20579
20580 /* Update pg_class tuple */
20581 classRel = table_open(RelationRelationId, RowExclusiveLock);
20582 tuple = SearchSysCacheCopy1(RELOID,
20584 if (!HeapTupleIsValid(tuple))
20585 elog(ERROR, "cache lookup failed for relation %u",
20586 RelationGetRelid(partRel));
20587 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
20588
20589 /* Clear relpartbound and reset relispartition */
20590 memset(new_val, 0, sizeof(new_val));
20591 memset(new_null, false, sizeof(new_null));
20592 memset(new_repl, false, sizeof(new_repl));
20593 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
20594 new_null[Anum_pg_class_relpartbound - 1] = true;
20595 new_repl[Anum_pg_class_relpartbound - 1] = true;
20596 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
20597 new_val, new_null, new_repl);
20598
20599 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
20600 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
20601 heap_freetuple(newtuple);
20602 table_close(classRel, RowExclusiveLock);
20603
20604 /*
20605 * Drop identity property from all identity columns of partition.
20606 */
20607 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
20608 {
20609 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
20610
20611 if (!attr->attisdropped && attr->attidentity)
20612 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
20613 AccessExclusiveLock, true, true);
20614 }
20615
20616 if (OidIsValid(defaultPartOid))
20617 {
20618 /*
20619 * If the relation being detached is the default partition itself,
20620 * remove it from the parent's pg_partitioned_table entry.
20621 *
20622 * If not, we must invalidate default partition's relcache entry, as
20623 * in StorePartitionBound: its partition constraint depends on every
20624 * other partition's partition constraint.
20625 */
20626 if (RelationGetRelid(partRel) == defaultPartOid)
20628 else
20629 CacheInvalidateRelcacheByRelid(defaultPartOid);
20630 }
20631
20632 /*
20633 * Invalidate the parent's relcache so that the partition is no longer
20634 * included in its partition descriptor.
20635 */
20637
20638 /*
20639 * If the partition we just detached is partitioned itself, invalidate
20640 * relcache for all descendent partitions too to ensure that their
20641 * rd_partcheck expression trees are rebuilt; must lock partitions before
20642 * doing so, using the same lockmode as what partRel has been locked with
20643 * by the caller.
20644 */
20645 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20646 {
20647 List *children;
20648
20649 children = find_all_inheritors(RelationGetRelid(partRel),
20650 AccessExclusiveLock, NULL);
20651 foreach(cell, children)
20652 {
20654 }
20655 }
20656}
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:519
static void DropClonedTriggersFromPartition(Oid partitionId)
Definition: tablecmds.c:20742

References AccessExclusiveLock, addFkRecurseReferenced(), Assert(), ATExecDropIdentity(), CacheInvalidateRelcache(), CacheInvalidateRelcacheByRelid(), CatalogTupleUpdate(), CommandCounterIncrement(), 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, 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 17431 of file tablecmds.c.

17433{
17434 Relation catalogRelation;
17435 SysScanDesc scan;
17436 ScanKeyData key[3];
17437 HeapTuple depTuple;
17438
17439 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
17440
17441 ScanKeyInit(&key[0],
17442 Anum_pg_depend_classid,
17443 BTEqualStrategyNumber, F_OIDEQ,
17444 ObjectIdGetDatum(RelationRelationId));
17445 ScanKeyInit(&key[1],
17446 Anum_pg_depend_objid,
17447 BTEqualStrategyNumber, F_OIDEQ,
17448 ObjectIdGetDatum(relid));
17449 ScanKeyInit(&key[2],
17450 Anum_pg_depend_objsubid,
17451 BTEqualStrategyNumber, F_INT4EQ,
17452 Int32GetDatum(0));
17453
17454 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
17455 NULL, 3, key);
17456
17457 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
17458 {
17459 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
17460
17461 if (dep->refclassid == refclassid &&
17462 dep->refobjid == refobjid &&
17463 dep->refobjsubid == 0 &&
17464 dep->deptype == deptype)
17465 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
17466 }
17467
17468 systable_endscan(scan);
17469 table_close(catalogRelation, RowExclusiveLock);
17470}

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

20743{
20744 ScanKeyData skey;
20745 SysScanDesc scan;
20746 HeapTuple trigtup;
20747 Relation tgrel;
20748 ObjectAddresses *objects;
20749
20750 objects = new_object_addresses();
20751
20752 /*
20753 * Scan pg_trigger to search for all triggers on this rel.
20754 */
20755 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20756 F_OIDEQ, ObjectIdGetDatum(partitionId));
20757 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20758 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20759 true, NULL, 1, &skey);
20760 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20761 {
20762 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20763 ObjectAddress trig;
20764
20765 /* Ignore triggers that weren't cloned */
20766 if (!OidIsValid(pg_trigger->tgparentid))
20767 continue;
20768
20769 /*
20770 * Ignore internal triggers that are implementation objects of foreign
20771 * keys, because these will be detached when the foreign keys
20772 * themselves are.
20773 */
20774 if (OidIsValid(pg_trigger->tgconstrrelid))
20775 continue;
20776
20777 /*
20778 * This is ugly, but necessary: remove the dependency markings on the
20779 * trigger so that it can be removed.
20780 */
20781 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20782 TriggerRelationId,
20784 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20785 RelationRelationId,
20787
20788 /* remember this trigger to remove it below */
20789 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20790 add_exact_object_address(&trig, objects);
20791 }
20792
20793 /* make the dependency removal visible to the deletion below */
20796
20797 /* done */
20798 free_object_addresses(objects);
20799 systable_endscan(scan);
20801}

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

13373{
13374 Relation conrel;
13376 ObjectAddress conobj;
13377 List *children;
13378 bool is_no_inherit_constraint = false;
13379 char *constrName;
13380 char *colname = NULL;
13381
13382 /* Guard against stack overflow due to overly deep inheritance tree. */
13384
13385 /* At top level, permission check was done in ATPrepCmd, else do it */
13386 if (recursing)
13389
13390 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13391
13392 con = (Form_pg_constraint) GETSTRUCT(constraintTup);
13393 constrName = NameStr(con->conname);
13394
13395 /* Don't allow drop of inherited constraints */
13396 if (con->coninhcount > 0 && !recursing)
13397 ereport(ERROR,
13398 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13399 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
13400 constrName, RelationGetRelationName(rel))));
13401
13402 /*
13403 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
13404 *
13405 * While doing that, we're in a good position to disallow dropping a not-
13406 * null constraint underneath a primary key, a replica identity index, or
13407 * a generated identity column.
13408 */
13409 if (con->contype == CONSTRAINT_NOTNULL)
13410 {
13411 Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
13412 AttrNumber attnum = extractNotNullColumn(constraintTup);
13413 Bitmapset *pkattrs;
13414 Bitmapset *irattrs;
13415 HeapTuple atttup;
13416 Form_pg_attribute attForm;
13417
13418 /* save column name for recursion step */
13419 colname = get_attname(RelationGetRelid(rel), attnum, false);
13420
13421 /*
13422 * Disallow if it's in the primary key. For partitioned tables we
13423 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
13424 * return NULL if the primary key is invalid; but we still need to
13425 * protect not-null constraints under such a constraint, so check the
13426 * slow way.
13427 */
13429
13430 if (pkattrs == NULL &&
13431 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13432 {
13433 Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
13434
13435 if (OidIsValid(pkindex))
13436 {
13437 Relation pk = relation_open(pkindex, AccessShareLock);
13438
13439 pkattrs = NULL;
13440 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
13441 pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
13442
13444 }
13445 }
13446
13447 if (pkattrs &&
13449 ereport(ERROR,
13450 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13451 errmsg("column \"%s\" is in a primary key",
13452 get_attname(RelationGetRelid(rel), attnum, false)));
13453
13454 /* Disallow if it's in the replica identity */
13457 ereport(ERROR,
13458 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13459 errmsg("column \"%s\" is in index used as replica identity",
13460 get_attname(RelationGetRelid(rel), attnum, false)));
13461
13462 /* Disallow if it's a GENERATED AS IDENTITY column */
13464 if (!HeapTupleIsValid(atttup))
13465 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
13466 attnum, RelationGetRelid(rel));
13467 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
13468 if (attForm->attidentity != '\0')
13469 ereport(ERROR,
13470 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13471 errmsg("column \"%s\" of relation \"%s\" is an identity column",
13473 false),
13475
13476 /* All good -- reset attnotnull if needed */
13477 if (attForm->attnotnull)
13478 {
13479 attForm->attnotnull = false;
13480 CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
13481 }
13482
13484 }
13485
13486 is_no_inherit_constraint = con->connoinherit;
13487
13488 /*
13489 * If it's a foreign-key constraint, we'd better lock the referenced table
13490 * and check that that's not in use, just as we've already done for the
13491 * constrained table (else we might, eg, be dropping a trigger that has
13492 * unfired events). But we can/must skip that in the self-referential
13493 * case.
13494 */
13495 if (con->contype == CONSTRAINT_FOREIGN &&
13496 con->confrelid != RelationGetRelid(rel))
13497 {
13498 Relation frel;
13499
13500 /* Must match lock taken by RemoveTriggerById: */
13501 frel = table_open(con->confrelid, AccessExclusiveLock);
13503 table_close(frel, NoLock);
13504 }
13505
13506 /*
13507 * Perform the actual constraint deletion
13508 */
13509 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13510 performDeletion(&conobj, behavior, 0);
13511
13512 /*
13513 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13514 * are dropped via the dependency mechanism, so we're done here.
13515 */
13516 if (con->contype != CONSTRAINT_CHECK &&
13517 con->contype != CONSTRAINT_NOTNULL &&
13518 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13519 {
13521 return conobj;
13522 }
13523
13524 /*
13525 * Propagate to children as appropriate. Unlike most other ALTER
13526 * routines, we have to do this one level of recursion at a time; we can't
13527 * use find_all_inheritors to do it in one pass.
13528 */
13529 if (!is_no_inherit_constraint)
13530 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13531 else
13532 children = NIL;
13533
13534 foreach_oid(childrelid, children)
13535 {
13536 Relation childrel;
13537 HeapTuple tuple;
13538 Form_pg_constraint childcon;
13539
13540 /* find_inheritance_children already got lock */
13541 childrel = table_open(childrelid, NoLock);
13542 CheckAlterTableIsSafe(childrel);
13543
13544 /*
13545 * We search for not-null constraints by column name, and others by
13546 * constraint name.
13547 */
13548 if (con->contype == CONSTRAINT_NOTNULL)
13549 {
13550 tuple = findNotNullConstraint(childrelid, colname);
13551 if (!HeapTupleIsValid(tuple))
13552 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13553 colname, RelationGetRelid(childrel));
13554 }
13555 else
13556 {
13557 SysScanDesc scan;
13558 ScanKeyData skey[3];
13559
13560 ScanKeyInit(&skey[0],
13561 Anum_pg_constraint_conrelid,
13562 BTEqualStrategyNumber, F_OIDEQ,
13563 ObjectIdGetDatum(childrelid));
13564 ScanKeyInit(&skey[1],
13565 Anum_pg_constraint_contypid,
13566 BTEqualStrategyNumber, F_OIDEQ,
13568 ScanKeyInit(&skey[2],
13569 Anum_pg_constraint_conname,
13570 BTEqualStrategyNumber, F_NAMEEQ,
13571 CStringGetDatum(constrName));
13572 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13573 true, NULL, 3, skey);
13574 /* There can only be one, so no need to loop */
13575 tuple = systable_getnext(scan);
13576 if (!HeapTupleIsValid(tuple))
13577 ereport(ERROR,
13578 (errcode(ERRCODE_UNDEFINED_OBJECT),
13579 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13580 constrName,
13581 RelationGetRelationName(childrel))));
13582 tuple = heap_copytuple(tuple);
13583 systable_endscan(scan);
13584 }
13585
13586 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
13587
13588 /* Right now only CHECK and not-null constraints can be inherited */
13589 if (childcon->contype != CONSTRAINT_CHECK &&
13590 childcon->contype != CONSTRAINT_NOTNULL)
13591 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
13592
13593 if (childcon->coninhcount <= 0) /* shouldn't happen */
13594 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13595 childrelid, NameStr(childcon->conname));
13596
13597 if (recurse)
13598 {
13599 /*
13600 * If the child constraint has other definition sources, just
13601 * decrement its inheritance count; if not, recurse to delete it.
13602 */
13603 if (childcon->coninhcount == 1 && !childcon->conislocal)
13604 {
13605 /* Time to delete this child constraint, too */
13606 dropconstraint_internal(childrel, tuple, behavior,
13607 recurse, true, missing_ok,
13608 lockmode);
13609 }
13610 else
13611 {
13612 /* Child constraint must survive my deletion */
13613 childcon->coninhcount--;
13614 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13615
13616 /* Make update visible */
13618 }
13619 }
13620 else
13621 {
13622 /*
13623 * If we were told to drop ONLY in this table (no recursion) and
13624 * there are no further parents for this constraint, we need to
13625 * mark the inheritors' constraints as locally defined rather than
13626 * inherited.
13627 */
13628 childcon->coninhcount--;
13629 if (childcon->coninhcount == 0)
13630 childcon->conislocal = true;
13631
13632 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13633
13634 /* Make update visible */
13636 }
13637
13638 heap_freetuple(tuple);
13639
13640 table_close(childrel, NoLock);
13641 }
13642
13644
13645 return conobj;
13646}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok)
Definition: relcache.c:4975
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition: relcache.c:5231
@ INDEX_ATTR_BITMAP_PRIMARY_KEY
Definition: relcache.h:62
@ INDEX_ATTR_BITMAP_IDENTITY_KEY
Definition: relcache.h:63
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 1424 of file tablecmds.c.

1425{
1426 const struct dropmsgstrings *rentry;
1427
1428 if (rel->schemaname != NULL &&
1430 {
1431 if (!missing_ok)
1432 {
1433 ereport(ERROR,
1434 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1435 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1436 }
1437 else
1438 {
1440 (errmsg("schema \"%s\" does not exist, skipping",
1441 rel->schemaname)));
1442 }
1443 return;
1444 }
1445
1446 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1447 {
1448 if (rentry->kind == rightkind)
1449 {
1450 if (!missing_ok)
1451 {
1452 ereport(ERROR,
1453 (errcode(rentry->nonexistent_code),
1454 errmsg(rentry->nonexistent_msg, rel->relname)));
1455 }
1456 else
1457 {
1458 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1459 break;
1460 }
1461 }
1462 }
1463
1464 Assert(rentry->kind != '\0'); /* Should be impossible */
1465}
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 1472 of file tablecmds.c.

1473{
1474 const struct dropmsgstrings *rentry;
1475 const struct dropmsgstrings *wentry;
1476
1477 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1478 if (rentry->kind == rightkind)
1479 break;
1480 Assert(rentry->kind != '\0');
1481
1482 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1483 if (wentry->kind == wrongkind)
1484 break;
1485 /* wrongkind could be something we don't have in our table... */
1486
1487 ereport(ERROR,
1488 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1489 errmsg(rentry->nota_msg, relname),
1490 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1491}
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 11738 of file tablecmds.c.

11740{
11742 SysScanDesc scan;
11743 HeapTuple trigtup;
11744
11746 Anum_pg_trigger_tgconstraint,
11747 BTEqualStrategyNumber, F_OIDEQ,
11748 ObjectIdGetDatum(conoid));
11749 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11750 NULL, 1, &key);
11751 while ((trigtup = systable_getnext(scan)) != NULL)
11752 {
11753 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11754 ObjectAddress trigger;
11755
11756 if (trgform->tgconstrrelid != conrelid)
11757 continue;
11758 if (trgform->tgrelid != confrelid)
11759 continue;
11760
11761 /*
11762 * The constraint is originally set up to contain this trigger as an
11763 * implementation object, so there's a dependency record that links
11764 * the two; however, since the trigger is no longer needed, we remove
11765 * the dependency link in order to be able to drop the trigger while
11766 * keeping the constraint intact.
11767 */
11768 deleteDependencyRecordsFor(TriggerRelationId,
11769 trgform->oid,
11770 false);
11771 /* make dependency deletion visible to performDeletion */
11773 ObjectAddressSet(trigger, TriggerRelationId,
11774 trgform->oid);
11775 performDeletion(&trigger, DROP_RESTRICT, 0);
11776 /* make trigger drop visible, in case the loop iterates */
11778 }
11779
11780 systable_endscan(scan);
11781}

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

Referenced by AttachPartitionForeignKey().

◆ ExecuteTruncate()

void ExecuteTruncate ( TruncateStmt stmt)

Definition at line 1822 of file tablecmds.c.

1823{
1824 List *rels = NIL;
1825 List *relids = NIL;
1826 List *relids_logged = NIL;
1827 ListCell *cell;
1828
1829 /*
1830 * Open, exclusive-lock, and check all the explicitly-specified relations
1831 */
1832 foreach(cell, stmt->relations)
1833 {
1834 RangeVar *rv = lfirst(cell);
1835 Relation rel;
1836 bool recurse = rv->inh;
1837 Oid myrelid;
1838 LOCKMODE lockmode = AccessExclusiveLock;
1839
1840 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1842 NULL);
1843
1844 /* don't throw error for "TRUNCATE foo, foo" */
1845 if (list_member_oid(relids, myrelid))
1846 continue;
1847
1848 /* open the relation, we already hold a lock on it */
1849 rel = table_open(myrelid, NoLock);
1850
1851 /*
1852 * RangeVarGetRelidExtended() has done most checks with its callback,
1853 * but other checks with the now-opened Relation remain.
1854 */
1856
1857 rels = lappend(rels, rel);
1858 relids = lappend_oid(relids, myrelid);
1859
1860 /* Log this relation only if needed for logical decoding */
1862 relids_logged = lappend_oid(relids_logged, myrelid);
1863
1864 if (recurse)
1865 {
1866 ListCell *child;
1867 List *children;
1868
1869 children = find_all_inheritors(myrelid, lockmode, NULL);
1870
1871 foreach(child, children)
1872 {
1873 Oid childrelid = lfirst_oid(child);
1874
1875 if (list_member_oid(relids, childrelid))
1876 continue;
1877
1878 /* find_all_inheritors already got lock */
1879 rel = table_open(childrelid, NoLock);
1880
1881 /*
1882 * It is possible that the parent table has children that are
1883 * temp tables of other backends. We cannot safely access
1884 * such tables (because of buffering issues), and the best
1885 * thing to do is to silently ignore them. Note that this
1886 * check is the same as one of the checks done in
1887 * truncate_check_activity() called below, still it is kept
1888 * here for simplicity.
1889 */
1890 if (RELATION_IS_OTHER_TEMP(rel))
1891 {
1892 table_close(rel, lockmode);
1893 continue;
1894 }
1895
1896 /*
1897 * Inherited TRUNCATE commands perform access permission
1898 * checks on the parent table only. So we skip checking the
1899 * children's permissions and don't call
1900 * truncate_check_perms() here.
1901 */
1904
1905 rels = lappend(rels, rel);
1906 relids = lappend_oid(relids, childrelid);
1907
1908 /* Log this relation only if needed for logical decoding */
1910 relids_logged = lappend_oid(relids_logged, childrelid);
1911 }
1912 }
1913 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1914 ereport(ERROR,
1915 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1916 errmsg("cannot truncate only a partitioned table"),
1917 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1918 }
1919
1920 ExecuteTruncateGuts(rels, relids, relids_logged,
1921 stmt->behavior, stmt->restart_seqs, false);
1922
1923 /* And close the rels */
1924 foreach(cell, rels)
1925 {
1926 Relation rel = (Relation) lfirst(cell);
1927
1928 table_close(rel, NoLock);
1929 }
1930}
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:709
struct RelationData * Relation
Definition: relcache.h:27
static void truncate_check_activity(Relation rel)
Definition: tablecmds.c:2399
static void truncate_check_rel(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2333
static void RangeVarCallbackForTruncate(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:18797
void ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
Definition: tablecmds.c:1946

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

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

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

Referenced by apply_handle_truncate(), and ExecuteTruncate().

◆ find_composite_type_dependencies()

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

Definition at line 6839 of file tablecmds.c.

6841{
6842 Relation depRel;
6843 ScanKeyData key[2];
6844 SysScanDesc depScan;
6845 HeapTuple depTup;
6846
6847 /* since this function recurses, it could be driven to stack overflow */
6849
6850 /*
6851 * We scan pg_depend to find those things that depend on the given type.
6852 * (We assume we can ignore refobjsubid for a type.)
6853 */
6854 depRel = table_open(DependRelationId, AccessShareLock);
6855
6856 ScanKeyInit(&key[0],
6857 Anum_pg_depend_refclassid,
6858 BTEqualStrategyNumber, F_OIDEQ,
6859 ObjectIdGetDatum(TypeRelationId));
6860 ScanKeyInit(&key[1],
6861 Anum_pg_depend_refobjid,
6862 BTEqualStrategyNumber, F_OIDEQ,
6863 ObjectIdGetDatum(typeOid));
6864
6865 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6866 NULL, 2, key);
6867
6868 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6869 {
6870 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6871 Relation rel;
6872 TupleDesc tupleDesc;
6874
6875 /* Check for directly dependent types */
6876 if (pg_depend->classid == TypeRelationId)
6877 {
6878 /*
6879 * This must be an array, domain, or range containing the given
6880 * type, so recursively check for uses of this type. Note that
6881 * any error message will mention the original type not the
6882 * container; this is intentional.
6883 */
6884 find_composite_type_dependencies(pg_depend->objid,
6885 origRelation, origTypeName);
6886 continue;
6887 }
6888
6889 /* Else, ignore dependees that aren't relations */
6890 if (pg_depend->classid != RelationRelationId)
6891 continue;
6892
6893 rel = relation_open(pg_depend->objid, AccessShareLock);
6894 tupleDesc = RelationGetDescr(rel);
6895
6896 /*
6897 * If objsubid identifies a specific column, refer to that in error
6898 * messages. Otherwise, search to see if there's a user column of the
6899 * type. (We assume system columns are never of interesting types.)
6900 * The search is needed because an index containing an expression
6901 * column of the target type will just be recorded as a whole-relation
6902 * dependency. If we do not find a column of the type, the dependency
6903 * must indicate that the type is transiently referenced in an index
6904 * expression but not stored on disk, which we assume is OK, just as
6905 * we do for references in views. (It could also be that the target
6906 * type is embedded in some container type that is stored in an index
6907 * column, but the previous recursion should catch such cases.)
6908 */
6909 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6910 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6911 else
6912 {
6913 att = NULL;
6914 for (int attno = 1; attno <= tupleDesc->natts; attno++)
6915 {
6916 att = TupleDescAttr(tupleDesc, attno - 1);
6917 if (att->atttypid == typeOid && !att->attisdropped)
6918 break;
6919 att = NULL;
6920 }
6921 if (att == NULL)
6922 {
6923 /* No such column, so assume OK */
6925 continue;
6926 }
6927 }
6928
6929 /*
6930 * We definitely should reject if the relation has storage. If it's
6931 * partitioned, then perhaps we don't have to reject: if there are
6932 * partitions then we'll fail when we find one, else there is no
6933 * stored data to worry about. However, it's possible that the type
6934 * change would affect conclusions about whether the type is sortable
6935 * or hashable and thus (if it's a partitioning column) break the
6936 * partitioning rule. For now, reject for partitioned rels too.
6937 */
6938 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6939 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6940 {
6941 if (origTypeName)
6942 ereport(ERROR,
6943 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6944 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6945 origTypeName,
6947 NameStr(att->attname))));
6948 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6949 ereport(ERROR,
6950 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6951 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6952 RelationGetRelationName(origRelation),
6954 NameStr(att->attname))));
6955 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6956 ereport(ERROR,
6957 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6958 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6959 RelationGetRelationName(origRelation),
6961 NameStr(att->attname))));
6962 else
6963 ereport(ERROR,
6964 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6965 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6966 RelationGetRelationName(origRelation),
6968 NameStr(att->attname))));
6969 }
6970 else if (OidIsValid(rel->rd_rel->reltype))
6971 {
6972 /*
6973 * A view or composite type itself isn't a problem, but we must
6974 * recursively check for indirect dependencies via its rowtype.
6975 */
6977 origRelation, origTypeName);
6978 }
6979
6981 }
6982
6983 systable_endscan(depScan);
6984
6986}

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

6998{
6999 Relation classRel;
7000 ScanKeyData key[1];
7001 TableScanDesc scan;
7002 HeapTuple tuple;
7003 List *result = NIL;
7004
7005 classRel = table_open(RelationRelationId, AccessShareLock);
7006
7007 ScanKeyInit(&key[0],
7008 Anum_pg_class_reloftype,
7009 BTEqualStrategyNumber, F_OIDEQ,
7010 ObjectIdGetDatum(typeOid));
7011
7012 scan = table_beginscan_catalog(classRel, 1, key);
7013
7014 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7015 {
7016 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7017
7018 if (behavior == DROP_RESTRICT)
7019 ereport(ERROR,
7020 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7021 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7022 typeName),
7023 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7024 else
7025 result = lappend_oid(result, classform->oid);
7026 }
7027
7028 table_endscan(scan);
7029 table_close(classRel, AccessShareLock);
7030
7031 return result;
7032}

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

3572{
3573 ListCell *lc;
3574 int i = 1;
3575
3576 foreach(lc, columns)
3577 {
3578 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3579 return i;
3580
3581 i++;
3582 }
3583 return 0;
3584}

References i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 12927 of file tablecmds.c.

12928{
12929 CoercionPathType ret;
12930
12931 if (targetTypeId == sourceTypeId)
12932 {
12934 *funcid = InvalidOid;
12935 }
12936 else
12937 {
12938 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12939 COERCION_IMPLICIT, funcid);
12940 if (ret == COERCION_PATH_NONE)
12941 /* A previously-relied-upon cast is now gone. */
12942 elog(ERROR, "could not find cast from %u to %u",
12943 sourceTypeId, targetTypeId);
12944 }
12945
12946 return ret;
12947}
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 21280 of file tablecmds.c.

21281{
21282 char cmethod;
21283
21284 if (compression == NULL || strcmp(compression, "default") == 0)
21286
21287 /*
21288 * To specify a nondefault method, the column data type must be toastable.
21289 * Note this says nothing about whether the column's attstorage setting
21290 * permits compression; we intentionally allow attstorage and
21291 * attcompression to be independent. But with a non-toastable type,
21292 * attstorage could not be set to a value that would permit compression.
21293 *
21294 * We don't actually need to enforce this, since nothing bad would happen
21295 * if attcompression were non-default; it would never be consulted. But
21296 * it seems more user-friendly to complain about a certainly-useless
21297 * attempt to set the property.
21298 */
21299 if (!TypeIsToastable(atttypid))
21300 ereport(ERROR,
21301 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21302 errmsg("column data type %s does not support compression",
21303 format_type_be(atttypid))));
21304
21305 cmethod = CompressionNameToMethod(compression);
21306 if (!CompressionMethodIsValid(cmethod))
21307 ereport(ERROR,
21308 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21309 errmsg("invalid compression method \"%s\"", compression)));
21310
21311 return cmethod;
21312}
#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 21318 of file tablecmds.c.

21319{
21320 char cstorage = 0;
21321
21322 if (pg_strcasecmp(storagemode, "plain") == 0)
21323 cstorage = TYPSTORAGE_PLAIN;
21324 else if (pg_strcasecmp(storagemode, "external") == 0)
21325 cstorage = TYPSTORAGE_EXTERNAL;
21326 else if (pg_strcasecmp(storagemode, "extended") == 0)
21327 cstorage = TYPSTORAGE_EXTENDED;
21328 else if (pg_strcasecmp(storagemode, "main") == 0)
21329 cstorage = TYPSTORAGE_MAIN;
21330 else if (pg_strcasecmp(storagemode, "default") == 0)
21331 cstorage = get_typstorage(atttypid);
21332 else
21333 ereport(ERROR,
21334 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21335 errmsg("invalid storage type \"%s\"",
21336 storagemode)));
21337
21338 /*
21339 * safety check: do not allow toasted storage modes unless column datatype
21340 * is TOAST-aware.
21341 */
21342 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
21343 ereport(ERROR,
21344 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21345 errmsg("column data type %s can only have storage PLAIN",
21346 format_type_be(atttypid))));
21347
21348 return cstorage;
21349}
char get_typstorage(Oid typid)
Definition: lsyscache.c:2502
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 11789 of file tablecmds.c.

11793{
11795 SysScanDesc scan;
11796 HeapTuple trigtup;
11797
11798 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11800 Anum_pg_trigger_tgconstraint,
11801 BTEqualStrategyNumber, F_OIDEQ,
11802 ObjectIdGetDatum(conoid));
11803
11804 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11805 NULL, 1, &key);
11806 while ((trigtup = systable_getnext(scan)) != NULL)
11807 {
11808 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11809
11810 if (trgform->tgconstrrelid != conrelid)
11811 continue;
11812 if (trgform->tgrelid != confrelid)
11813 continue;
11814 /* Only ever look at "action" triggers on the PK side. */
11815 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11816 continue;
11817 if (TRIGGER_FOR_DELETE(trgform->tgtype))
11818 {
11819 Assert(*deleteTriggerOid == InvalidOid);
11820 *deleteTriggerOid = trgform->oid;
11821 }
11822 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11823 {
11824 Assert(*updateTriggerOid == InvalidOid);
11825 *updateTriggerOid = trgform->oid;
11826 }
11827#ifndef USE_ASSERT_CHECKING
11828 /* In an assert-enabled build, continue looking to find duplicates */
11829 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11830 break;
11831#endif
11832 }
11833
11834 if (!OidIsValid(*deleteTriggerOid))
11835 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11836 conoid);
11837 if (!OidIsValid(*updateTriggerOid))
11838 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11839 conoid);
11840
11841 systable_endscan(scan);
11842}
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3210
#define RI_TRIGGER_PK
Definition: trigger.h:283

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

Referenced by CloneFkReferenced().

◆ GetForeignKeyCheckTriggers()

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

Definition at line 11850 of file tablecmds.c.

11854{
11856 SysScanDesc scan;
11857 HeapTuple trigtup;
11858
11859 *insertTriggerOid = *updateTriggerOid = InvalidOid;
11861 Anum_pg_trigger_tgconstraint,
11862 BTEqualStrategyNumber, F_OIDEQ,
11863 ObjectIdGetDatum(conoid));
11864
11865 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11866 NULL, 1, &key);
11867 while ((trigtup = systable_getnext(scan)) != NULL)
11868 {
11869 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11870
11871 if (trgform->tgconstrrelid != confrelid)
11872 continue;
11873 if (trgform->tgrelid != conrelid)
11874 continue;
11875 /* Only ever look at "check" triggers on the FK side. */
11876 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11877 continue;
11878 if (TRIGGER_FOR_INSERT(trgform->tgtype))
11879 {
11880 Assert(*insertTriggerOid == InvalidOid);
11881 *insertTriggerOid = trgform->oid;
11882 }
11883 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11884 {
11885 Assert(*updateTriggerOid == InvalidOid);
11886 *updateTriggerOid = trgform->oid;
11887 }
11888#ifndef USE_ASSERT_CHECKING
11889 /* In an assert-enabled build, continue looking to find duplicates. */
11890 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11891 break;
11892#endif
11893 }
11894
11895 if (!OidIsValid(*insertTriggerOid))
11896 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11897 conoid);
11898 if (!OidIsValid(*updateTriggerOid))
11899 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11900 conoid);
11901
11902 systable_endscan(scan);
11903}
#define RI_TRIGGER_FK
Definition: trigger.h:284

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

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

◆ GetParentedForeignKeyRefs()

static List * GetParentedForeignKeyRefs ( Relation  partition)
static

Definition at line 21178 of file tablecmds.c.

21179{
21180 Relation pg_constraint;
21181 HeapTuple tuple;
21182 SysScanDesc scan;
21183 ScanKeyData key[2];
21184 List *constraints = NIL;
21185
21186 /*
21187 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21188 * scan.
21189 */
21190 if (RelationGetIndexList(partition) == NIL ||
21193 return NIL;
21194
21195 /* Search for constraints referencing this table */
21196 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21197 ScanKeyInit(&key[0],
21198 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21199 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21200 ScanKeyInit(&key[1],
21201 Anum_pg_constraint_contype, BTEqualStrategyNumber,
21202 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21203
21204 /* XXX This is a seqscan, as we don't have a usable index */
21205 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21206 while ((tuple = systable_getnext(scan)) != NULL)
21207 {
21208 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21209
21210 /*
21211 * We only need to process constraints that are part of larger ones.
21212 */
21213 if (!OidIsValid(constrForm->conparentid))
21214 continue;
21215
21216 constraints = lappend_oid(constraints, constrForm->oid);
21217 }
21218
21219 systable_endscan(scan);
21220 table_close(pg_constraint, AccessShareLock);
21221
21222 return constraints;
21223}
#define bms_is_empty(a)
Definition: bitmapset.h:118
@ INDEX_ATTR_BITMAP_KEY
Definition: relcache.h:61

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

16415{
16416 SMgrRelation dstrel;
16417
16418 /*
16419 * Since we copy the file directly without looking at the shared buffers,
16420 * we'd better first flush out any pages of the source relation that are
16421 * in shared buffers. We assume no new changes will be made while we are
16422 * holding exclusive lock on the rel.
16423 */
16425
16426 /*
16427 * Create and copy all forks of the relation, and schedule unlinking of
16428 * old physical files.
16429 *
16430 * NOTE: any conflict in relfilenumber value will be caught in
16431 * RelationCreateStorage().
16432 */
16433 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
16434
16435 /* copy main fork */
16437 rel->rd_rel->relpersistence);
16438
16439 /* copy those extra forks that exist */
16440 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
16441 forkNum <= MAX_FORKNUM; forkNum++)
16442 {
16443 if (smgrexists(RelationGetSmgr(rel), forkNum))
16444 {
16445 smgrcreate(dstrel, forkNum, false);
16446
16447 /*
16448 * WAL log creation if the relation is persistent, or this is the
16449 * init fork of an unlogged relation.
16450 */
16451 if (RelationIsPermanent(rel) ||
16452 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
16453 forkNum == INIT_FORKNUM))
16454 log_smgrcreate(&newrlocator, forkNum);
16455 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
16456 rel->rd_rel->relpersistence);
16457 }
16458 }
16459
16460 /* drop old relation, and close new one */
16462 smgrclose(dstrel);
16463}
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:4506
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:575
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:463
void smgrclose(SMgrRelation reln)
Definition: smgr.c:356
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:444
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition: storage.c:477
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition: storage.c:121
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition: storage.c:186
void RelationDropStorage(Relation rel)
Definition: storage.c:206

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

17135{
17136 Relation catalogRelation;
17137 SysScanDesc scan;
17139 HeapTuple inheritsTuple;
17140 bool found = false;
17141
17142 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17143
17144 /*
17145 * Find pg_inherits entries by inhparent. (We need to scan them all in
17146 * order to verify that no other partition is pending detach.)
17147 */
17148 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17150 Anum_pg_inherits_inhparent,
17151 BTEqualStrategyNumber, F_OIDEQ,
17152 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17153 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17154 true, NULL, 1, &key);
17155
17156 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17157 {
17158 Form_pg_inherits inhForm;
17159
17160 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17161 if (inhForm->inhdetachpending)
17162 ereport(ERROR,
17163 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17164 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17165 get_rel_name(inhForm->inhrelid),
17166 get_namespace_name(parent_rel->rd_rel->relnamespace),
17167 RelationGetRelationName(parent_rel)),
17168 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17169
17170 if (inhForm->inhrelid == RelationGetRelid(child_rel))
17171 {
17172 HeapTuple newtup;
17173
17174 newtup = heap_copytuple(inheritsTuple);
17175 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17176
17177 CatalogTupleUpdate(catalogRelation,
17178 &inheritsTuple->t_self,
17179 newtup);
17180 found = true;
17181 heap_freetuple(newtup);
17182 /* keep looking, to ensure we catch others pending detach */
17183 }
17184 }
17185
17186 /* Done */
17187 systable_endscan(scan);
17188 table_close(catalogRelation, RowExclusiveLock);
17189
17190 if (!found)
17191 ereport(ERROR,
17193 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17194 RelationGetRelationName(child_rel),
17195 RelationGetRelationName(parent_rel))));
17196}
#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 2507 of file tablecmds.c.

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

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

16768{
16769 Relation attrrel;
16770 TupleDesc parent_desc;
16771
16772 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
16773 parent_desc = RelationGetDescr(parent_rel);
16774
16775 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
16776 {
16777 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
16778 char *parent_attname = NameStr(parent_att->attname);
16779 HeapTuple tuple;
16780
16781 /* Ignore dropped columns in the parent. */
16782 if (parent_att->attisdropped)
16783 continue;
16784
16785 /* Find same column in child (matching on column name). */
16786 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
16787 if (HeapTupleIsValid(tuple))
16788 {
16789 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
16790
16791 if (parent_att->atttypid != child_att->atttypid ||
16792 parent_att->atttypmod != child_att->atttypmod)
16793 ereport(ERROR,
16794 (errcode(ERRCODE_DATATYPE_MISMATCH),
16795 errmsg("child table \"%s\" has different type for column \"%s\"",
16796 RelationGetRelationName(child_rel), parent_attname)));
16797
16798 if (parent_att->attcollation != child_att->attcollation)
16799 ereport(ERROR,
16800 (errcode(ERRCODE_COLLATION_MISMATCH),
16801 errmsg("child table \"%s\" has different collation for column \"%s\"",
16802 RelationGetRelationName(child_rel), parent_attname)));
16803
16804 /*
16805 * If the parent has a not-null constraint that's not NO INHERIT,
16806 * make sure the child has one too.
16807 *
16808 * Other constraints are checked elsewhere.
16809 */
16810 if (parent_att->attnotnull && !child_att->attnotnull)
16811 {
16812 HeapTuple contup;
16813
16814 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
16815 parent_att->attnum);
16816 if (HeapTupleIsValid(contup) &&
16817 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
16818 ereport(ERROR,
16819 errcode(ERRCODE_DATATYPE_MISMATCH),
16820 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16821 parent_attname, RelationGetRelationName(child_rel)));
16822 }
16823
16824 /*
16825 * Child column must be generated if and only if parent column is.
16826 */
16827 if (parent_att->attgenerated && !child_att->attgenerated)
16828 ereport(ERROR,
16829 (errcode(ERRCODE_DATATYPE_MISMATCH),
16830 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
16831 if (child_att->attgenerated && !parent_att->attgenerated)
16832 ereport(ERROR,
16833 (errcode(ERRCODE_DATATYPE_MISMATCH),
16834 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
16835
16836 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
16837 ereport(ERROR,
16838 (errcode(ERRCODE_DATATYPE_MISMATCH),
16839 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
16840 errdetail("Parent column is %s, child column is %s.",
16841 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
16842 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
16843
16844 /*
16845 * Regular inheritance children are independent enough not to
16846 * inherit identity columns. But partitions are integral part of
16847 * a partitioned table and inherit identity column.
16848 */
16849 if (ispartition)
16850 child_att->attidentity = parent_att->attidentity;
16851
16852 /*
16853 * OK, bump the child column's inheritance count. (If we fail
16854 * later on, this change will just roll back.)
16855 */
16856 if (pg_add_s16_overflow(child_att->attinhcount, 1,
16857 &child_att->attinhcount))
16858 ereport(ERROR,
16859 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16860 errmsg("too many inheritance parents"));
16861
16862 /*
16863 * In case of partitions, we must enforce that value of attislocal
16864 * is same in all partitions. (Note: there are only inherited
16865 * attributes in partitions)
16866 */
16867 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16868 {
16869 Assert(child_att->attinhcount == 1);
16870 child_att->attislocal = false;
16871 }
16872
16873 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
16874 heap_freetuple(tuple);
16875 }
16876 else
16877 {
16878 ereport(ERROR,
16879 (errcode(ERRCODE_DATATYPE_MISMATCH),
16880 errmsg("child table is missing column \"%s\"", parent_attname)));
16881 }
16882 }
16883
16884 table_close(attrrel, RowExclusiveLock);
16885}

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

3129{
3130 ListCell *lc;
3131 CookedConstraint *newcon;
3132
3133 foreach(lc, constraints)
3134 {
3136
3137 Assert(ccon->contype == CONSTR_CHECK);
3138
3139 /* Non-matching names never conflict */
3140 if (strcmp(ccon->name, name) != 0)
3141 continue;
3142
3143 if (equal(expr, ccon->expr))
3144 {
3145 /* OK to merge constraint with existing */
3146 if (pg_add_s16_overflow(ccon->inhcount, 1,
3147 &ccon->inhcount))
3148 ereport(ERROR,
3149 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3150 errmsg("too many inheritance parents"));
3151
3152 /*
3153 * When enforceability differs, the merged constraint should be
3154 * marked as ENFORCED because one of the parents is ENFORCED.
3155 */
3156 if (!ccon->is_enforced && is_enforced)
3157 {
3158 ccon->is_enforced = true;
3159 ccon->skip_validation = false;
3160 }
3161
3162 return constraints;
3163 }
3164
3165 ereport(ERROR,
3167 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3168 name)));
3169 }
3170
3171 /*
3172 * Constraint couldn't be merged with an existing one and also didn't
3173 * conflict with an existing one, so add it as a new one to the list.
3174 */
3176 newcon->contype = CONSTR_CHECK;
3177 newcon->name = pstrdup(name);
3178 newcon->expr = expr;
3179 newcon->inhcount = 1;
3180 newcon->is_enforced = is_enforced;
3181 newcon->skip_validation = !is_enforced;
3182 return lappend(constraints, newcon);
3183}
#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 3207 of file tablecmds.c.

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

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

16906{
16907 Relation constraintrel;
16908 SysScanDesc parent_scan;
16909 ScanKeyData parent_key;
16910 HeapTuple parent_tuple;
16911 Oid parent_relid = RelationGetRelid(parent_rel);
16912 AttrMap *attmap;
16913
16914 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
16915
16916 /* Outer loop scans through the parent's constraint definitions */
16917 ScanKeyInit(&parent_key,
16918 Anum_pg_constraint_conrelid,
16919 BTEqualStrategyNumber, F_OIDEQ,
16920 ObjectIdGetDatum(parent_relid));
16921 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16922 true, NULL, 1, &parent_key);
16923
16924 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
16925 RelationGetDescr(child_rel),
16926 true);
16927
16928 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
16929 {
16930 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
16931 SysScanDesc child_scan;
16932 ScanKeyData child_key;
16933 HeapTuple child_tuple;
16934 AttrNumber parent_attno;
16935 bool found = false;
16936
16937 if (parent_con->contype != CONSTRAINT_CHECK &&
16938 parent_con->contype != CONSTRAINT_NOTNULL)
16939 continue;
16940
16941 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16942 if (parent_con->connoinherit)
16943 continue;
16944
16945 if (parent_con->contype == CONSTRAINT_NOTNULL)
16946 parent_attno = extractNotNullColumn(parent_tuple);
16947 else
16948 parent_attno = InvalidAttrNumber;
16949
16950 /* Search for a child constraint matching this one */
16951 ScanKeyInit(&child_key,
16952 Anum_pg_constraint_conrelid,
16953 BTEqualStrategyNumber, F_OIDEQ,
16955 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16956 true, NULL, 1, &child_key);
16957
16958 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
16959 {
16960 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
16961 HeapTuple child_copy;
16962
16963 if (child_con->contype != parent_con->contype)
16964 continue;
16965
16966 /*
16967 * CHECK constraint are matched by constraint name, NOT NULL ones
16968 * by attribute number.
16969 */
16970 if (child_con->contype == CONSTRAINT_CHECK)
16971 {
16972 if (strcmp(NameStr(parent_con->conname),
16973 NameStr(child_con->conname)) != 0)
16974 continue;
16975 }
16976 else if (child_con->contype == CONSTRAINT_NOTNULL)
16977 {
16978 Form_pg_attribute parent_attr;
16979 Form_pg_attribute child_attr;
16980 AttrNumber child_attno;
16981
16982 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
16983 child_attno = extractNotNullColumn(child_tuple);
16984 if (parent_attno != attmap->attnums[child_attno - 1])
16985 continue;
16986
16987 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
16988 /* there shouldn't be constraints on dropped columns */
16989 if (parent_attr->attisdropped || child_attr->attisdropped)
16990 elog(ERROR, "found not-null constraint on dropped columns");
16991 }
16992
16993 if (child_con->contype == CONSTRAINT_CHECK &&
16994 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
16995 ereport(ERROR,
16996 (errcode(ERRCODE_DATATYPE_MISMATCH),
16997 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16998 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
16999
17000 /*
17001 * If the child constraint is "no inherit" then cannot merge
17002 */
17003 if (child_con->connoinherit)
17004 ereport(ERROR,
17005 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17006 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17007 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17008
17009 /*
17010 * If the child constraint is "not valid" then cannot merge with a
17011 * valid parent constraint
17012 */
17013 if (parent_con->convalidated && child_con->conenforced &&
17014 !child_con->convalidated)
17015 ereport(ERROR,
17016 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17017 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17018 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17019
17020 /*
17021 * A non-enforced child constraint cannot be merged with an
17022 * enforced parent constraint. However, the reverse is allowed,
17023 * where the child constraint is enforced.
17024 */
17025 if (parent_con->conenforced && !child_con->conenforced)
17026 ereport(ERROR,
17027 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17028 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17029 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17030
17031 /*
17032 * OK, bump the child constraint's inheritance count. (If we fail
17033 * later on, this change will just roll back.)
17034 */
17035 child_copy = heap_copytuple(child_tuple);
17036 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17037
17038 if (pg_add_s16_overflow(child_con->coninhcount, 1,
17039 &child_con->coninhcount))
17040 ereport(ERROR,
17041 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17042 errmsg("too many inheritance parents"));
17043
17044 /*
17045 * In case of partitions, an inherited constraint must be
17046 * inherited only once since it cannot have multiple parents and
17047 * it is never considered local.
17048 */
17049 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17050 {
17051 Assert(child_con->coninhcount == 1);
17052 child_con->conislocal = false;
17053 }
17054
17055 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17056 heap_freetuple(child_copy);
17057
17058 found = true;
17059 break;
17060 }
17061
17062 systable_endscan(child_scan);
17063
17064 if (!found)
17065 {
17066 if (parent_con->contype == CONSTRAINT_NOTNULL)
17067 ereport(ERROR,
17068 errcode(ERRCODE_DATATYPE_MISMATCH),
17069 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17070 get_attname(parent_relid,
17071 extractNotNullColumn(parent_tuple),
17072 false),
17073 RelationGetRelationName(child_rel)));
17074
17075 ereport(ERROR,
17076 (errcode(ERRCODE_DATATYPE_MISMATCH),
17077 errmsg("child table is missing constraint \"%s\"",
17078 NameStr(parent_con->conname))));
17079 }
17080 }
17081
17082 systable_endscan(parent_scan);
17083 table_close(constraintrel, RowExclusiveLock);
17084}
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
Definition: tablecmds.c:16738

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

3380{
3381 char *attributeName = newdef->colname;
3382 ColumnDef *prevdef;
3383 Oid prevtypeid,
3384 newtypeid;
3385 int32 prevtypmod,
3386 newtypmod;
3387 Oid prevcollid,
3388 newcollid;
3389
3391 (errmsg("merging multiple inherited definitions of column \"%s\"",
3392 attributeName)));
3393 prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3394
3395 /*
3396 * Must have the same type and typmod
3397 */
3398 typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3399 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3400 if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3401 ereport(ERROR,
3402 (errcode(ERRCODE_DATATYPE_MISMATCH),
3403 errmsg("inherited column \"%s\" has a type conflict",
3404 attributeName),
3405 errdetail("%s versus %s",
3406 format_type_with_typemod(prevtypeid, prevtypmod),
3407 format_type_with_typemod(newtypeid, newtypmod))));
3408
3409 /*
3410 * Must have the same collation
3411 */
3412 prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3413 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3414 if (prevcollid != newcollid)
3415 ereport(ERROR,
3416 (errcode(ERRCODE_COLLATION_MISMATCH),
3417 errmsg("inherited column \"%s\" has a collation conflict",
3418 attributeName),
3419 errdetail("\"%s\" versus \"%s\"",
3420 get_collation_name(prevcollid),
3421 get_collation_name(newcollid))));
3422
3423 /*
3424 * Copy/check storage parameter
3425 */
3426 if (prevdef->storage == 0)
3427 prevdef->storage = newdef->storage;
3428 else if (prevdef->storage != newdef->storage)
3429 ereport(ERROR,
3430 (errcode(ERRCODE_DATATYPE_MISMATCH),
3431 errmsg("inherited column \"%s\" has a storage parameter conflict",
3432 attributeName),
3433 errdetail("%s versus %s",
3434 storage_name(prevdef->storage),
3435 storage_name(newdef->storage))));
3436
3437 /*
3438 * Copy/check compression parameter
3439 */
3440 if (prevdef->compression == NULL)
3441 prevdef->compression = newdef->compression;
3442 else if (newdef->compression != NULL)
3443 {
3444 if (strcmp(prevdef->compression, newdef->compression) != 0)
3445 ereport(ERROR,
3446 (errcode(ERRCODE_DATATYPE_MISMATCH),
3447 errmsg("column \"%s\" has a compression method conflict",
3448 attributeName),
3449 errdetail("%s versus %s",
3450 prevdef->compression, newdef->compression)));
3451 }
3452
3453 /*
3454 * Check for GENERATED conflicts
3455 */
3456 if (prevdef->generated != newdef->generated)
3457 ereport(ERROR,
3458 (errcode(ERRCODE_DATATYPE_MISMATCH),
3459 errmsg("inherited column \"%s\" has a generation conflict",
3460 attributeName)));
3461
3462 /*
3463 * Default and other constraints are handled by the caller.
3464 */
3465
3466 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3467 &prevdef->inhcount))
3468 ereport(ERROR,
3469 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3470 errmsg("too many inheritance parents"));
3471
3472 return prevdef;
3473}

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

7977{
7978 NullTest *nnulltest = makeNode(NullTest);
7979
7980 nnulltest->arg = (Expr *) makeVar(1,
7981 attr->attnum,
7982 attr->atttypid,
7983 attr->atttypmod,
7984 attr->attcollation,
7985 0);
7986 nnulltest->nulltesttype = IS_NOT_NULL;
7987
7988 /*
7989 * argisrow = false is correct even for a composite column, because
7990 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7991 * case, just IS DISTINCT FROM NULL.
7992 */
7993 nnulltest->argisrow = false;
7994 nnulltest->location = -1;
7995
7996 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
7997 {
7999 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8000 RelationGetRelationName(rel), NameStr(attr->attname))));
8001 return true;
8002 }
8003
8004 return false;
8005}
@ 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:19369

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

19318{
19319 List *existConstraint = NIL;
19320 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19321 int i;
19322
19323 if (constr && constr->has_not_null)
19324 {
19325 int natts = scanrel->rd_att->natts;
19326
19327 for (i = 1; i <= natts; i++)
19328 {
19329 Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
19330
19331 if (att->attnotnull && !att->attisdropped)
19332 {
19333 NullTest *ntest = makeNode(NullTest);
19334
19335 ntest->arg = (Expr *) makeVar(1,
19336 i,
19337 att->atttypid,
19338 att->atttypmod,
19339 att->attcollation,
19340 0);
19341 ntest->nulltesttype = IS_NOT_NULL;
19342
19343 /*
19344 * argisrow=false is correct even for a composite column,
19345 * because attnotnull does not represent a SQL-spec IS NOT
19346 * NULL test in such a case, just IS DISTINCT FROM NULL.
19347 */
19348 ntest->argisrow = false;
19349 ntest->location = -1;
19350 existConstraint = lappend(existConstraint, ntest);
19351 }
19352 }
19353 }
19354
19355 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
19356}
bool has_not_null
Definition: tupdesc.h:45

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

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

◆ PreCommit_on_commit_actions()

void PreCommit_on_commit_actions ( void  )

Definition at line 18587 of file tablecmds.c.

18588{
18589 ListCell *l;
18590 List *oids_to_truncate = NIL;
18591 List *oids_to_drop = NIL;
18592
18593 foreach(l, on_commits)
18594 {
18595 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18596
18597 /* Ignore entry if already dropped in this xact */
18599 continue;
18600
18601 switch (oc->oncommit)
18602 {
18603 case ONCOMMIT_NOOP:
18605 /* Do nothing (there shouldn't be such entries, actually) */
18606 break;
18608
18609 /*
18610 * If this transaction hasn't accessed any temporary
18611 * relations, we can skip truncating ON COMMIT DELETE ROWS
18612 * tables, as they must still be empty.
18613 */
18615 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18616 break;
18617 case ONCOMMIT_DROP:
18618 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18619 break;
18620 }
18621 }
18622
18623 /*
18624 * Truncate relations before dropping so that all dependencies between
18625 * relations are removed after they are worked on. Doing it like this
18626 * might be a waste as it is possible that a relation being truncated will
18627 * be dropped anyway due to its parent being dropped, but this makes the
18628 * code more robust because of not having to re-check that the relation
18629 * exists at truncation time.
18630 */
18631 if (oids_to_truncate != NIL)
18632 heap_truncate(oids_to_truncate);
18633
18634 if (oids_to_drop != NIL)
18635 {
18636 ObjectAddresses *targetObjects = new_object_addresses();
18637
18638 foreach(l, oids_to_drop)
18639 {
18640 ObjectAddress object;
18641
18642 object.classId = RelationRelationId;
18643 object.objectId = lfirst_oid(l);
18644 object.objectSubId = 0;
18645
18646 Assert(!object_address_present(&object, targetObjects));
18647
18648 add_exact_object_address(&object, targetObjects);
18649 }
18650
18651 /*
18652 * Object deletion might involve toast table access (to clean up
18653 * toasted catalog entries), so ensure we have a valid snapshot.
18654 */
18656
18657 /*
18658 * Since this is an automatic drop, rather than one directly initiated
18659 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18660 */
18663
18665
18666#ifdef USE_ASSERT_CHECKING
18667
18668 /*
18669 * Note that table deletion will call remove_on_commit_action, so the
18670 * entry should get marked as deleted.
18671 */
18672 foreach(l, on_commits)
18673 {
18674 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18675
18676 if (oc->oncommit != ONCOMMIT_DROP)
18677 continue;
18678
18680 }
18681#endif
18682 }
18683}
#define PERFORM_DELETION_QUIETLY
Definition: dependency.h:94
void heap_truncate(List *relids)
Definition: heap.c:3498
@ 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 12511 of file tablecmds.c.

12514{
12516 AlteredTableInfo *tab;
12517 HeapTuple copyTuple;
12518 Form_pg_constraint copy_con;
12519
12520 List *children = NIL;
12521 ListCell *child;
12522 NewConstraint *newcon;
12523 Datum val;
12524 char *conbin;
12525
12526 con = (Form_pg_constraint) GETSTRUCT(contuple);
12527 Assert(con->contype == CONSTRAINT_CHECK);
12528
12529 /*
12530 * If we're recursing, the parent has already done this, so skip it. Also,
12531 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
12532 * for it in the children.
12533 */
12534 if (!recursing && !con->connoinherit)
12535 children = find_all_inheritors(RelationGetRelid(rel),
12536 lockmode, NULL);
12537
12538 /*
12539 * For CHECK constraints, we must ensure that we only mark the constraint
12540 * as validated on the parent if it's already validated on the children.
12541 *
12542 * We recurse before validating on the parent, to reduce risk of
12543 * deadlocks.
12544 */
12545 foreach(child, children)
12546 {
12547 Oid childoid = lfirst_oid(child);
12548 Relation childrel;
12549
12550 if (childoid == RelationGetRelid(rel))
12551 continue;
12552
12553 /*
12554 * If we are told not to recurse, there had better not be any child
12555 * tables, because we can't mark the constraint on the parent valid
12556 * unless it is valid for all child tables.
12557 */
12558 if (!recurse)
12559 ereport(ERROR,
12560 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12561 errmsg("constraint must be validated on child tables too")));
12562
12563 /* find_all_inheritors already got lock */
12564 childrel = table_open(childoid, NoLock);
12565
12566 ATExecValidateConstraint(wqueue, childrel, constrName, false,
12567 true, lockmode);
12568 table_close(childrel, NoLock);
12569 }
12570
12571 /* Queue validation for phase 3 */
12572 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12573 newcon->name = constrName;
12574 newcon->contype = CONSTR_CHECK;
12575 newcon->refrelid = InvalidOid;
12576 newcon->refindid = InvalidOid;
12577 newcon->conid = con->oid;
12578
12579 val = SysCacheGetAttrNotNull(CONSTROID, contuple,
12580 Anum_pg_constraint_conbin);
12581 conbin = TextDatumGetCString(val);
12582 newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
12583
12584 /* Find or create work queue entry for this table */
12585 tab = ATGetQueueEntry(wqueue, rel);
12586 tab->constraints = lappend(tab->constraints, newcon);
12587
12588 /*
12589 * Invalidate relcache so that others see the new validated constraint.
12590 */
12592
12593 /*
12594 * Now update the catalog, while we have the door open.
12595 */
12596 copyTuple = heap_copytuple(contuple);
12597 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12598 copy_con->convalidated = true;
12599 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12600
12601 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12602
12603 heap_freetuple(copyTuple);
12604}
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 12411 of file tablecmds.c.

12413{
12415 AlteredTableInfo *tab;
12416 HeapTuple copyTuple;
12417 Form_pg_constraint copy_con;
12418
12419 con = (Form_pg_constraint) GETSTRUCT(contuple);
12420 Assert(con->contype == CONSTRAINT_FOREIGN);
12421 Assert(!con->convalidated);
12422
12423 if (rel->rd_rel->relkind == RELKIND_RELATION)
12424 {
12425 NewConstraint *newcon;
12426 Constraint *fkconstraint;
12427
12428 /* Queue validation for phase 3 */
12429 fkconstraint = makeNode(Constraint);
12430 /* for now this is all we need */
12431 fkconstraint->conname = pstrdup(NameStr(con->conname));
12432
12433 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12434 newcon->name = fkconstraint->conname;
12435 newcon->contype = CONSTR_FOREIGN;
12436 newcon->refrelid = con->confrelid;
12437 newcon->refindid = con->conindid;
12438 newcon->conid = con->oid;
12439 newcon->qual = (Node *) fkconstraint;
12440
12441 /* Find or create work queue entry for this table */
12442 tab = ATGetQueueEntry(wqueue, rel);
12443 tab->constraints = lappend(tab->constraints, newcon);
12444 }
12445
12446 /*
12447 * If the table at either end of the constraint is partitioned, we need to
12448 * recurse and handle every constraint that is a child of this constraint.
12449 */
12450 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12451 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
12452 {
12453 ScanKeyData pkey;
12454 SysScanDesc pscan;
12455 HeapTuple childtup;
12456
12457 ScanKeyInit(&pkey,
12458 Anum_pg_constraint_conparentid,
12459 BTEqualStrategyNumber, F_OIDEQ,
12460 ObjectIdGetDatum(con->oid));
12461
12462 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12463 true, NULL, 1, &pkey);
12464
12465 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12466 {
12467 Form_pg_constraint childcon;
12468 Relation childrel;
12469
12470 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12471
12472 /*
12473 * If the child constraint has already been validated, no further
12474 * action is required for it or its descendants, as they are all
12475 * valid.
12476 */
12477 if (childcon->convalidated)
12478 continue;
12479
12480 childrel = table_open(childcon->conrelid, lockmode);
12481
12482 QueueFKConstraintValidation(wqueue, conrel, childrel, childtup,
12483 lockmode);
12484 table_close(childrel, NoLock);
12485 }
12486
12487 systable_endscan(pscan);
12488 }
12489
12490 /*
12491 * Now update the catalog, while we have the door open.
12492 */
12493 copyTuple = heap_copytuple(contuple);
12494 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12495 copy_con->convalidated = true;
12496 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12497
12498 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12499
12500 heap_freetuple(copyTuple);
12501}

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().

◆ QueuePartitionConstraintValidation()

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

Definition at line 19432 of file tablecmds.c.

19435{
19436 /*
19437 * Based on the table's existing constraints, determine whether or not we
19438 * may skip scanning the table.
19439 */
19440 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
19441 {
19442 if (!validate_default)
19444 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19445 RelationGetRelationName(scanrel))));
19446 else
19448 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19449 RelationGetRelationName(scanrel))));
19450 return;
19451 }
19452
19453 /*
19454 * Constraints proved insufficient. For plain relations, queue a
19455 * validation item now; for partitioned tables, recurse to process each
19456 * partition.
19457 */
19458 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
19459 {
19460 AlteredTableInfo *tab;
19461
19462 /* Grab a work queue entry. */
19463 tab = ATGetQueueEntry(wqueue, scanrel);
19464 Assert(tab->partition_constraint == NULL);
19465 tab->partition_constraint = (Expr *) linitial(partConstraint);
19466 tab->validate_default = validate_default;
19467 }
19468 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19469 {
19470 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
19471 int i;
19472
19473 for (i = 0; i < partdesc->nparts; i++)
19474 {
19475 Relation part_rel;
19476 List *thisPartConstraint;
19477
19478 /*
19479 * This is the minimum lock we need to prevent deadlocks.
19480 */
19481 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
19482
19483 /*
19484 * Adjust the constraint for scanrel so that it matches this
19485 * partition's attribute numbers.
19486 */
19487 thisPartConstraint =
19488 map_partition_varattnos(partConstraint, 1,
19489 part_rel, scanrel);
19490
19491 QueuePartitionConstraintValidation(wqueue, part_rel,
19492 thisPartConstraint,
19493 validate_default);
19494 table_close(part_rel, NoLock); /* keep lock till commit */
19495 }
19496 }
19497}

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

18855{
18856 Node *stmt = (Node *) arg;
18857 ObjectType reltype;
18858 HeapTuple tuple;
18859 Form_pg_class classform;
18860 AclResult aclresult;
18861 char relkind;
18862
18863 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
18864 if (!HeapTupleIsValid(tuple))
18865 return; /* concurrently dropped */
18866 classform = (Form_pg_class) GETSTRUCT(tuple);
18867 relkind = classform->relkind;
18868
18869 /* Must own relation. */
18870 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
18872
18873 /* No system table modifications unless explicitly allowed. */
18874 if (!allowSystemTableMods && IsSystemClass(relid, classform))
18875 ereport(ERROR,
18876 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18877 errmsg("permission denied: \"%s\" is a system catalog",
18878 rv->relname)));
18879
18880 /*
18881 * Extract the specified relation type from the statement parse tree.
18882 *
18883 * Also, for ALTER .. RENAME, check permissions: the user must (still)
18884 * have CREATE rights on the containing namespace.
18885 */
18886 if (IsA(stmt, RenameStmt))
18887 {
18888 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
18890 if (aclresult != ACLCHECK_OK)
18891 aclcheck_error(aclresult, OBJECT_SCHEMA,
18892 get_namespace_name(classform->relnamespace));
18893 reltype = ((RenameStmt *) stmt)->renameType;
18894 }
18895 else if (IsA(stmt, AlterObjectSchemaStmt))
18896 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
18897
18898 else if (IsA(stmt, AlterTableStmt))
18899 reltype = ((AlterTableStmt *) stmt)->objtype;
18900 else
18901 {
18902 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
18903 reltype = OBJECT_TABLE; /* placate compiler */
18904 }
18905
18906 /*
18907 * For compatibility with prior releases, we allow ALTER TABLE to be used
18908 * with most other types of relations (but not composite types). We allow
18909 * similar flexibility for ALTER INDEX in the case of RENAME, but not
18910 * otherwise. Otherwise, the user must select the correct form of the
18911 * command for the relation at issue.
18912 */
18913 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
18914 ereport(ERROR,
18915 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18916 errmsg("\"%s\" is not a sequence", rv->relname)));
18917
18918 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
18919 ereport(ERROR,
18920 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18921 errmsg("\"%s\" is not a view", rv->relname)));
18922
18923 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
18924 ereport(ERROR,
18925 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18926 errmsg("\"%s\" is not a materialized view", rv->relname)));
18927
18928 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
18929 ereport(ERROR,
18930 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18931 errmsg("\"%s\" is not a foreign table", rv->relname)));
18932
18933 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
18934 ereport(ERROR,
18935 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18936 errmsg("\"%s\" is not a composite type", rv->relname)));
18937
18938 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
18939 relkind != RELKIND_PARTITIONED_INDEX
18940 && !IsA(stmt, RenameStmt))
18941 ereport(ERROR,
18942 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18943 errmsg("\"%s\" is not an index", rv->relname)));
18944
18945 /*
18946 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18947 * TYPE for that.
18948 */
18949 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
18950 ereport(ERROR,
18951 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18952 errmsg("\"%s\" is a composite type", rv->relname),
18953 /* translator: %s is an SQL ALTER command */
18954 errhint("Use %s instead.",
18955 "ALTER TYPE")));
18956
18957 /*
18958 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18959 * to a different schema, such as indexes and TOAST tables.
18960 */
18962 {
18963 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
18964 ereport(ERROR,
18965 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18966 errmsg("cannot change schema of index \"%s\"",
18967 rv->relname),
18968 errhint("Change the schema of the table instead.")));
18969 else if (relkind == RELKIND_COMPOSITE_TYPE)
18970 ereport(ERROR,
18971 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18972 errmsg("cannot change schema of composite type \"%s\"",
18973 rv->relname),
18974 /* translator: %s is an SQL ALTER command */
18975 errhint("Use %s instead.",
18976 "ALTER TYPE")));
18977 else if (relkind == RELKIND_TOASTVALUE)
18978 ereport(ERROR,
18979 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18980 errmsg("cannot change schema of TOAST table \"%s\"",
18981 rv->relname),
18982 errhint("Change the schema of the table instead.")));
18983 }
18984
18985 ReleaseSysCache(tuple);
18986}
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:85
ObjectType
Definition: parsenodes.h:2311
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2330
@ OBJECT_VIEW
Definition: parsenodes.h:2363
@ OBJECT_TYPE
Definition: parsenodes.h:2361

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

20817{
20819 Form_pg_class classform;
20820 HeapTuple tuple;
20821
20822 state = (struct AttachIndexCallbackState *) arg;
20823
20824 if (!state->lockedParentTbl)
20825 {
20826 LockRelationOid(state->parentTblOid, AccessShareLock);
20827 state->lockedParentTbl = true;
20828 }
20829
20830 /*
20831 * If we previously locked some other heap, and the name we're looking up
20832 * no longer refers to an index on that relation, release the now-useless
20833 * lock. XXX maybe we should do *after* we verify whether the index does
20834 * not actually belong to the same relation ...
20835 */
20836 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20837 {
20838 UnlockRelationOid(state->partitionOid, AccessShareLock);
20839 state->partitionOid = InvalidOid;
20840 }
20841
20842 /* Didn't find a relation, so no need for locking or permission checks. */
20843 if (!OidIsValid(relOid))
20844 return;
20845
20846 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20847 if (!HeapTupleIsValid(tuple))
20848 return; /* concurrently dropped, so nothing to do */
20849 classform = (Form_pg_class) GETSTRUCT(tuple);
20850 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20851 classform->relkind != RELKIND_INDEX)
20852 ereport(ERROR,
20853 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20854 errmsg("\"%s\" is not an index", rv->relname)));
20855 ReleaseSysCache(tuple);
20856
20857 /*
20858 * Since we need only examine the heap's tupledesc, an access share lock
20859 * on it (preventing any DDL) is sufficient.
20860 */
20861 state->partitionOid = IndexGetRelation(relOid, false);
20862 LockRelationOid(state->partitionOid, AccessShareLock);
20863}
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 1663 of file tablecmds.c.

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

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

3952{
3953 HeapTuple tuple;
3954 Form_pg_class form;
3955
3956 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3957 if (!HeapTupleIsValid(tuple))
3958 return; /* concurrently dropped */
3959 form = (Form_pg_class) GETSTRUCT(tuple);
3960 renameatt_check(relid, form, false);
3961 ReleaseSysCache(tuple);
3962}
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition: tablecmds.c:3756

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

18799{
18800 HeapTuple tuple;
18801
18802 /* Nothing to do if the relation was not found. */
18803 if (!OidIsValid(relId))
18804 return;
18805
18806 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18807 if (!HeapTupleIsValid(tuple)) /* should not happen */
18808 elog(ERROR, "cache lookup failed for relation %u", relId);
18809
18812
18813 ReleaseSysCache(tuple);
18814}

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

18763{
18764 char relkind;
18765 AclResult aclresult;
18766
18767 /* Nothing to do if the relation was not found. */
18768 if (!OidIsValid(relId))
18769 return;
18770
18771 /*
18772 * If the relation does exist, check whether it's an index. But note that
18773 * the relation might have been dropped between the time we did the name
18774 * lookup and now. In that case, there's nothing to do.
18775 */
18776 relkind = get_rel_relkind(relId);
18777 if (!relkind)
18778 return;
18779 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18780 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18781 ereport(ERROR,
18782 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18783 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18784
18785 /* Check permissions */
18786 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18787 if (aclresult != ACLCHECK_OK)
18788 aclcheck_error(aclresult,
18790 relation->relname);
18791}
#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 18821 of file tablecmds.c.

18823{
18824 HeapTuple tuple;
18825
18826 /* Nothing to do if the relation was not found. */
18827 if (!OidIsValid(relId))
18828 return;
18829
18830 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18831 if (!HeapTupleIsValid(tuple)) /* should not happen */
18832 elog(ERROR, "cache lookup failed for relation %u", relId);
18833
18834 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18836 relation->relname);
18837
18838 if (!allowSystemTableMods &&
18839 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18840 ereport(ERROR,
18841 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18842 errmsg("permission denied: \"%s\" is a system catalog",
18843 relation->relname)));
18844
18845 ReleaseSysCache(tuple);
18846}

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

15112{
15113 CommentStmt *cmd;
15114 char *comment_str;
15115 AlterTableCmd *newcmd;
15116
15117 /* Look for comment for object wanted, and leave if none */
15118 comment_str = GetComment(objid, ConstraintRelationId, 0);
15119 if (comment_str == NULL)
15120 return;
15121
15122 /* Build CommentStmt node, copying all input data for safety */
15123 cmd = makeNode(CommentStmt);
15124 if (rel)
15125 {
15127 cmd->object = (Node *)
15130 makeString(pstrdup(conname)));
15131 }
15132 else
15133 {
15135 cmd->object = (Node *)
15137 makeString(pstrdup(conname)));
15138 }
15139 cmd->comment = comment_str;
15140
15141 /* Append it to list of commands */
15142 newcmd = makeNode(AlterTableCmd);
15143 newcmd->subtype = AT_ReAddComment;
15144 newcmd->def = (Node *) cmd;
15145 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15146}
TypeName * makeTypeNameFromNameList(List *names)
Definition: makefuncs.c:531
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2352
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2325
#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:3344
ObjectType objtype
Definition: parsenodes.h:3342
Node * object
Definition: parsenodes.h:3343

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

21032{
21033 Oid existingIdx;
21034
21035 existingIdx = index_get_partition(partitionTbl,
21036 RelationGetRelid(parentIdx));
21037 if (OidIsValid(existingIdx))
21038 ereport(ERROR,
21039 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21040 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21041 RelationGetRelationName(partIdx),
21042 RelationGetRelationName(parentIdx)),
21043 errdetail("Another index is already attached for partition \"%s\".",
21044 RelationGetRelationName(partitionTbl))));
21045}

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

18529{
18530 OnCommitItem *oc;
18531 MemoryContext oldcxt;
18532
18533 /*
18534 * We needn't bother registering the relation unless there is an ON COMMIT
18535 * action we need to take.
18536 */
18538 return;
18539
18541
18542 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18543 oc->relid = relid;
18544 oc->oncommit = action;
18547
18548 /*
18549 * We use lcons() here so that ON COMMIT actions are processed in reverse
18550 * order of registration. That might not be essential but it seems
18551 * reasonable.
18552 */
18554
18555 MemoryContextSwitchTo(oldcxt);
18556}
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 17669 of file tablecmds.c.

17671{
17672 Relation pg_index;
17673 Relation pg_class;
17674 HeapTuple pg_class_tuple;
17675 HeapTuple pg_index_tuple;
17676 Form_pg_class pg_class_form;
17677 Form_pg_index pg_index_form;
17678 ListCell *index;
17679
17680 /*
17681 * Check whether relreplident has changed, and update it if so.
17682 */
17683 pg_class = table_open(RelationRelationId, RowExclusiveLock);
17684 pg_class_tuple = SearchSysCacheCopy1(RELOID,
17686 if (!HeapTupleIsValid(pg_class_tuple))
17687 elog(ERROR, "cache lookup failed for relation \"%s\"",
17689 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17690 if (pg_class_form->relreplident != ri_type)
17691 {
17692 pg_class_form->relreplident = ri_type;
17693 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17694 }
17695 table_close(pg_class, RowExclusiveLock);
17696 heap_freetuple(pg_class_tuple);
17697
17698 /*
17699 * Update the per-index indisreplident flags correctly.
17700 */
17701 pg_index = table_open(IndexRelationId, RowExclusiveLock);
17702 foreach(index, RelationGetIndexList(rel))
17703 {
17704 Oid thisIndexOid = lfirst_oid(index);
17705 bool dirty = false;
17706
17707 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17708 ObjectIdGetDatum(thisIndexOid));
17709 if (!HeapTupleIsValid(pg_index_tuple))
17710 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17711 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17712
17713 if (thisIndexOid == indexOid)
17714 {
17715 /* Set the bit if not already set. */
17716 if (!pg_index_form->indisreplident)
17717 {
17718 dirty = true;
17719 pg_index_form->indisreplident = true;
17720 }
17721 }
17722 else
17723 {
17724 /* Unset the bit if set. */
17725 if (pg_index_form->indisreplident)
17726 {
17727 dirty = true;
17728 pg_index_form->indisreplident = false;
17729 }
17730 }
17731
17732 if (dirty)
17733 {
17734 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17735 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17736 InvalidOid, is_internal);
17737
17738 /*
17739 * Invalidate the relcache for the table, so that after we commit
17740 * all sessions will refresh the table's replica identity index
17741 * before attempting any UPDATE or DELETE on the table. (If we
17742 * changed the table's pg_class row above, then a relcache inval
17743 * is already queued due to that; but we might not have.)
17744 */
17746 }
17747 heap_freetuple(pg_index_tuple);
17748 }
17749
17750 table_close(pg_index, RowExclusiveLock);
17751}
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 14333 of file tablecmds.c.

14335{
14336 Relation depRel;
14337 ScanKeyData key[3];
14338 SysScanDesc scan;
14339 HeapTuple depTup;
14340
14341 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
14342
14343 depRel = table_open(DependRelationId, RowExclusiveLock);
14344
14345 ScanKeyInit(&key[0],
14346 Anum_pg_depend_refclassid,
14347 BTEqualStrategyNumber, F_OIDEQ,
14348 ObjectIdGetDatum(RelationRelationId));
14349 ScanKeyInit(&key[1],
14350 Anum_pg_depend_refobjid,
14351 BTEqualStrategyNumber, F_OIDEQ,
14353 ScanKeyInit(&key[2],
14354 Anum_pg_depend_refobjsubid,
14355 BTEqualStrategyNumber, F_INT4EQ,
14357
14358 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14359 NULL, 3, key);
14360
14361 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14362 {
14363 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14364 ObjectAddress foundObject;
14365
14366 foundObject.classId = foundDep->classid;
14367 foundObject.objectId = foundDep->objid;
14368 foundObject.objectSubId = foundDep->objsubid;
14369
14370 switch (foundObject.classId)
14371 {
14372 case RelationRelationId:
14373 {
14374 char relKind = get_rel_relkind(foundObject.objectId);
14375
14376 if (relKind == RELKIND_INDEX ||
14377 relKind == RELKIND_PARTITIONED_INDEX)
14378 {
14379 Assert(foundObject.objectSubId == 0);
14380 RememberIndexForRebuilding(foundObject.objectId, tab);
14381 }
14382 else if (relKind == RELKIND_SEQUENCE)
14383 {
14384 /*
14385 * This must be a SERIAL column's sequence. We need
14386 * not do anything to it.
14387 */
14388 Assert(foundObject.objectSubId == 0);
14389 }
14390 else
14391 {
14392 /* Not expecting any other direct dependencies... */
14393 elog(ERROR, "unexpected object depending on column: %s",
14394 getObjectDescription(&foundObject, false));
14395 }
14396 break;
14397 }
14398
14399 case ConstraintRelationId:
14400 Assert(foundObject.objectSubId == 0);
14401 RememberConstraintForRebuilding(foundObject.objectId, tab);
14402 break;
14403
14404 case ProcedureRelationId:
14405
14406 /*
14407 * A new-style SQL function can depend on a column, if that
14408 * column is referenced in the parsed function body. Ideally
14409 * we'd automatically update the function by deparsing and
14410 * reparsing it, but that's risky and might well fail anyhow.
14411 * FIXME someday.
14412 *
14413 * This is only a problem for AT_AlterColumnType, not
14414 * AT_SetExpression.
14415 */
14416 if (subtype == AT_AlterColumnType)
14417 ereport(ERROR,
14418 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14419 errmsg("cannot alter type of a column used by a function or procedure"),
14420 errdetail("%s depends on column \"%s\"",
14421 getObjectDescription(&foundObject, false),
14422 colName)));
14423 break;
14424
14425 case RewriteRelationId:
14426
14427 /*
14428 * View/rule bodies have pretty much the same issues as
14429 * function bodies. FIXME someday.
14430 */
14431 if (subtype == AT_AlterColumnType)
14432 ereport(ERROR,
14433 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14434 errmsg("cannot alter type of a column used by a view or rule"),
14435 errdetail("%s depends on column \"%s\"",
14436 getObjectDescription(&foundObject, false),
14437 colName)));
14438 break;
14439
14440 case TriggerRelationId:
14441
14442 /*
14443 * A trigger can depend on a column because the column is
14444 * specified as an update target, or because the column is
14445 * used in the trigger's WHEN condition. The first case would
14446 * not require any extra work, but the second case would
14447 * require updating the WHEN expression, which has the same
14448 * issues as above. Since we can't easily tell which case
14449 * applies, we punt for both. FIXME someday.
14450 */
14451 if (subtype == AT_AlterColumnType)
14452 ereport(ERROR,
14453 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14454 errmsg("cannot alter type of a column used in a trigger definition"),
14455 errdetail("%s depends on column \"%s\"",
14456 getObjectDescription(&foundObject, false),
14457 colName)));
14458 break;
14459
14460 case PolicyRelationId:
14461
14462 /*
14463 * A policy can depend on a column because the column is
14464 * specified in the policy's USING or WITH CHECK qual
14465 * expressions. It might be possible to rewrite and recheck
14466 * the policy expression, but punt for now. It's certainly
14467 * easy enough to remove and recreate the policy; still, FIXME
14468 * someday.
14469 */
14470 if (subtype == AT_AlterColumnType)
14471 ereport(ERROR,
14472 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14473 errmsg("cannot alter type of a column used in a policy definition"),
14474 errdetail("%s depends on column \"%s\"",
14475 getObjectDescription(&foundObject, false),
14476 colName)));
14477 break;
14478
14479 case AttrDefaultRelationId:
14480 {
14482
14483 if (col.objectId == RelationGetRelid(rel) &&
14484 col.objectSubId == attnum)
14485 {
14486 /*
14487 * Ignore the column's own default expression. The
14488 * caller deals with it.
14489 */
14490 }
14491 else
14492 {
14493 /*
14494 * This must be a reference from the expression of a
14495 * generated column elsewhere in the same table.
14496 * Changing the type/generated expression of a column
14497 * that is used by a generated column is not allowed
14498 * by SQL standard, so just punt for now. It might be
14499 * doable with some thinking and effort.
14500 */
14501 if (subtype == AT_AlterColumnType)
14502 ereport(ERROR,
14503 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14504 errmsg("cannot alter type of a column used by a generated column"),
14505 errdetail("Column \"%s\" is used by generated column \"%s\".",
14506 colName,
14508 col.objectSubId,
14509 false))));
14510 }
14511 break;
14512 }
14513
14514 case StatisticExtRelationId:
14515
14516 /*
14517 * Give the extended-stats machinery a chance to fix anything
14518 * that this column type change would break.
14519 */
14520 RememberStatisticsForRebuilding(foundObject.objectId, tab);
14521 break;
14522
14523 case PublicationRelRelationId:
14524
14525 /*
14526 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
14527 * clause. Same issues as above. FIXME someday.
14528 */
14529 if (subtype == AT_AlterColumnType)
14530 ereport(ERROR,
14531 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14532 errmsg("cannot alter type of a column used by a publication WHERE clause"),
14533 errdetail("%s depends on column \"%s\"",
14534 getObjectDescription(&foundObject, false),
14535 colName)));
14536 break;
14537
14538 default:
14539
14540 /*
14541 * We don't expect any other sorts of objects to depend on a
14542 * column.
14543 */
14544 elog(ERROR, "unexpected object depending on column: %s",
14545 getObjectDescription(&foundObject, false));
14546 break;
14547 }
14548 }
14549
14550 systable_endscan(scan);
14551 table_close(depRel, NoLock);
14552}
ObjectAddress GetAttrDefaultColumnAddress(Oid attrdefoid)
Definition: pg_attrdef.c:320
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:14646
static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
Definition: tablecmds.c:14697
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
Definition: tablecmds.c:14590

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

14575{
14576 if (!get_index_isclustered(indoid))
14577 return;
14578
14579 if (tab->clusterOnIndex)
14580 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14581
14582 tab->clusterOnIndex = get_rel_name(indoid);
14583}
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3684

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

14591{
14592 /*
14593 * This de-duplication check is critical for two independent reasons: we
14594 * mustn't try to recreate the same constraint twice, and if a constraint
14595 * depends on more than one column whose type is to be altered, we must
14596 * capture its definition string before applying any of the column type
14597 * changes. ruleutils.c will get confused if we ask again later.
14598 */
14599 if (!list_member_oid(tab->changedConstraintOids, conoid))
14600 {
14601 /* OK, capture the constraint's existing definition string */
14602 char *defstring = pg_get_constraintdef_command(conoid);
14603 Oid indoid;
14604
14605 /*
14606 * It is critical to create not-null constraints ahead of primary key
14607 * indexes; otherwise, the not-null constraint would be created by the
14608 * primary key, and the constraint name would be wrong.
14609 */
14610 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
14611 {
14612 tab->changedConstraintOids = lcons_oid(conoid,
14614 tab->changedConstraintDefs = lcons(defstring,
14616 }
14617 else
14618 {
14619
14621 conoid);
14623 defstring);
14624 }
14625
14626 /*
14627 * For the index of a constraint, if any, remember if it is used for
14628 * the table's replica identity or if it is a clustered index, so that
14629 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14630 * those properties.
14631 */
14632 indoid = get_constraint_index(conoid);
14633 if (OidIsValid(indoid))
14634 {
14636 RememberClusterOnForRebuilding(indoid, tab);
14637 }
14638 }
14639}
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:14559
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:14574

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

14647{
14648 /*
14649 * This de-duplication check is critical for two independent reasons: we
14650 * mustn't try to recreate the same index twice, and if an index depends
14651 * on more than one column whose type is to be altered, we must capture
14652 * its definition string before applying any of the column type changes.
14653 * ruleutils.c will get confused if we ask again later.
14654 */
14655 if (!list_member_oid(tab->changedIndexOids, indoid))
14656 {
14657 /*
14658 * Before adding it as an index-to-rebuild, we'd better see if it
14659 * belongs to a constraint, and if so rebuild the constraint instead.
14660 * Typically this check fails, because constraint indexes normally
14661 * have only dependencies on their constraint. But it's possible for
14662 * such an index to also have direct dependencies on table columns,
14663 * for example with a partial exclusion constraint.
14664 */
14665 Oid conoid = get_index_constraint(indoid);
14666
14667 if (OidIsValid(conoid))
14668 {
14670 }
14671 else
14672 {
14673 /* OK, capture the index's existing definition string */
14674 char *defstring = pg_get_indexdef_string(indoid);
14675
14677 indoid);
14679 defstring);
14680
14681 /*
14682 * Remember if this index is used for the table's replica identity
14683 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14684 * can queue up commands necessary to restore those properties.
14685 */
14687 RememberClusterOnForRebuilding(indoid, tab);
14688 }
14689 }
14690}
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 14559 of file tablecmds.c.

14560{
14561 if (!get_index_isreplident(indoid))
14562 return;
14563
14564 if (tab->replicaIdentityIndex)
14565 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14566
14567 tab->replicaIdentityIndex = get_rel_name(indoid);
14568}
bool get_index_isreplident(Oid index_oid)
Definition: lsyscache.c:3638

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

14698{
14699 /*
14700 * This de-duplication check is critical for two independent reasons: we
14701 * mustn't try to recreate the same statistics object twice, and if the
14702 * statistics object depends on more than one column whose type is to be
14703 * altered, we must capture its definition string before applying any of
14704 * the type changes. ruleutils.c will get confused if we ask again later.
14705 */
14706 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14707 {
14708 /* OK, capture the statistics object's existing definition string */
14709 char *defstring = pg_get_statisticsobjdef_string(stxoid);
14710
14712 stxoid);
14714 defstring);
14715 }
14716}
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 18564 of file tablecmds.c.

18565{
18566 ListCell *l;
18567
18568 foreach(l, on_commits)
18569 {
18570 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18571
18572 if (oc->relid == relid)
18573 {
18575 break;
18576 }
18577 }
18578}

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

17218{
17219 Relation catalogRelation;
17220 SysScanDesc scan;
17221 ScanKeyData key[3];
17222 HeapTuple attributeTuple,
17223 constraintTuple;
17224 AttrMap *attmap;
17225 List *connames;
17226 List *nncolumns;
17227 bool found;
17228 bool is_partitioning;
17229
17230 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17231
17232 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17233 RelationGetRelid(parent_rel),
17234 expect_detached,
17235 RelationGetRelationName(child_rel));
17236 if (!found)
17237 {
17238 if (is_partitioning)
17239 ereport(ERROR,
17241 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17242 RelationGetRelationName(child_rel),
17243 RelationGetRelationName(parent_rel))));
17244 else
17245 ereport(ERROR,
17247 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17248 RelationGetRelationName(parent_rel),
17249 RelationGetRelationName(child_rel))));
17250 }
17251
17252 /*
17253 * Search through child columns looking for ones matching parent rel
17254 */
17255 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17256 ScanKeyInit(&key[0],
17257 Anum_pg_attribute_attrelid,
17258 BTEqualStrategyNumber, F_OIDEQ,
17260 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17261 true, NULL, 1, key);
17262 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17263 {
17264 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17265
17266 /* Ignore if dropped or not inherited */
17267 if (att->attisdropped)
17268 continue;
17269 if (att->attinhcount <= 0)
17270 continue;
17271
17273 NameStr(att->attname)))
17274 {
17275 /* Decrement inhcount and possibly set islocal to true */
17276 HeapTuple copyTuple = heap_copytuple(attributeTuple);
17277 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
17278
17279 copy_att->attinhcount--;
17280 if (copy_att->attinhcount == 0)
17281 copy_att->attislocal = true;
17282
17283 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
17284 heap_freetuple(copyTuple);
17285 }
17286 }
17287 systable_endscan(scan);
17288 table_close(catalogRelation, RowExclusiveLock);
17289
17290 /*
17291 * Likewise, find inherited check and not-null constraints and disinherit
17292 * them. To do this, we first need a list of the names of the parent's
17293 * check constraints. (We cheat a bit by only checking for name matches,
17294 * assuming that the expressions will match.)
17295 *
17296 * For NOT NULL columns, we store column numbers to match, mapping them in
17297 * to the child rel's attribute numbers.
17298 */
17299 attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
17300 RelationGetDescr(parent_rel),
17301 false);
17302
17303 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
17304 ScanKeyInit(&key[0],
17305 Anum_pg_constraint_conrelid,
17306 BTEqualStrategyNumber, F_OIDEQ,
17307 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17308 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17309 true, NULL, 1, key);
17310
17311 connames = NIL;
17312 nncolumns = NIL;
17313
17314 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17315 {
17316 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17317
17318 if (con->connoinherit)
17319 continue;
17320
17321 if (con->contype == CONSTRAINT_CHECK)
17322 connames = lappend(connames, pstrdup(NameStr(con->conname)));
17323 if (con->contype == CONSTRAINT_NOTNULL)
17324 {
17325 AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
17326
17327 nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
17328 }
17329 }
17330
17331 systable_endscan(scan);
17332
17333 /* Now scan the child's constraints to find matches */
17334 ScanKeyInit(&key[0],
17335 Anum_pg_constraint_conrelid,
17336 BTEqualStrategyNumber, F_OIDEQ,
17338 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17339 true, NULL, 1, key);
17340
17341 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17342 {
17343 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17344 bool match = false;
17345
17346 /*
17347 * Match CHECK constraints by name, not-null constraints by column
17348 * number, and ignore all others.
17349 */
17350 if (con->contype == CONSTRAINT_CHECK)
17351 {
17352 foreach_ptr(char, chkname, connames)
17353 {
17354 if (con->contype == CONSTRAINT_CHECK &&
17355 strcmp(NameStr(con->conname), chkname) == 0)
17356 {
17357 match = true;
17358 connames = foreach_delete_current(connames, chkname);
17359 break;
17360 }
17361 }
17362 }
17363 else if (con->contype == CONSTRAINT_NOTNULL)
17364 {
17365 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
17366
17367 foreach_int(prevattno, nncolumns)
17368 {
17369 if (prevattno == child_attno)
17370 {
17371 match = true;
17372 nncolumns = foreach_delete_current(nncolumns, prevattno);
17373 break;
17374 }
17375 }
17376 }
17377 else
17378 continue;
17379
17380 if (match)
17381 {
17382 /* Decrement inhcount and possibly set islocal to true */
17383 HeapTuple copyTuple = heap_copytuple(constraintTuple);
17384 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
17385
17386 if (copy_con->coninhcount <= 0) /* shouldn't happen */
17387 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
17388 RelationGetRelid(child_rel), NameStr(copy_con->conname));
17389
17390 copy_con->coninhcount--;
17391 if (copy_con->coninhcount == 0)
17392 copy_con->conislocal = true;
17393
17394 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
17395 heap_freetuple(copyTuple);
17396 }
17397 }
17398
17399 /* We should have matched all constraints */
17400 if (connames != NIL || nncolumns != NIL)
17401 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
17402 list_length(connames) + list_length(nncolumns),
17403 RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
17404
17405 systable_endscan(scan);
17406 table_close(catalogRelation, RowExclusiveLock);
17407
17409 RelationRelationId,
17410 RelationGetRelid(parent_rel),
17411 child_dependency_type(is_partitioning));
17412
17413 /*
17414 * Post alter hook of this inherits. Since object_access_hook doesn't take
17415 * multiple object identifiers, we relay oid of parent relation using
17416 * auxiliary_id argument.
17417 */
17418 InvokeObjectPostAlterHookArg(InheritsRelationId,
17419 RelationGetRelid(child_rel), 0,
17420 RelationGetRelid(parent_rel), false);
17421}
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 11660 of file tablecmds.c.

11662{
11663 ObjectAddresses *objs;
11664 HeapTuple consttup;
11666 SysScanDesc scan;
11667 HeapTuple trigtup;
11668
11670 Anum_pg_constraint_conrelid,
11671 BTEqualStrategyNumber, F_OIDEQ,
11672 ObjectIdGetDatum(conrelid));
11673
11674 scan = systable_beginscan(conrel,
11675 ConstraintRelidTypidNameIndexId,
11676 true, NULL, 1, &key);
11677 objs = new_object_addresses();
11678 while ((consttup = systable_getnext(scan)) != NULL)
11679 {
11680 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11681
11682 if (conform->conparentid != conoid)
11683 continue;
11684 else
11685 {
11686 ObjectAddress addr;
11687 SysScanDesc scan2;
11688 ScanKeyData key2;
11690
11691 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11692 add_exact_object_address(&addr, objs);
11693
11694 /*
11695 * First we must delete the dependency record that binds the
11696 * constraint records together.
11697 */
11698 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11699 conform->oid,
11701 ConstraintRelationId,
11702 conoid);
11703 Assert(n == 1); /* actually only one is expected */
11704
11705 /*
11706 * Now search for the triggers for this constraint and set them up
11707 * for deletion too
11708 */
11709 ScanKeyInit(&key2,
11710 Anum_pg_trigger_tgconstraint,
11711 BTEqualStrategyNumber, F_OIDEQ,
11712 ObjectIdGetDatum(conform->oid));
11713 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11714 true, NULL, 1, &key2);
11715 while ((trigtup = systable_getnext(scan2)) != NULL)
11716 {
11717 ObjectAddressSet(addr, TriggerRelationId,
11718 ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11719 add_exact_object_address(&addr, objs);
11720 }
11721 systable_endscan(scan2);
11722 }
11723 }
11724 /* make the dependency deletions visible */
11728 systable_endscan(scan);
11729}
#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 1499 of file tablecmds.c.

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

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

4015{
4016 Relation targetrelation = NULL;
4017 Oid constraintOid;
4018 HeapTuple tuple;
4020 ObjectAddress address;
4021
4022 Assert(!myrelid || !mytypid);
4023
4024 if (mytypid)
4025 {
4026 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4027 }
4028 else
4029 {
4030 targetrelation = relation_open(myrelid, AccessExclusiveLock);
4031
4032 /*
4033 * don't tell it whether we're recursing; we allow changing typed
4034 * tables here
4035 */
4036 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4037
4038 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4039 }
4040
4041 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4042 if (!HeapTupleIsValid(tuple))
4043 elog(ERROR, "cache lookup failed for constraint %u",
4044 constraintOid);
4045 con = (Form_pg_constraint) GETSTRUCT(tuple);
4046
4047 if (myrelid &&
4048 (con->contype == CONSTRAINT_CHECK ||
4049 con->contype == CONSTRAINT_NOTNULL) &&
4050 !con->connoinherit)
4051 {
4052 if (recurse)
4053 {
4054 List *child_oids,
4055 *child_numparents;
4056 ListCell *lo,
4057 *li;
4058
4059 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4060 &child_numparents);
4061
4062 forboth(lo, child_oids, li, child_numparents)
4063 {
4064 Oid childrelid = lfirst_oid(lo);
4065 int numparents = lfirst_int(li);
4066
4067 if (childrelid == myrelid)
4068 continue;
4069
4070 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4071 }
4072 }
4073 else
4074 {
4075 if (expected_parents == 0 &&
4077 ereport(ERROR,
4078 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4079 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4080 oldconname)));
4081 }
4082
4083 if (con->coninhcount > expected_parents)
4084 ereport(ERROR,
4085 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4086 errmsg("cannot rename inherited constraint \"%s\"",
4087 oldconname)));
4088 }
4089
4090 if (con->conindid
4091 && (con->contype == CONSTRAINT_PRIMARY
4092 || con->contype == CONSTRAINT_UNIQUE
4093 || con->contype == CONSTRAINT_EXCLUSION))
4094 /* rename the index; this renames the constraint as well */
4095 RenameRelationInternal(con->conindid, newconname, false, true);
4096 else
4097 RenameConstraintById(constraintOid, newconname);
4098
4099 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4100
4101 ReleaseSysCache(tuple);
4102
4103 if (targetrelation)
4104 {
4105 /*
4106 * Invalidate relcache so as others can see the new constraint name.
4107 */
4108 CacheInvalidateRelcache(targetrelation);
4109
4110 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4111 }
4112
4113 return address;
4114}
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:4008

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

3971{
3972 Oid relid;
3974 ObjectAddress address;
3975
3976 /* lock level taken here should match renameatt_internal */
3978 stmt->missing_ok ? RVR_MISSING_OK : 0,
3980 NULL);
3981
3982 if (!OidIsValid(relid))
3983 {
3985 (errmsg("relation \"%s\" does not exist, skipping",
3986 stmt->relation->relname)));
3987 return InvalidObjectAddress;
3988 }
3989
3990 attnum =
3991 renameatt_internal(relid,
3992 stmt->subname, /* old att name */
3993 stmt->newname, /* new att name */
3994 stmt->relation->inh, /* recursive? */
3995 false, /* recursing? */
3996 0, /* expected inhcount */
3997 stmt->behavior);
3998
3999 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4000
4001 return address;
4002}
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition: tablecmds.c:3805
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:3950

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

3757{
3758 char relkind = classform->relkind;
3759
3760 if (classform->reloftype && !recursing)
3761 ereport(ERROR,
3762 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3763 errmsg("cannot rename column of typed table")));
3764
3765 /*
3766 * Renaming the columns of sequences or toast tables doesn't actually
3767 * break anything from the system's point of view, since internal
3768 * references are by attnum. But it doesn't seem right to allow users to
3769 * change names that are hardcoded into the system, hence the following
3770 * restriction.
3771 */
3772 if (relkind != RELKIND_RELATION &&
3773 relkind != RELKIND_VIEW &&
3774 relkind != RELKIND_MATVIEW &&
3775 relkind != RELKIND_COMPOSITE_TYPE &&
3776 relkind != RELKIND_INDEX &&
3777 relkind != RELKIND_PARTITIONED_INDEX &&
3778 relkind != RELKIND_FOREIGN_TABLE &&
3779 relkind != RELKIND_PARTITIONED_TABLE)
3780 ereport(ERROR,
3781 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3782 errmsg("cannot rename columns of relation \"%s\"",
3783 NameStr(classform->relname)),
3785
3786 /*
3787 * permissions checking. only the owner of a class can change its schema.
3788 */
3789 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3791 NameStr(classform->relname));
3792 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3793 ereport(ERROR,
3794 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3795 errmsg("permission denied: \"%s\" is a system catalog",
3796 NameStr(classform->relname))));
3797}

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

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

4118{
4119 Oid relid = InvalidOid;
4120 Oid typid = InvalidOid;
4121
4122 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4123 {
4124 Relation rel;
4125 HeapTuple tup;
4126
4127 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4128 rel = table_open(TypeRelationId, RowExclusiveLock);
4129 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4130 if (!HeapTupleIsValid(tup))
4131 elog(ERROR, "cache lookup failed for type %u", typid);
4132 checkDomainOwner(tup);
4133 ReleaseSysCache(tup);
4134 table_close(rel, NoLock);
4135 }
4136 else
4137 {
4138 /* lock level taken here should match rename_constraint_internal */
4140 stmt->missing_ok ? RVR_MISSING_OK : 0,
4142 NULL);
4143 if (!OidIsValid(relid))
4144 {
4146 (errmsg("relation \"%s\" does not exist, skipping",
4147 stmt->relation->relname)));
4148 return InvalidObjectAddress;
4149 }
4150 }
4151
4152 return
4153 rename_constraint_internal(relid, typid,
4154 stmt->subname,
4155 stmt->newname,
4156 (stmt->relation &&
4157 stmt->relation->inh), /* recursive? */
4158 false, /* recursing? */
4159 0 /* expected inhcount */ );
4160}
void checkDomainOwner(HeapTuple tup)
Definition: typecmds.c:3473

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

4168{
4169 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4170 Oid relid;
4171 ObjectAddress address;
4172
4173 /*
4174 * Grab an exclusive lock on the target table, index, sequence, view,
4175 * materialized view, or foreign table, which we will NOT release until
4176 * end of transaction.
4177 *
4178 * Lock level used here should match RenameRelationInternal, to avoid lock
4179 * escalation. However, because ALTER INDEX can be used with any relation
4180 * type, we mustn't believe without verification.
4181 */
4182 for (;;)
4183 {
4184 LOCKMODE lockmode;
4185 char relkind;
4186 bool obj_is_index;
4187
4188 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4189
4190 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4191 stmt->missing_ok ? RVR_MISSING_OK : 0,
4193 stmt);
4194
4195 if (!OidIsValid(relid))
4196 {
4198 (errmsg("relation \"%s\" does not exist, skipping",
4199 stmt->relation->relname)));
4200 return InvalidObjectAddress;
4201 }
4202
4203 /*
4204 * We allow mismatched statement and object types (e.g., ALTER INDEX
4205 * to rename a table), but we might've used the wrong lock level. If
4206 * that happens, retry with the correct lock level. We don't bother
4207 * if we already acquired AccessExclusiveLock with an index, however.
4208 */
4209 relkind = get_rel_relkind(relid);
4210 obj_is_index = (relkind == RELKIND_INDEX ||
4211 relkind == RELKIND_PARTITIONED_INDEX);
4212 if (obj_is_index || is_index_stmt == obj_is_index)
4213 break;
4214
4215 UnlockRelationOid(relid, lockmode);
4216 is_index_stmt = obj_is_index;
4217 }
4218
4219 /* Do the work */
4220 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4221
4222 ObjectAddressSet(address, RelationRelationId, relid);
4223
4224 return address;
4225}

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

4232{
4233 Relation targetrelation;
4234 Relation relrelation; /* for RELATION relation */
4235 ItemPointerData otid;
4236 HeapTuple reltup;
4237 Form_pg_class relform;
4238 Oid namespaceId;
4239
4240 /*
4241 * Grab a lock on the target relation, which we will NOT release until end
4242 * of transaction. We need at least a self-exclusive lock so that
4243 * concurrent DDL doesn't overwrite the rename if they start updating
4244 * while still seeing the old version. The lock also guards against
4245 * triggering relcache reloads in concurrent sessions, which might not
4246 * handle this information changing under them. For indexes, we can use a
4247 * reduced lock level because RelationReloadIndexInfo() handles indexes
4248 * specially.
4249 */
4250 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4251 namespaceId = RelationGetNamespace(targetrelation);
4252
4253 /*
4254 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4255 */
4256 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4257
4258 reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4259 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4260 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4261 otid = reltup->t_self;
4262 relform = (Form_pg_class) GETSTRUCT(reltup);
4263
4264 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4265 ereport(ERROR,
4266 (errcode(ERRCODE_DUPLICATE_TABLE),
4267 errmsg("relation \"%s\" already exists",
4268 newrelname)));
4269
4270 /*
4271 * RenameRelation is careful not to believe the caller's idea of the
4272 * relation kind being handled. We don't have to worry about this, but
4273 * let's not be totally oblivious to it. We can process an index as
4274 * not-an-index, but not the other way around.
4275 */
4276 Assert(!is_index ||
4277 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4278 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4279
4280 /*
4281 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4282 * because it's a copy...)
4283 */
4284 namestrcpy(&(relform->relname), newrelname);
4285
4286 CatalogTupleUpdate(relrelation, &otid, reltup);
4287 UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4288
4289 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4290 InvalidOid, is_internal);
4291
4292 heap_freetuple(reltup);
4293 table_close(relrelation, RowExclusiveLock);
4294
4295 /*
4296 * Also rename the associated type, if any.
4297 */
4298 if (OidIsValid(targetrelation->rd_rel->reltype))
4299 RenameTypeInternal(targetrelation->rd_rel->reltype,
4300 newrelname, namespaceId);
4301
4302 /*
4303 * Also rename the associated constraint, if any.
4304 */
4305 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4306 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4307 {
4308 Oid constraintId = get_index_constraint(myrelid);
4309
4310 if (OidIsValid(constraintId))
4311 RenameConstraintById(constraintId, newrelname);
4312 }
4313
4314 /*
4315 * Close rel, but keep lock!
4316 */
4317 relation_close(targetrelation, NoLock);
4318}
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 4324 of file tablecmds.c.

4325{
4326 Relation relrelation; /* for RELATION relation */
4327 HeapTuple reltup;
4328 Form_pg_class relform;
4329
4330 /*
4331 * Find relation's pg_class tuple.
4332 */
4333 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4334
4335 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4336 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4337 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4338 relform = (Form_pg_class) GETSTRUCT(reltup);
4339
4340 /*
4341 * Update pg_class tuple.
4342 */
4343 relform->relrewrite = InvalidOid;
4344
4345 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4346
4347 heap_freetuple(reltup);
4348 table_close(relrelation, RowExclusiveLock);
4349}

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,
LOCKMODE  lockmode 
)
static

Definition at line 7739 of file tablecmds.c.

7741{
7742 Form_pg_attribute attr;
7743
7745
7746 /*
7747 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7748 * attribute.
7749 */
7750 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7751 if (attr->attisdropped)
7752 return;
7753
7754 if (!attr->attnotnull)
7755 {
7756 Relation attr_rel;
7757 HeapTuple tuple;
7758
7759 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7760
7762 if (!HeapTupleIsValid(tuple))
7763 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7764 attnum, RelationGetRelid(rel));
7765
7766 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7767 Assert(!attr->attnotnull);
7768 attr->attnotnull = true;
7769 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7770
7771 /*
7772 * If the nullness isn't already proven by validated constraints, have
7773 * ALTER TABLE phase 3 test for it.
7774 */
7775 if (wqueue && !NotNullImpliedByRelConstraints(rel, attr))
7776 {
7777 AlteredTableInfo *tab;
7778
7779 tab = ATGetQueueEntry(wqueue, rel);
7780 tab->verify_new_notnull = true;
7781 }
7782
7784
7785 table_close(attr_rel, RowExclusiveLock);
7786 heap_freetuple(tuple);
7787 }
7788}
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition: tablecmds.c:7976

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

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

◆ SetIndexStorageProperties()

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

Definition at line 8989 of file tablecmds.c.

8994{
8995 ListCell *lc;
8996
8997 foreach(lc, RelationGetIndexList(rel))
8998 {
8999 Oid indexoid = lfirst_oid(lc);
9000 Relation indrel;
9001 AttrNumber indattnum = 0;
9002 HeapTuple tuple;
9003
9004 indrel = index_open(indexoid, lockmode);
9005
9006 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9007 {
9008 if (indrel->rd_index->indkey.values[i] == attnum)
9009 {
9010 indattnum = i + 1;
9011 break;
9012 }
9013 }
9014
9015 if (indattnum == 0)
9016 {
9017 index_close(indrel, lockmode);
9018 continue;
9019 }
9020
9021 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9022
9023 if (HeapTupleIsValid(tuple))
9024 {
9025 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9026
9027 if (setstorage)
9028 attrtuple->attstorage = newstorage;
9029
9030 if (setcompression)
9031 attrtuple->attcompression = newcompression;
9032
9033 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9034
9035 InvokeObjectPostAlterHook(RelationRelationId,
9036 RelationGetRelid(rel),
9037 attrtuple->attnum);
9038
9039 heap_freetuple(tuple);
9040 }
9041
9042 index_close(indrel, lockmode);
9043 }
9044}

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

3609{
3610 Relation relationRelation;
3611 HeapTuple tuple;
3612 Form_pg_class classtuple;
3613
3615 ShareUpdateExclusiveLock, false) ||
3616 CheckRelationOidLockedByMe(relationId,
3617 ShareRowExclusiveLock, true));
3618
3619 /*
3620 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3621 */
3622 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3623 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3624 if (!HeapTupleIsValid(tuple))
3625 elog(ERROR, "cache lookup failed for relation %u", relationId);
3626 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3627
3628 if (classtuple->relhassubclass != relhassubclass)
3629 {
3630 classtuple->relhassubclass = relhassubclass;
3631 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3632 }
3633 else
3634 {
3635 /* no need to change tuple, but force relcache rebuild anyway */
3637 }
3638
3639 heap_freetuple(tuple);
3640 table_close(relationRelation, RowExclusiveLock);
3641}
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1655

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

3714{
3715 Relation pg_class;
3716 HeapTuple tuple;
3717 ItemPointerData otid;
3718 Form_pg_class rd_rel;
3719 Oid reloid = RelationGetRelid(rel);
3720
3721 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3722
3723 /* Get a modifiable copy of the relation's pg_class row. */
3724 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3725
3726 tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3727 if (!HeapTupleIsValid(tuple))
3728 elog(ERROR, "cache lookup failed for relation %u", reloid);
3729 otid = tuple->t_self;
3730 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3731
3732 /* Update the pg_class row. */
3733 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3734 InvalidOid : newTableSpaceId;
3735 if (RelFileNumberIsValid(newRelFilenumber))
3736 rd_rel->relfilenode = newRelFilenumber;
3737 CatalogTupleUpdate(pg_class, &otid, tuple);
3738 UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3739
3740 /*
3741 * Record dependency on tablespace. This is only required for relations
3742 * that have no physical storage.
3743 */
3744 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3745 changeDependencyOnTablespace(RelationRelationId, reloid,
3746 rd_rel->reltablespace);
3747
3748 heap_freetuple(tuple);
3749 table_close(pg_class, RowExclusiveLock);
3750}
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 2422 of file tablecmds.c.

2423{
2424 switch (c)
2425 {
2426 case TYPSTORAGE_PLAIN:
2427 return "PLAIN";
2428 case TYPSTORAGE_EXTERNAL:
2429 return "EXTERNAL";
2430 case TYPSTORAGE_EXTENDED:
2431 return "EXTENDED";
2432 case TYPSTORAGE_MAIN:
2433 return "MAIN";
2434 default:
2435 return "???";
2436 }
2437}
char * c

Referenced by MergeChildAttribute(), and MergeInheritedAttribute().

◆ StoreCatalogInheritance()

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

Definition at line 3482 of file tablecmds.c.

3484{
3485 Relation relation;
3486 int32 seqNumber;
3487 ListCell *entry;
3488
3489 /*
3490 * sanity checks
3491 */
3492 Assert(OidIsValid(relationId));
3493
3494 if (supers == NIL)
3495 return;
3496
3497 /*
3498 * Store INHERITS information in pg_inherits using direct ancestors only.
3499 * Also enter dependencies on the direct ancestors, and make sure they are
3500 * marked with relhassubclass = true.
3501 *
3502 * (Once upon a time, both direct and indirect ancestors were found here
3503 * and then entered into pg_ipl. Since that catalog doesn't exist
3504 * anymore, there's no need to look for indirect ancestors.)
3505 */
3506 relation = table_open(InheritsRelationId, RowExclusiveLock);
3507
3508 seqNumber = 1;
3509 foreach(entry, supers)
3510 {
3511 Oid parentOid = lfirst_oid(entry);
3512
3513 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3514 child_is_partition);
3515 seqNumber++;
3516 }
3517
3518 table_close(relation, RowExclusiveLock);
3519}

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

3529{
3530 ObjectAddress childobject,
3531 parentobject;
3532
3533 /* store the pg_inherits row */
3534 StoreSingleInheritance(relationId, parentOid, seqNumber);
3535
3536 /*
3537 * Store a dependency too
3538 */
3539 parentobject.classId = RelationRelationId;
3540 parentobject.objectId = parentOid;
3541 parentobject.objectSubId = 0;
3542 childobject.classId = RelationRelationId;
3543 childobject.objectId = relationId;
3544 childobject.objectSubId = 0;
3545
3546 recordDependencyOn(&childobject, &parentobject,
3547 child_dependency_type(child_is_partition));
3548
3549 /*
3550 * Post creation hook of this inheritance. Since object_access_hook
3551 * doesn't take multiple object identifiers, we relay oid of parent
3552 * relation using auxiliary_id argument.
3553 */
3554 InvokeObjectPostAlterHookArg(InheritsRelationId,
3555 relationId, 0,
3556 parentOid, false);
3557
3558 /*
3559 * Mark the parent as having subclasses.
3560 */
3561 SetRelationHasSubclass(parentOid, true);
3562}
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
Definition: pg_inherits.c:508
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition: tablecmds.c:3608

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

12620{
12621 ListCell *l;
12622 int attnum;
12623
12624 attnum = 0;
12625 foreach(l, colList)
12626 {
12627 char *attname = strVal(lfirst(l));
12628 HeapTuple atttuple;
12629 Form_pg_attribute attform;
12630
12631 atttuple = SearchSysCacheAttName(relId, attname);
12632 if (!HeapTupleIsValid(atttuple))
12633 ereport(ERROR,
12634 (errcode(ERRCODE_UNDEFINED_COLUMN),
12635 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12636 attname)));
12637 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
12638 if (attform->attnum < 0)
12639 ereport(ERROR,
12640 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12641 errmsg("system columns cannot be used in foreign keys")));
12642 if (attnum >= INDEX_MAX_KEYS)
12643 ereport(ERROR,
12644 (errcode(ERRCODE_TOO_MANY_COLUMNS),
12645 errmsg("cannot have more than %d keys in a foreign key",
12646 INDEX_MAX_KEYS)));
12647 attnums[attnum] = attform->attnum;
12648 if (atttypids != NULL)
12649 atttypids[attnum] = attform->atttypid;
12650 if (attcollids != NULL)
12651 attcollids[attnum] = attform->attcollation;
12652 ReleaseSysCache(atttuple);
12653 attnum++;
12654 }
12655
12656 return attnum;
12657}

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

12780{
12781 Oid indexoid = InvalidOid;
12782 bool found = false;
12783 bool found_deferrable = false;
12784 List *indexoidlist;
12785 ListCell *indexoidscan;
12786 int i,
12787 j;
12788
12789 /*
12790 * Reject duplicate appearances of columns in the referenced-columns list.
12791 * Such a case is forbidden by the SQL standard, and even if we thought it
12792 * useful to allow it, there would be ambiguity about how to match the
12793 * list to unique indexes (in particular, it'd be unclear which index
12794 * opclass goes with which FK column).
12795 */
12796 for (i = 0; i < numattrs; i++)
12797 {
12798 for (j = i + 1; j < numattrs; j++)
12799 {
12800 if (attnums[i] == attnums[j])
12801 ereport(ERROR,
12802 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12803 errmsg("foreign key referenced-columns list must not contain duplicates")));
12804 }
12805 }
12806
12807 /*
12808 * Get the list of index OIDs for the table from the relcache, and look up
12809 * each one in the pg_index syscache, and match unique indexes to the list
12810 * of attnums we are given.
12811 */
12812 indexoidlist = RelationGetIndexList(pkrel);
12813
12814 foreach(indexoidscan, indexoidlist)
12815 {
12816 HeapTuple indexTuple;
12817 Form_pg_index indexStruct;
12818
12819 indexoid = lfirst_oid(indexoidscan);
12820 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12821 if (!HeapTupleIsValid(indexTuple))
12822 elog(ERROR, "cache lookup failed for index %u", indexoid);
12823 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12824
12825 /*
12826 * Must have the right number of columns; must be unique (or if
12827 * temporal then exclusion instead) and not a partial index; forget it
12828 * if there are any expressions, too. Invalid indexes are out as well.
12829 */
12830 if (indexStruct->indnkeyatts == numattrs &&
12831 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
12832 indexStruct->indisvalid &&
12833 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
12834 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
12835 {
12836 Datum indclassDatum;
12837 oidvector *indclass;
12838
12839 /* Must get indclass the hard way */
12840 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12841 Anum_pg_index_indclass);
12842 indclass = (oidvector *) DatumGetPointer(indclassDatum);
12843
12844 /*
12845 * The given attnum list may match the index columns in any order.
12846 * Check for a match, and extract the appropriate opclasses while
12847 * we're at it.
12848 *
12849 * We know that attnums[] is duplicate-free per the test at the
12850 * start of this function, and we checked above that the number of
12851 * index columns agrees, so if we find a match for each attnums[]
12852 * entry then we must have a one-to-one match in some order.
12853 */
12854 for (i = 0; i < numattrs; i++)
12855 {
12856 found = false;
12857 for (j = 0; j < numattrs; j++)
12858 {
12859 if (attnums[i] == indexStruct->indkey.values[j])
12860 {
12861 opclasses[i] = indclass->values[j];
12862 found = true;
12863 break;
12864 }
12865 }
12866 if (!found)
12867 break;
12868 }
12869 /* The last attribute in the index must be the PERIOD FK part */
12870 if (found && with_period)
12871 {
12872 int16 periodattnum = attnums[numattrs - 1];
12873
12874 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
12875 }
12876
12877 /*
12878 * Refuse to use a deferrable unique/primary key. This is per SQL
12879 * spec, and there would be a lot of interesting semantic problems
12880 * if we tried to allow it.
12881 */
12882 if (found && !indexStruct->indimmediate)
12883 {
12884 /*
12885 * Remember that we found an otherwise matching index, so that
12886 * we can generate a more appropriate error message.
12887 */
12888 found_deferrable = true;
12889 found = false;
12890 }
12891
12892 /* We need to know whether the index has WITHOUT OVERLAPS */
12893 if (found)
12894 *pk_has_without_overlaps = indexStruct->indisexclusion;
12895 }
12896 ReleaseSysCache(indexTuple);
12897 if (found)
12898 break;
12899 }
12900
12901 if (!found)
12902 {
12903 if (found_deferrable)
12904 ereport(ERROR,
12905 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12906 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12907 RelationGetRelationName(pkrel))));
12908 else
12909 ereport(ERROR,
12910 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12911 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12912 RelationGetRelationName(pkrel))));
12913 }
12914
12915 list_free(indexoidlist);
12916
12917 return indexoid;
12918}
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 12673 of file tablecmds.c.

12677{
12678 List *indexoidlist;
12679 ListCell *indexoidscan;
12680 HeapTuple indexTuple = NULL;
12681 Form_pg_index indexStruct = NULL;
12682 Datum indclassDatum;
12683 oidvector *indclass;
12684 int i;
12685
12686 /*
12687 * Get the list of index OIDs for the table from the relcache, and look up
12688 * each one in the pg_index syscache until we find one marked primary key
12689 * (hopefully there isn't more than one such). Insist it's valid, too.
12690 */
12691 *indexOid = InvalidOid;
12692
12693 indexoidlist = RelationGetIndexList(pkrel);
12694
12695 foreach(indexoidscan, indexoidlist)
12696 {
12697 Oid indexoid = lfirst_oid(indexoidscan);
12698
12699 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12700 if (!HeapTupleIsValid(indexTuple))
12701 elog(ERROR, "cache lookup failed for index %u", indexoid);
12702 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12703 if (indexStruct->indisprimary && indexStruct->indisvalid)
12704 {
12705 /*
12706 * Refuse to use a deferrable primary key. This is per SQL spec,
12707 * and there would be a lot of interesting semantic problems if we
12708 * tried to allow it.
12709 */
12710 if (!indexStruct->indimmediate)
12711 ereport(ERROR,
12712 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12713 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12714 RelationGetRelationName(pkrel))));
12715
12716 *indexOid = indexoid;
12717 break;
12718 }
12719 ReleaseSysCache(indexTuple);
12720 }
12721
12722 list_free(indexoidlist);
12723
12724 /*
12725 * Check that we found it
12726 */
12727 if (!OidIsValid(*indexOid))
12728 ereport(ERROR,
12729 (errcode(ERRCODE_UNDEFINED_OBJECT),
12730 errmsg("there is no primary key for referenced table \"%s\"",
12731 RelationGetRelationName(pkrel))));
12732
12733 /* Must get indclass the hard way */
12734 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12735 Anum_pg_index_indclass);
12736 indclass = (oidvector *) DatumGetPointer(indclassDatum);
12737
12738 /*
12739 * Now build the list of PK attributes from the indkey definition (we
12740 * assume a primary key cannot have expressional elements)
12741 */
12742 *attnamelist = NIL;
12743 for (i = 0; i < indexStruct->indnkeyatts; i++)
12744 {
12745 int pkattno = indexStruct->indkey.values[i];
12746
12747 attnums[i] = pkattno;
12748 atttypids[i] = attnumTypeId(pkrel, pkattno);
12749 attcollids[i] = attnumCollationId(pkrel, pkattno);
12750 opclasses[i] = indclass->values[i];
12751 *attnamelist = lappend(*attnamelist,
12752 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
12753 }
12754
12755 *pk_has_without_overlaps = indexStruct->indisexclusion;
12756
12757 ReleaseSysCache(indexTuple);
12758
12759 return i;
12760}
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 18994 of file tablecmds.c.

18995{
18996 PartitionSpec *newspec;
18997 ParseState *pstate;
18998 ParseNamespaceItem *nsitem;
18999 ListCell *l;
19000
19001 newspec = makeNode(PartitionSpec);
19002
19003 newspec->strategy = partspec->strategy;
19004 newspec->partParams = NIL;
19005 newspec->location = partspec->location;
19006
19007 /* Check valid number of columns for strategy */
19008 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19009 list_length(partspec->partParams) != 1)
19010 ereport(ERROR,
19011 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19012 errmsg("cannot use \"list\" partition strategy with more than one column")));
19013
19014 /*
19015 * Create a dummy ParseState and insert the target relation as its sole
19016 * rangetable entry. We need a ParseState for transformExpr.
19017 */
19018 pstate = make_parsestate(NULL);
19019 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19020 NULL, false, true);
19021 addNSItemToQuery(pstate, nsitem, true, true, true);
19022
19023 /* take care of any partition expressions */
19024 foreach(l, partspec->partParams)
19025 {
19027
19028 if (pelem->expr)
19029 {
19030 /* Copy, to avoid scribbling on the input */
19031 pelem = copyObject(pelem);
19032
19033 /* Now do parse transformation of the expression */
19034 pelem->expr = transformExpr(pstate, pelem->expr,
19036
19037 /* we have to fix its collations too */
19038 assign_expr_collations(pstate, pelem->expr);
19039 }
19040
19041 newspec->partParams = lappend(newspec->partParams, pelem);
19042 }
19043
19044 return newspec;
19045}
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 2399 of file tablecmds.c.

2400{
2401 /*
2402 * Don't allow truncate on temp tables of other backends ... their local
2403 * buffer manager is not going to cope.
2404 */
2405 if (RELATION_IS_OTHER_TEMP(rel))
2406 ereport(ERROR,
2407 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2408 errmsg("cannot truncate temporary tables of other sessions")));
2409
2410 /*
2411 * Also check for active uses of the relation in the current transaction,
2412 * including open scans and pending AFTER trigger events.
2413 */
2414 CheckTableNotInUse(rel, "TRUNCATE");
2415}

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

2382{
2383 char *relname = NameStr(reltuple->relname);
2384 AclResult aclresult;
2385
2386 /* Permissions checks */
2387 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2388 if (aclresult != ACLCHECK_OK)
2389 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2390 relname);
2391}
#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 2333 of file tablecmds.c.

2334{
2335 char *relname = NameStr(reltuple->relname);
2336
2337 /*
2338 * Only allow truncate on regular tables, foreign tables using foreign
2339 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2340 * latter are only being included here for the following checks; no
2341 * physical truncation will occur in their case.).
2342 */
2343 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2344 {
2345 Oid serverid = GetForeignServerIdByRelId(relid);
2346 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2347
2348 if (!fdwroutine->ExecForeignTruncate)
2349 ereport(ERROR,
2350 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2351 errmsg("cannot truncate foreign table \"%s\"",
2352 relname)));
2353 }
2354 else if (reltuple->relkind != RELKIND_RELATION &&
2355 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2356 ereport(ERROR,
2357 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2358 errmsg("\"%s\" is not a table", relname)));
2359
2360 /*
2361 * Most system catalogs can't be truncated at all, or at least not unless
2362 * allow_system_table_mods=on. As an exception, however, we allow
2363 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2364 * to change its relfilenode to match the old cluster, and allowing a
2365 * TRUNCATE command to be executed is the easiest way of doing that.
2366 */
2367 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2368 && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2369 ereport(ERROR,
2370 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2371 errmsg("permission denied: \"%s\" is a system catalog",
2372 relname)));
2373
2375}
bool IsBinaryUpgrade
Definition: globals.c:120
#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 11460 of file tablecmds.c.

11471{
11472 HeapTuple parentConstrTup;
11473 Form_pg_constraint parentConstr;
11474 HeapTuple partcontup;
11475 Form_pg_constraint partConstr;
11476
11477 parentConstrTup = SearchSysCache1(CONSTROID,
11478 ObjectIdGetDatum(parentConstrOid));
11479 if (!HeapTupleIsValid(parentConstrTup))
11480 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11481 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11482
11483 /*
11484 * Do some quick & easy initial checks. If any of these fail, we cannot
11485 * use this constraint.
11486 */
11487 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11488 {
11489 ReleaseSysCache(parentConstrTup);
11490 return false;
11491 }
11492 for (int i = 0; i < numfks; i++)
11493 {
11494 if (fk->conkey[i] != mapped_conkey[i] ||
11495 fk->confkey[i] != confkey[i] ||
11496 fk->conpfeqop[i] != conpfeqop[i])
11497 {
11498 ReleaseSysCache(parentConstrTup);
11499 return false;
11500 }
11501 }
11502
11503 /* Looks good so far; perform more extensive checks. */
11504 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11505 if (!HeapTupleIsValid(partcontup))
11506 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11507 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11508 if (OidIsValid(partConstr->conparentid) ||
11509 partConstr->condeferrable != parentConstr->condeferrable ||
11510 partConstr->condeferred != parentConstr->condeferred ||
11511 partConstr->confupdtype != parentConstr->confupdtype ||
11512 partConstr->confdeltype != parentConstr->confdeltype ||
11513 partConstr->confmatchtype != parentConstr->confmatchtype)
11514 {
11515 ReleaseSysCache(parentConstrTup);
11516 ReleaseSysCache(partcontup);
11517 return false;
11518 }
11519
11520 ReleaseSysCache(parentConstrTup);
11521 ReleaseSysCache(partcontup);
11522
11523 /* Looks good! Attach this constraint. */
11524 AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11525 parentConstrOid, parentInsTrigger,
11526 parentUpdTrigger, trigrel);
11527
11528 return true;
11529}
static void AttachPartitionForeignKey(List **wqueue, Relation partition, Oid partConstrOid, Oid parentConstrOid, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11539

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

Referenced by addFkRecurseReferencing(), and CloneFkReferencing().

◆ TryReuseForeignKey()

static void TryReuseForeignKey ( Oid  oldId,
Constraint con 
)
static

Definition at line 15182 of file tablecmds.c.

15183{
15184 HeapTuple tup;
15185 Datum adatum;
15186 ArrayType *arr;
15187 Oid *rawarr;
15188 int numkeys;
15189 int i;
15190
15191 Assert(con->contype == CONSTR_FOREIGN);
15192 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15193
15194 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15195 if (!HeapTupleIsValid(tup)) /* should not happen */
15196 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15197
15198 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15199 Anum_pg_constraint_conpfeqop);
15200 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15201 numkeys = ARR_DIMS(arr)[0];
15202 /* test follows the one in ri_FetchConstraintInfo() */
15203 if (ARR_NDIM(arr) != 1 ||
15204 ARR_HASNULL(arr) ||
15205 ARR_ELEMTYPE(arr) != OIDOID)
15206 elog(ERROR, "conpfeqop is not a 1-D Oid array");
15207 rawarr = (Oid *) ARR_DATA_PTR(arr);
15208
15209 /* stash a List of the operator Oids in our Constraint node */
15210 for (i = 0; i < numkeys; i++)
15211 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15212
15213 ReleaseSysCache(tup);
15214}
#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 15153 of file tablecmds.c.

15154{
15155 if (CheckIndexCompatible(oldId,
15156 stmt->accessMethod,
15157 stmt->indexParams,
15158 stmt->excludeOpNames,
15159 stmt->iswithoutoverlaps))
15160 {
15161 Relation irel = index_open(oldId, NoLock);
15162
15163 /* If it's a partitioned index, there is no storage to share. */
15164 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15165 {
15166 stmt->oldNumber = irel->rd_locator.relNumber;
15167 stmt->oldCreateSubid = irel->rd_createSubid;
15168 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15169 }
15170 index_close(irel, NoLock);
15171 }
15172}
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()

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

Definition at line 10428 of file tablecmds.c.

10431{
10432 for (int i = 0; i < numfksetcols; i++)
10433 {
10434 int16 setcol_attnum = fksetcolsattnums[i];
10435 bool seen = false;
10436
10437 for (int j = 0; j < numfks; j++)
10438 {
10439 if (fkattnums[j] == setcol_attnum)
10440 {
10441 seen = true;
10442 break;
10443 }
10444 }
10445
10446 if (!seen)
10447 {
10448 char *col = strVal(list_nth(fksetcols, i));
10449
10450 ereport(ERROR,
10451 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10452 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10453 }
10454 }
10455}

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

12991{
12992 TupleTableSlot *slot;
12993 TableScanDesc scan;
12994 Trigger trig = {0};
12995 Snapshot snapshot;
12996 MemoryContext oldcxt;
12997 MemoryContext perTupCxt;
12998
13000 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13001
13002 /*
13003 * Build a trigger call structure; we'll need it either way.
13004 */
13005 trig.tgoid = InvalidOid;
13006 trig.tgname = conname;
13008 trig.tgisinternal = true;
13009 trig.tgconstrrelid = RelationGetRelid(pkrel);
13010 trig.tgconstrindid = pkindOid;
13011 trig.tgconstraint = constraintOid;
13012 trig.tgdeferrable = false;
13013 trig.tginitdeferred = false;
13014 /* we needn't fill in remaining fields */
13015
13016 /*
13017 * See if we can do it with a single LEFT JOIN query. A false result
13018 * indicates we must proceed with the fire-the-trigger method. We can't do
13019 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13020 * left joins.
13021 */
13022 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13023 return;
13024
13025 /*
13026 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13027 * if that tuple had just been inserted. If any of those fail, it should
13028 * ereport(ERROR) and that's that.
13029 */
13030 snapshot = RegisterSnapshot(GetLatestSnapshot());
13031 slot = table_slot_create(rel, NULL);
13032 scan = table_beginscan(rel, snapshot, 0, NULL);
13033
13035 "validateForeignKeyConstraint",
13037 oldcxt = MemoryContextSwitchTo(perTupCxt);
13038
13039 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13040 {
13041 LOCAL_FCINFO(fcinfo, 0);
13042 TriggerData trigdata = {0};
13043
13045
13046 /*
13047 * Make a call to the trigger function
13048 *
13049 * No parameters are passed, but we do set a context
13050 */
13051 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13052
13053 /*
13054 * We assume RI_FKey_check_ins won't look at flinfo...
13055 */
13056 trigdata.type = T_TriggerData;
13058 trigdata.tg_relation = rel;
13059 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13060 trigdata.tg_trigslot = slot;
13061 trigdata.tg_trigger = &trig;
13062
13063 fcinfo->context = (Node *) &trigdata;
13064
13065 RI_FKey_check_ins(fcinfo);
13066
13067 MemoryContextReset(perTupCxt);
13068 }
13069
13070 MemoryContextSwitchTo(oldcxt);
13071 MemoryContextDelete(perTupCxt);
13072 table_endscan(scan);
13073 UnregisterSnapshot(snapshot);
13075}
#define MemSet(start, val, len)
Definition: c.h:991
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1831
#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 21054 of file tablecmds.c.

21055{
21056 Relation inheritsRel;
21057 SysScanDesc scan;
21059 int tuples = 0;
21060 HeapTuple inhTup;
21061 bool updated = false;
21062
21063 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21064
21065 /*
21066 * Scan pg_inherits for this parent index. Count each valid index we find
21067 * (verifying the pg_index entry for each), and if we reach the total
21068 * amount we expect, we can mark this parent index as valid.
21069 */
21070 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21071 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21072 BTEqualStrategyNumber, F_OIDEQ,
21074 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21075 NULL, 1, &key);
21076 while ((inhTup = systable_getnext(scan)) != NULL)
21077 {
21078 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21079 HeapTuple indTup;
21080 Form_pg_index indexForm;
21081
21082 indTup = SearchSysCache1(INDEXRELID,
21083 ObjectIdGetDatum(inhForm->inhrelid));
21084 if (!HeapTupleIsValid(indTup))
21085 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21086 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21087 if (indexForm->indisvalid)
21088 tuples += 1;
21089 ReleaseSysCache(indTup);
21090 }
21091
21092 /* Done with pg_inherits */
21093 systable_endscan(scan);
21094 table_close(inheritsRel, AccessShareLock);
21095
21096 /*
21097 * If we found as many inherited indexes as the partitioned table has
21098 * partitions, we're good; update pg_index to set indisvalid.
21099 */
21100 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21101 {
21102 Relation idxRel;
21103 HeapTuple indTup;
21104 Form_pg_index indexForm;
21105
21106 idxRel = table_open(IndexRelationId, RowExclusiveLock);
21107 indTup = SearchSysCacheCopy1(INDEXRELID,
21109 if (!HeapTupleIsValid(indTup))
21110 elog(ERROR, "cache lookup failed for index %u",
21111 RelationGetRelid(partedIdx));
21112 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21113
21114 indexForm->indisvalid = true;
21115 updated = true;
21116
21117 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21118
21120 heap_freetuple(indTup);
21121 }
21122
21123 /*
21124 * If this index is in turn a partition of a larger index, validating it
21125 * might cause the parent to become valid also. Try that.
21126 */
21127 if (updated && partedIdx->rd_rel->relispartition)
21128 {
21129 Oid parentIdxId,
21130 parentTblId;
21131 Relation parentIdx,
21132 parentTbl;
21133
21134 /* make sure we see the validation we just did */
21136
21137 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21138 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21139 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21140 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21141 Assert(!parentIdx->rd_index->indisvalid);
21142
21143 validatePartitionedIndex(parentIdx, parentTbl);
21144
21147 }
21148}

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().

◆ verifyPartitionIndexNotNull()

static void verifyPartitionIndexNotNull ( IndexInfo iinfo,
Relation  partIdx 
)
static

Definition at line 21156 of file tablecmds.c.

21157{
21158 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21159 {
21161 iinfo->ii_IndexAttrNumbers[i] - 1);
21162
21163 if (!att->attnotnull)
21164 ereport(ERROR,
21165 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21166 errmsg("invalid primary key definition"),
21167 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21168 NameStr(att->attname),
21169 RelationGetRelationName(partition)));
21170 }
21171}
int ii_NumIndexKeyAttrs
Definition: execnodes.h:197
AttrNumber ii_IndexAttrNumbers[INDEX_MAX_KEYS]
Definition: execnodes.h:198

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

Referenced by ATExecAttachPartitionIdx().

Variable Documentation

◆ dropmsgstringarray

const struct dropmsgstrings dropmsgstringarray[]
static

Definition at line 255 of file tablecmds.c.

Referenced by DropErrorMsgNonExistent(), and DropErrorMsgWrongType().

◆ on_commits