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_largeobject_metadata.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication_rel.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "catalog/toasting.h"
#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "commands/user.h"
#include "commands/vacuum.h"
#include "common/int.h"
#include "executor/executor.h"
#include "foreign/fdwapi.h"
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/parsenodes.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "parser/parse_utilcmd.h"
#include "parser/parser.h"
#include "partitioning/partbounds.h"
#include "partitioning/partdesc.h"
#include "pgstat.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/lock.h"
#include "storage/predicate.h"
#include "storage/smgr.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/partcache.h"
#include "utils/relcache.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
#include "utils/usercontext.h"
Include dependency graph for tablecmds.c:

Go to the source code of this file.

Data Structures

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

Macros

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

Typedefs

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

Enumerations

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

Functions

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

Variables

static Liston_commits = NIL
 
static const struct dropmsgstrings dropmsgstringarray []
 

Macro Definition Documentation

◆ AT_NUM_PASSES

#define AT_NUM_PASSES   (AT_PASS_MISC + 1)

Definition at line 167 of file tablecmds.c.

◆ ATT_COMPOSITE_TYPE

#define ATT_COMPOSITE_TYPE   0x0010

Definition at line 333 of file tablecmds.c.

◆ ATT_FOREIGN_TABLE

#define ATT_FOREIGN_TABLE   0x0020

Definition at line 334 of file tablecmds.c.

◆ ATT_INDEX

#define ATT_INDEX   0x0008

Definition at line 332 of file tablecmds.c.

◆ ATT_MATVIEW

#define ATT_MATVIEW   0x0004

Definition at line 331 of file tablecmds.c.

◆ ATT_PARTITIONED_INDEX

#define ATT_PARTITIONED_INDEX   0x0040

Definition at line 335 of file tablecmds.c.

◆ ATT_PARTITIONED_TABLE

#define ATT_PARTITIONED_TABLE   0x0100

Definition at line 337 of file tablecmds.c.

◆ ATT_SEQUENCE

#define ATT_SEQUENCE   0x0080

Definition at line 336 of file tablecmds.c.

◆ ATT_TABLE

#define ATT_TABLE   0x0001

Definition at line 329 of file tablecmds.c.

◆ ATT_VIEW

#define ATT_VIEW   0x0002

Definition at line 330 of file tablecmds.c.

◆ child_dependency_type

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

Definition at line 366 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 354 of file tablecmds.c.

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

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

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

Function Documentation

◆ add_column_collation_dependency()

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

Definition at line 7709 of file tablecmds.c.

7710{
7711 ObjectAddress myself,
7712 referenced;
7713
7714 /* We know the default collation is pinned, so don't bother recording it */
7715 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7716 {
7717 myself.classId = RelationRelationId;
7718 myself.objectId = relid;
7719 myself.objectSubId = attnum;
7720 referenced.classId = CollationRelationId;
7721 referenced.objectId = collid;
7722 referenced.objectSubId = 0;
7723 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7724 }
7725}
#define OidIsValid(objectId)
Definition: c.h:777
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 7691 of file tablecmds.c.

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

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

10721{
10722 ObjectAddress address;
10723 Oid constrOid;
10724 char *conname;
10725 bool conislocal;
10726 int16 coninhcount;
10727 bool connoinherit;
10728
10729 /*
10730 * Verify relkind for each referenced partition. At the top level, this
10731 * is redundant with a previous check, but we need it when recursing.
10732 */
10733 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10734 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10735 ereport(ERROR,
10736 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10737 errmsg("referenced relation \"%s\" is not a table",
10738 RelationGetRelationName(pkrel))));
10739
10740 /*
10741 * Caller supplies us with a constraint name; however, it may be used in
10742 * this partition, so come up with a different one in that case. Unless
10743 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10744 * supplied name with an underscore and digit(s) appended.
10745 */
10747 RelationGetRelid(rel),
10748 constraintname))
10749 conname = ChooseConstraintName(constraintname,
10750 NULL,
10751 "",
10753 else
10754 conname = constraintname;
10755
10756 if (fkconstraint->conname == NULL)
10757 fkconstraint->conname = pstrdup(conname);
10758
10759 if (OidIsValid(parentConstr))
10760 {
10761 conislocal = false;
10762 coninhcount = 1;
10763 connoinherit = false;
10764 }
10765 else
10766 {
10767 conislocal = true;
10768 coninhcount = 0;
10769
10770 /*
10771 * always inherit for partitioned tables, never for legacy inheritance
10772 */
10773 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10774 }
10775
10776 /*
10777 * Record the FK constraint in pg_constraint.
10778 */
10779 constrOid = CreateConstraintEntry(conname,
10781 CONSTRAINT_FOREIGN,
10782 fkconstraint->deferrable,
10783 fkconstraint->initdeferred,
10784 fkconstraint->is_enforced,
10785 fkconstraint->initially_valid,
10786 parentConstr,
10787 RelationGetRelid(rel),
10788 fkattnum,
10789 numfks,
10790 numfks,
10791 InvalidOid, /* not a domain constraint */
10792 indexOid,
10793 RelationGetRelid(pkrel),
10794 pkattnum,
10795 pfeqoperators,
10796 ppeqoperators,
10797 ffeqoperators,
10798 numfks,
10799 fkconstraint->fk_upd_action,
10800 fkconstraint->fk_del_action,
10801 fkdelsetcols,
10802 numfkdelsetcols,
10803 fkconstraint->fk_matchtype,
10804 NULL, /* no exclusion constraint */
10805 NULL, /* no check constraint */
10806 NULL,
10807 conislocal, /* islocal */
10808 coninhcount, /* inhcount */
10809 connoinherit, /* conNoInherit */
10810 with_period, /* conPeriod */
10811 is_internal); /* is_internal */
10812
10813 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10814
10815 /*
10816 * In partitioning cases, create the dependency entries for this
10817 * constraint. (For non-partitioned cases, relevant entries were created
10818 * by CreateConstraintEntry.)
10819 *
10820 * On the referenced side, we need the constraint to have an internal
10821 * dependency on its parent constraint; this means that this constraint
10822 * cannot be dropped on its own -- only through the parent constraint. It
10823 * also means the containing partition cannot be dropped on its own, but
10824 * it can be detached, at which point this dependency is removed (after
10825 * verifying that no rows are referenced via this FK.)
10826 *
10827 * When processing the referencing side, we link the constraint via the
10828 * special partitioning dependencies: the parent constraint is the primary
10829 * dependent, and the partition on which the foreign key exists is the
10830 * secondary dependency. That way, this constraint is dropped if either
10831 * of these objects is.
10832 *
10833 * Note that this is only necessary for the subsidiary pg_constraint rows
10834 * in partitions; the topmost row doesn't need any of this.
10835 */
10836 if (OidIsValid(parentConstr))
10837 {
10838 ObjectAddress referenced;
10839
10840 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10841
10842 Assert(fkside != addFkBothSides);
10843 if (fkside == addFkReferencedSide)
10844 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10845 else
10846 {
10847 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10848 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10849 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10850 }
10851 }
10852
10853 /* make new constraint visible, in case we add more */
10855
10856 return address;
10857}
int16_t int16
Definition: c.h:536
@ 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:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:150
Assert(PointerIsAligned(start, uint64))
char * pstrdup(const char *in)
Definition: mcxt.c:1759
#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:515
#define RelationGetRelationName(relation)
Definition: rel.h:549
#define RelationGetNamespace(relation)
Definition: rel.h:556
bool initdeferred
Definition: parsenodes.h:2836
char fk_upd_action
Definition: parsenodes.h:2870
bool is_enforced
Definition: parsenodes.h:2837
char fk_matchtype
Definition: parsenodes.h:2869
bool initially_valid
Definition: parsenodes.h:2839
bool deferrable
Definition: parsenodes.h:2835
char * conname
Definition: parsenodes.h:2834
char fk_del_action
Definition: parsenodes.h:2871
Form_pg_class rd_rel
Definition: rel.h:111
void CommandCounterIncrement(void)
Definition: xact.c:1101

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

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

◆ addFkRecurseReferenced()

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

Definition at line 10892 of file tablecmds.c.

10901{
10902 Oid deleteTriggerOid = InvalidOid,
10903 updateTriggerOid = InvalidOid;
10904
10907
10908 /*
10909 * Create action triggers to enforce the constraint, or skip them if the
10910 * constraint is NOT ENFORCED.
10911 */
10912 if (fkconstraint->is_enforced)
10914 RelationGetRelid(pkrel),
10915 fkconstraint,
10916 parentConstr, indexOid,
10917 parentDelTrigger, parentUpdTrigger,
10918 &deleteTriggerOid, &updateTriggerOid);
10919
10920 /*
10921 * If the referenced table is partitioned, recurse on ourselves to handle
10922 * each partition. We need one pg_constraint row created for each
10923 * partition in addition to the pg_constraint row for the parent table.
10924 */
10925 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10926 {
10927 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10928
10929 for (int i = 0; i < pd->nparts; i++)
10930 {
10931 Relation partRel;
10932 AttrMap *map;
10933 AttrNumber *mapped_pkattnum;
10934 Oid partIndexId;
10935 ObjectAddress address;
10936
10937 /* XXX would it be better to acquire these locks beforehand? */
10938 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10939
10940 /*
10941 * Map the attribute numbers in the referenced side of the FK
10942 * definition to match the partition's column layout.
10943 */
10945 RelationGetDescr(pkrel),
10946 false);
10947 if (map)
10948 {
10949 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10950 for (int j = 0; j < numfks; j++)
10951 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10952 }
10953 else
10954 mapped_pkattnum = pkattnum;
10955
10956 /* Determine the index to use at this level */
10957 partIndexId = index_get_partition(partRel, indexOid);
10958 if (!OidIsValid(partIndexId))
10959 elog(ERROR, "index for %u not found in partition %s",
10960 indexOid, RelationGetRelationName(partRel));
10961
10962 /* Create entry at this level ... */
10964 fkconstraint->conname, fkconstraint, rel,
10965 partRel, partIndexId, parentConstr,
10966 numfks, mapped_pkattnum,
10967 fkattnum, pfeqoperators, ppeqoperators,
10968 ffeqoperators, numfkdelsetcols,
10969 fkdelsetcols, true, with_period);
10970 /* ... and recurse to our children */
10971 addFkRecurseReferenced(fkconstraint, rel, partRel,
10972 partIndexId, address.objectId, numfks,
10973 mapped_pkattnum, fkattnum,
10974 pfeqoperators, ppeqoperators, ffeqoperators,
10975 numfkdelsetcols, fkdelsetcols,
10976 old_check_ok,
10977 deleteTriggerOid, updateTriggerOid,
10978 with_period);
10979
10980 /* Done -- clean up (but keep the lock) */
10981 table_close(partRel, NoLock);
10982 if (map)
10983 {
10984 pfree(mapped_pkattnum);
10985 free_attrmap(map);
10986 }
10987 }
10988 }
10989}
void free_attrmap(AttrMap *map)
Definition: attmap.c:56
AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:261
int16 AttrNumber
Definition: attnum.h:21
#define elog(elevel,...)
Definition: elog.h:226
int j
Definition: isn.c:78
int i
Definition: isn.c:77
bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)
Definition: lmgr.c:334
#define NoLock
Definition: lockdefs.h:34
#define ShareRowExclusiveLock
Definition: lockdefs.h:41
void pfree(void *pointer)
Definition: mcxt.c:1594
void * palloc(Size size)
Definition: mcxt.c:1365
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:541
Definition: attmap.h:35
AttrNumber * attnums
Definition: attmap.h:36
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13849
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:10714
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:10892

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

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

◆ addFkRecurseReferencing()

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

Definition at line 11030 of file tablecmds.c.

11038{
11039 Oid insertTriggerOid = InvalidOid,
11040 updateTriggerOid = InvalidOid;
11041
11042 Assert(OidIsValid(parentConstr));
11045
11046 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11047 ereport(ERROR,
11048 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11049 errmsg("foreign key constraints are not supported on foreign tables")));
11050
11051 /*
11052 * Add check triggers if the constraint is ENFORCED, and if needed,
11053 * schedule them to be checked in Phase 3.
11054 *
11055 * If the relation is partitioned, drill down to do it to its partitions.
11056 */
11057 if (fkconstraint->is_enforced)
11059 RelationGetRelid(pkrel),
11060 fkconstraint,
11061 parentConstr,
11062 indexOid,
11063 parentInsTrigger, parentUpdTrigger,
11064 &insertTriggerOid, &updateTriggerOid);
11065
11066 if (rel->rd_rel->relkind == RELKIND_RELATION)
11067 {
11068 /*
11069 * Tell Phase 3 to check that the constraint is satisfied by existing
11070 * rows. We can skip this during table creation, when constraint is
11071 * specified as NOT ENFORCED, or when requested explicitly by
11072 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11073 * recreating a constraint following a SET DATA TYPE operation that
11074 * did not impugn its validity.
11075 */
11076 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11077 fkconstraint->is_enforced)
11078 {
11079 NewConstraint *newcon;
11080 AlteredTableInfo *tab;
11081
11082 tab = ATGetQueueEntry(wqueue, rel);
11083
11084 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11085 newcon->name = get_constraint_name(parentConstr);
11086 newcon->contype = CONSTR_FOREIGN;
11087 newcon->refrelid = RelationGetRelid(pkrel);
11088 newcon->refindid = indexOid;
11089 newcon->conid = parentConstr;
11090 newcon->conwithperiod = fkconstraint->fk_with_period;
11091 newcon->qual = (Node *) fkconstraint;
11092
11093 tab->constraints = lappend(tab->constraints, newcon);
11094 }
11095 }
11096 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11097 {
11099 Relation trigrel;
11100
11101 /*
11102 * Triggers of the foreign keys will be manipulated a bunch of times
11103 * in the loop below. To avoid repeatedly opening/closing the trigger
11104 * catalog relation, we open it here and pass it to the subroutines
11105 * called below.
11106 */
11107 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11108
11109 /*
11110 * Recurse to take appropriate action on each partition; either we
11111 * find an existing constraint to reparent to ours, or we create a new
11112 * one.
11113 */
11114 for (int i = 0; i < pd->nparts; i++)
11115 {
11116 Relation partition = table_open(pd->oids[i], lockmode);
11117 List *partFKs;
11118 AttrMap *attmap;
11119 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11120 bool attached;
11121 ObjectAddress address;
11122
11123 CheckAlterTableIsSafe(partition);
11124
11125 attmap = build_attrmap_by_name(RelationGetDescr(partition),
11126 RelationGetDescr(rel),
11127 false);
11128 for (int j = 0; j < numfks; j++)
11129 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11130
11131 /* Check whether an existing constraint can be repurposed */
11132 partFKs = copyObject(RelationGetFKeyList(partition));
11133 attached = false;
11134 foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11135 {
11137 fk,
11138 partition,
11139 parentConstr,
11140 numfks,
11141 mapped_fkattnum,
11142 pkattnum,
11143 pfeqoperators,
11144 insertTriggerOid,
11145 updateTriggerOid,
11146 trigrel))
11147 {
11148 attached = true;
11149 break;
11150 }
11151 }
11152 if (attached)
11153 {
11154 table_close(partition, NoLock);
11155 continue;
11156 }
11157
11158 /*
11159 * No luck finding a good constraint to reuse; create our own.
11160 */
11162 fkconstraint->conname, fkconstraint,
11163 partition, pkrel, indexOid, parentConstr,
11164 numfks, pkattnum,
11165 mapped_fkattnum, pfeqoperators,
11166 ppeqoperators, ffeqoperators,
11167 numfkdelsetcols, fkdelsetcols, true,
11168 with_period);
11169
11170 /* call ourselves to finalize the creation and we're done */
11171 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11172 indexOid,
11173 address.objectId,
11174 numfks,
11175 pkattnum,
11176 mapped_fkattnum,
11177 pfeqoperators,
11178 ppeqoperators,
11179 ffeqoperators,
11180 numfkdelsetcols,
11181 fkdelsetcols,
11182 old_check_ok,
11183 lockmode,
11184 insertTriggerOid,
11185 updateTriggerOid,
11186 with_period);
11187
11188 table_close(partition, NoLock);
11189 }
11190
11191 table_close(trigrel, RowExclusiveLock);
11192 }
11193}
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:1174
void * palloc0(Size size)
Definition: mcxt.c:1395
#define copyObject(obj)
Definition: nodes.h:232
@ CONSTR_FOREIGN
Definition: parsenodes.h:2809
#define INDEX_MAX_KEYS
#define foreach_node(type, var, lst)
Definition: pg_list.h:496
List * RelationGetFKeyList(Relation relation)
Definition: relcache.c:4731
List * constraints
Definition: tablecmds.c:188
bool fk_with_period
Definition: parsenodes.h:2867
bool skip_validation
Definition: parsenodes.h:2838
Definition: pg_list.h:54
char * name
Definition: tablecmds.c:217
ConstrType contype
Definition: tablecmds.c:218
bool conwithperiod
Definition: tablecmds.c:221
Node * qual
Definition: tablecmds.c:223
Definition: nodes.h:135
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition: tablecmds.c:6554
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:11030
static void CheckAlterTableIsSafe(Relation rel)
Definition: tablecmds.c:4441
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13984
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:11686

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

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

◆ alter_table_type_to_string()

static const char * alter_table_type_to_string ( AlterTableType  cmdtype)
static

Definition at line 6588 of file tablecmds.c.

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

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

Referenced by ATSimplePermissions().

◆ AlterConstrDeferrabilityRecurse()

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

Definition at line 12804 of file tablecmds.c.

12808{
12809 Form_pg_constraint currcon;
12810 Oid conoid;
12811 ScanKeyData pkey;
12812 SysScanDesc pscan;
12813 HeapTuple childtup;
12814
12815 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12816 conoid = currcon->oid;
12817
12818 ScanKeyInit(&pkey,
12819 Anum_pg_constraint_conparentid,
12820 BTEqualStrategyNumber, F_OIDEQ,
12821 ObjectIdGetDatum(conoid));
12822
12823 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12824 true, NULL, 1, &pkey);
12825
12826 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12827 {
12828 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12829 Relation childrel;
12830
12831 childrel = table_open(childcon->conrelid, lockmode);
12832
12833 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12834 childtup, recurse, otherrelids, lockmode);
12835 table_close(childrel, NoLock);
12836 }
12837
12838 systable_endscan(pscan);
12839}
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:603
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:514
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:388
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
FormData_pg_constraint * Form_pg_constraint
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:262
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define BTEqualStrategyNumber
Definition: stratnum.h:31
static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12550

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

Referenced by ATExecAlterConstrDeferrability().

◆ AlterConstrEnforceabilityRecurse()

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

Definition at line 12755 of file tablecmds.c.

12763{
12764 Form_pg_constraint currcon;
12765 Oid conoid;
12766 ScanKeyData pkey;
12767 SysScanDesc pscan;
12768 HeapTuple childtup;
12769
12770 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12771 conoid = currcon->oid;
12772
12773 ScanKeyInit(&pkey,
12774 Anum_pg_constraint_conparentid,
12775 BTEqualStrategyNumber, F_OIDEQ,
12776 ObjectIdGetDatum(conoid));
12777
12778 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12779 true, NULL, 1, &pkey);
12780
12781 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12782 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12783 pkrelid, childtup, lockmode,
12784 ReferencedParentDelTrigger,
12785 ReferencedParentUpdTrigger,
12786 ReferencingParentInsTrigger,
12787 ReferencingParentUpdTrigger);
12788
12789 systable_endscan(pscan);
12790}
static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
Definition: tablecmds.c:12404

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

Referenced by ATExecAlterConstrEnforceability().

◆ AlterConstrTriggerDeferrability()

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

Definition at line 12686 of file tablecmds.c.

12689{
12690 HeapTuple tgtuple;
12691 ScanKeyData tgkey;
12692 SysScanDesc tgscan;
12693
12694 ScanKeyInit(&tgkey,
12695 Anum_pg_trigger_tgconstraint,
12696 BTEqualStrategyNumber, F_OIDEQ,
12697 ObjectIdGetDatum(conoid));
12698 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12699 NULL, 1, &tgkey);
12700 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12701 {
12702 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12703 Form_pg_trigger copy_tg;
12704 HeapTuple tgCopyTuple;
12705
12706 /*
12707 * Remember OIDs of other relation(s) involved in FK constraint.
12708 * (Note: it's likely that we could skip forcing a relcache inval for
12709 * other rels that don't have a trigger whose properties change, but
12710 * let's be conservative.)
12711 */
12712 if (tgform->tgrelid != RelationGetRelid(rel))
12713 *otherrelids = list_append_unique_oid(*otherrelids,
12714 tgform->tgrelid);
12715
12716 /*
12717 * Update enable status and deferrability of RI_FKey_noaction_del,
12718 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12719 * triggers, but not others; see createForeignKeyActionTriggers and
12720 * CreateFKCheckTrigger.
12721 */
12722 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12723 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12724 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12725 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12726 continue;
12727
12728 tgCopyTuple = heap_copytuple(tgtuple);
12729 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12730
12731 copy_tg->tgdeferrable = deferrable;
12732 copy_tg->tginitdeferred = initdeferred;
12733 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12734
12735 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12736
12737 heap_freetuple(tgCopyTuple);
12738 }
12739
12740 systable_endscan(tgscan);
12741}
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:778
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition: indexing.c:313
List * list_append_unique_oid(List *list, Oid datum)
Definition: list.c:1380
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:80
ItemPointerData t_self
Definition: htup.h:65

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

Referenced by ATExecAlterConstrDeferrability().

◆ AlterConstrUpdateConstraintEntry()

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

Definition at line 12846 of file tablecmds.c.

12848{
12849 HeapTuple copyTuple;
12850 Form_pg_constraint copy_con;
12851
12852 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12853 cmdcon->alterInheritability);
12854
12855 copyTuple = heap_copytuple(contuple);
12856 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12857
12858 if (cmdcon->alterEnforceability)
12859 {
12860 copy_con->conenforced = cmdcon->is_enforced;
12861
12862 /*
12863 * NB: The convalidated status is irrelevant when the constraint is
12864 * set to NOT ENFORCED, but for consistency, it should still be set
12865 * appropriately. Similarly, if the constraint is later changed to
12866 * ENFORCED, validation will be performed during phase 3, so it makes
12867 * sense to mark it as valid in that case.
12868 */
12869 copy_con->convalidated = cmdcon->is_enforced;
12870 }
12871 if (cmdcon->alterDeferrability)
12872 {
12873 copy_con->condeferrable = cmdcon->deferrable;
12874 copy_con->condeferred = cmdcon->initdeferred;
12875 }
12876 if (cmdcon->alterInheritability)
12877 copy_con->connoinherit = cmdcon->noinherit;
12878
12879 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12880 InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12881
12882 /* Make new constraint flags visible to others */
12883 CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12884
12885 heap_freetuple(copyTuple);
12886}
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1687

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

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

◆ AlterIndexNamespaces()

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

Definition at line 19121 of file tablecmds.c.

19123{
19124 List *indexList;
19125 ListCell *l;
19126
19127 indexList = RelationGetIndexList(rel);
19128
19129 foreach(l, indexList)
19130 {
19131 Oid indexOid = lfirst_oid(l);
19132 ObjectAddress thisobj;
19133
19134 thisobj.classId = RelationRelationId;
19135 thisobj.objectId = indexOid;
19136 thisobj.objectSubId = 0;
19137
19138 /*
19139 * Note: currently, the index will not have its own dependency on the
19140 * namespace, so we don't need to do changeDependencyFor(). There's no
19141 * row type in pg_type, either.
19142 *
19143 * XXX this objsMoved test may be pointless -- surely we have a single
19144 * dependency link from a relation to each index?
19145 */
19146 if (!object_address_present(&thisobj, objsMoved))
19147 {
19148 AlterRelationNamespaceInternal(classRel, indexOid,
19149 oldNspOid, newNspOid,
19150 false, objsMoved);
19151 add_exact_object_address(&thisobj, objsMoved);
19152 }
19153 }
19154
19155 list_free(indexList);
19156}
bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs)
Definition: dependency.c:2721
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2661
void list_free(List *list)
Definition: list.c:1546
#define lfirst_oid(lc)
Definition: pg_list.h:174
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4836
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19044

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

19048{
19049 HeapTuple classTup;
19050 Form_pg_class classForm;
19051 ObjectAddress thisobj;
19052 bool already_done = false;
19053
19054 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19055 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19056 if (!HeapTupleIsValid(classTup))
19057 elog(ERROR, "cache lookup failed for relation %u", relOid);
19058 classForm = (Form_pg_class) GETSTRUCT(classTup);
19059
19060 Assert(classForm->relnamespace == oldNspOid);
19061
19062 thisobj.classId = RelationRelationId;
19063 thisobj.objectId = relOid;
19064 thisobj.objectSubId = 0;
19065
19066 /*
19067 * If the object has already been moved, don't move it again. If it's
19068 * already in the right place, don't move it, but still fire the object
19069 * access hook.
19070 */
19071 already_done = object_address_present(&thisobj, objsMoved);
19072 if (!already_done && oldNspOid != newNspOid)
19073 {
19074 ItemPointerData otid = classTup->t_self;
19075
19076 /* check for duplicate name (more friendly than unique-index failure) */
19077 if (get_relname_relid(NameStr(classForm->relname),
19078 newNspOid) != InvalidOid)
19079 ereport(ERROR,
19080 (errcode(ERRCODE_DUPLICATE_TABLE),
19081 errmsg("relation \"%s\" already exists in schema \"%s\"",
19082 NameStr(classForm->relname),
19083 get_namespace_name(newNspOid))));
19084
19085 /* classTup is a copy, so OK to scribble on */
19086 classForm->relnamespace = newNspOid;
19087
19088 CatalogTupleUpdate(classRel, &otid, classTup);
19089 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19090
19091
19092 /* Update dependency on schema if caller said so */
19093 if (hasDependEntry &&
19094 changeDependencyFor(RelationRelationId,
19095 relOid,
19096 NamespaceRelationId,
19097 oldNspOid,
19098 newNspOid) != 1)
19099 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19100 NameStr(classForm->relname));
19101 }
19102 else
19103 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19104 if (!already_done)
19105 {
19106 add_exact_object_address(&thisobj, objsMoved);
19107
19108 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19109 }
19110
19111 heap_freetuple(classTup);
19112}
#define NameStr(name)
Definition: c.h:754
void UnlockTuple(Relation relation, const ItemPointerData *tid, LOCKMODE lockmode)
Definition: lmgr.c:601
#define InplaceUpdateTupleLock
Definition: lockdefs.h:48
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3533
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:2052
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:399

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

19169{
19170 Relation depRel;
19171 SysScanDesc scan;
19172 ScanKeyData key[2];
19173 HeapTuple tup;
19174
19175 /*
19176 * SERIAL sequences are those having an auto dependency on one of the
19177 * table's columns (we don't care *which* column, exactly).
19178 */
19179 depRel = table_open(DependRelationId, AccessShareLock);
19180
19181 ScanKeyInit(&key[0],
19182 Anum_pg_depend_refclassid,
19183 BTEqualStrategyNumber, F_OIDEQ,
19184 ObjectIdGetDatum(RelationRelationId));
19185 ScanKeyInit(&key[1],
19186 Anum_pg_depend_refobjid,
19187 BTEqualStrategyNumber, F_OIDEQ,
19189 /* we leave refobjsubid unspecified */
19190
19191 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19192 NULL, 2, key);
19193
19194 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19195 {
19196 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19197 Relation seqRel;
19198
19199 /* skip dependencies other than auto dependencies on columns */
19200 if (depForm->refobjsubid == 0 ||
19201 depForm->classid != RelationRelationId ||
19202 depForm->objsubid != 0 ||
19203 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19204 continue;
19205
19206 /* Use relation_open just in case it's an index */
19207 seqRel = relation_open(depForm->objid, lockmode);
19208
19209 /* skip non-sequence relations */
19210 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19211 {
19212 /* No need to keep the lock */
19213 relation_close(seqRel, lockmode);
19214 continue;
19215 }
19216
19217 /* Fix the pg_class and pg_depend entries */
19218 AlterRelationNamespaceInternal(classRel, depForm->objid,
19219 oldNspOid, newNspOid,
19220 true, objsMoved);
19221
19222 /*
19223 * Sequences used to have entries in pg_type, but no longer do. If we
19224 * ever re-instate that, we'll need to move the pg_type entry to the
19225 * new namespace, too (using AlterTypeNamespaceInternal).
19226 */
19227 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19228
19229 /* Now we can close it. Keep the lock till end of transaction. */
19230 relation_close(seqRel, NoLock);
19231 }
19232
19233 systable_endscan(scan);
19234
19236}
@ 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:509
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 4526 of file tablecmds.c.

4528{
4529 Relation rel;
4530
4531 /* Caller is required to provide an adequate lock. */
4532 rel = relation_open(context->relid, NoLock);
4533
4535
4536 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4537}
#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:4862

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

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4600 of file tablecmds.c.

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

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

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

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4467 of file tablecmds.c.

4468{
4469 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4470 stmt->missing_ok ? RVR_MISSING_OK : 0,
4472 stmt);
4473}
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:440
@ RVR_MISSING_OK
Definition: namespace.h:90
static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:19576

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

Referenced by ProcessUtilitySlow().

◆ AlterTableMoveAll()

Oid AlterTableMoveAll ( AlterTableMoveAllStmt stmt)

Definition at line 16977 of file tablecmds.c.

16978{
16979 List *relations = NIL;
16980 ListCell *l;
16981 ScanKeyData key[1];
16982 Relation rel;
16983 TableScanDesc scan;
16984 HeapTuple tuple;
16985 Oid orig_tablespaceoid;
16986 Oid new_tablespaceoid;
16987 List *role_oids = roleSpecsToIds(stmt->roles);
16988
16989 /* Ensure we were not asked to move something we can't */
16990 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16991 stmt->objtype != OBJECT_MATVIEW)
16992 ereport(ERROR,
16993 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16994 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16995
16996 /* Get the orig and new tablespace OIDs */
16997 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16998 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16999
17000 /* Can't move shared relations in to or out of pg_global */
17001 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17002 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
17003 new_tablespaceoid == GLOBALTABLESPACE_OID)
17004 ereport(ERROR,
17005 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17006 errmsg("cannot move relations in to or out of pg_global tablespace")));
17007
17008 /*
17009 * Must have CREATE rights on the new tablespace, unless it is the
17010 * database default tablespace (which all users implicitly have CREATE
17011 * rights on).
17012 */
17013 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
17014 {
17015 AclResult aclresult;
17016
17017 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
17018 ACL_CREATE);
17019 if (aclresult != ACLCHECK_OK)
17021 get_tablespace_name(new_tablespaceoid));
17022 }
17023
17024 /*
17025 * Now that the checks are done, check if we should set either to
17026 * InvalidOid because it is our database's default tablespace.
17027 */
17028 if (orig_tablespaceoid == MyDatabaseTableSpace)
17029 orig_tablespaceoid = InvalidOid;
17030
17031 if (new_tablespaceoid == MyDatabaseTableSpace)
17032 new_tablespaceoid = InvalidOid;
17033
17034 /* no-op */
17035 if (orig_tablespaceoid == new_tablespaceoid)
17036 return new_tablespaceoid;
17037
17038 /*
17039 * Walk the list of objects in the tablespace and move them. This will
17040 * only find objects in our database, of course.
17041 */
17042 ScanKeyInit(&key[0],
17043 Anum_pg_class_reltablespace,
17044 BTEqualStrategyNumber, F_OIDEQ,
17045 ObjectIdGetDatum(orig_tablespaceoid));
17046
17047 rel = table_open(RelationRelationId, AccessShareLock);
17048 scan = table_beginscan_catalog(rel, 1, key);
17049 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17050 {
17051 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17052 Oid relOid = relForm->oid;
17053
17054 /*
17055 * Do not move objects in pg_catalog as part of this, if an admin
17056 * really wishes to do so, they can issue the individual ALTER
17057 * commands directly.
17058 *
17059 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17060 * (TOAST will be moved with the main table).
17061 */
17062 if (IsCatalogNamespace(relForm->relnamespace) ||
17063 relForm->relisshared ||
17064 isAnyTempNamespace(relForm->relnamespace) ||
17065 IsToastNamespace(relForm->relnamespace))
17066 continue;
17067
17068 /* Only move the object type requested */
17069 if ((stmt->objtype == OBJECT_TABLE &&
17070 relForm->relkind != RELKIND_RELATION &&
17071 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17072 (stmt->objtype == OBJECT_INDEX &&
17073 relForm->relkind != RELKIND_INDEX &&
17074 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17075 (stmt->objtype == OBJECT_MATVIEW &&
17076 relForm->relkind != RELKIND_MATVIEW))
17077 continue;
17078
17079 /* Check if we are only moving objects owned by certain roles */
17080 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17081 continue;
17082
17083 /*
17084 * Handle permissions-checking here since we are locking the tables
17085 * and also to avoid doing a bunch of work only to fail part-way. Note
17086 * that permissions will also be checked by AlterTableInternal().
17087 *
17088 * Caller must be considered an owner on the table to move it.
17089 */
17090 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17092 NameStr(relForm->relname));
17093
17094 if (stmt->nowait &&
17096 ereport(ERROR,
17097 (errcode(ERRCODE_OBJECT_IN_USE),
17098 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17099 get_namespace_name(relForm->relnamespace),
17100 NameStr(relForm->relname))));
17101 else
17103
17104 /* Add to our list of objects to move */
17105 relations = lappend_oid(relations, relOid);
17106 }
17107
17108 table_endscan(scan);
17110
17111 if (relations == NIL)
17113 (errcode(ERRCODE_NO_DATA_FOUND),
17114 errmsg("no matching relations in tablespace \"%s\" found",
17115 orig_tablespaceoid == InvalidOid ? "(database default)" :
17116 get_tablespace_name(orig_tablespaceoid))));
17117
17118 /* Everything is locked, loop through and move all of the relations. */
17119 foreach(l, relations)
17120 {
17121 List *cmds = NIL;
17123
17125 cmd->name = stmt->new_tablespacename;
17126
17127 cmds = lappend(cmds, cmd);
17128
17130 /* OID is set by AlterTableInternal */
17131 AlterTableInternal(lfirst_oid(l), cmds, false);
17133 }
17134
17135 return new_tablespaceoid;
17136}
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:2652
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3834
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4088
char * get_tablespace_name(Oid spc_oid)
Definition: tablespace.c:1472
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
Definition: tablespace.c:1426
bool IsToastNamespace(Oid namespaceId)
Definition: catalog.c:261
bool IsCatalogNamespace(Oid namespaceId)
Definition: catalog.c:243
#define NOTICE
Definition: elog.h:35
void EventTriggerAlterTableStart(Node *parsetree)
void EventTriggerAlterTableEnd(void)
Oid MyDatabaseTableSpace
Definition: globals.c:96
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1361
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:2170
Oid GetUserId(void)
Definition: miscinit.c:469
bool isAnyTempNamespace(Oid namespaceId)
Definition: namespace.c:3757
#define makeNode(_type_)
Definition: nodes.h:161
ObjectType get_relkind_objtype(char relkind)
@ OBJECT_MATVIEW
Definition: parsenodes.h:2348
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2367
@ OBJECT_INDEX
Definition: parsenodes.h:2345
@ OBJECT_TABLE
Definition: parsenodes.h:2366
#define ACL_CREATE
Definition: parsenodes.h:85
@ ForwardScanDirection
Definition: sdir.h:28
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, ScanKeyData *key)
Definition: tableam.c:113
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:985
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition: tablecmds.c:4555
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 18936 of file tablecmds.c.

18937{
18938 Relation rel;
18939 Oid relid;
18940 Oid oldNspOid;
18941 Oid nspOid;
18942 RangeVar *newrv;
18943 ObjectAddresses *objsMoved;
18944 ObjectAddress myself;
18945
18947 stmt->missing_ok ? RVR_MISSING_OK : 0,
18949 stmt);
18950
18951 if (!OidIsValid(relid))
18952 {
18954 (errmsg("relation \"%s\" does not exist, skipping",
18955 stmt->relation->relname)));
18956 return InvalidObjectAddress;
18957 }
18958
18959 rel = relation_open(relid, NoLock);
18960
18961 oldNspOid = RelationGetNamespace(rel);
18962
18963 /* If it's an owned sequence, disallow moving it by itself. */
18964 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18965 {
18966 Oid tableId;
18967 int32 colId;
18968
18969 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18970 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18971 ereport(ERROR,
18972 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18973 errmsg("cannot move an owned sequence into another schema"),
18974 errdetail("Sequence \"%s\" is linked to table \"%s\".",
18976 get_rel_name(tableId))));
18977 }
18978
18979 /* Get and lock schema OID and check its permissions. */
18980 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18981 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18982
18983 /* common checks on switching namespaces */
18984 CheckSetNamespace(oldNspOid, nspOid);
18985
18986 objsMoved = new_object_addresses();
18987 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18988 free_object_addresses(objsMoved);
18989
18990 ObjectAddressSet(myself, RelationRelationId, relid);
18991
18992 if (oldschema)
18993 *oldschema = oldNspOid;
18994
18995 /* close rel, but keep lock until commit */
18996 relation_close(rel, NoLock);
18997
18998 return myself;
18999}
int32_t int32
Definition: c.h:537
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2615
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2901
int errdetail(const char *fmt,...)
Definition: elog.c:1216
char * get_rel_name(Oid relid)
Definition: lsyscache.c:2095
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:738
void CheckSetNamespace(Oid oldNspOid, Oid nspOid)
Definition: namespace.c:3529
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:19007

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

19009{
19010 Relation classRel;
19011
19012 Assert(objsMoved != NULL);
19013
19014 /* OK, modify the pg_class row and pg_depend entry */
19015 classRel = table_open(RelationRelationId, RowExclusiveLock);
19016
19017 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
19018 nspOid, true, objsMoved);
19019
19020 /* Fix the table's row type too, if it has one */
19021 if (OidIsValid(rel->rd_rel->reltype))
19022 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
19023 false, /* isImplicitArray */
19024 false, /* ignoreDependent */
19025 false, /* errorOnTableType */
19026 objsMoved);
19027
19028 /* Fix other dependent stuff */
19029 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
19030 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
19031 objsMoved, AccessExclusiveLock);
19032 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
19033 false, objsMoved);
19034
19035 table_close(classRel, RowExclusiveLock);
19036}
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:19166
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19121
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition: typecmds.c:4169

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

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

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

Referenced by ATAddCheckNNConstraint(), and ATExecAddConstraint().

◆ ATAddForeignKeyConstraint()

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

Definition at line 10059 of file tablecmds.c.

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

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

Referenced by ATExecAddConstraint().

◆ ATCheckPartitionsNotInUse()

static void ATCheckPartitionsNotInUse ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 6853 of file tablecmds.c.

6854{
6855 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6856 {
6857 List *inh;
6858 ListCell *cell;
6859
6860 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6861 /* first element is the parent rel; must ignore it */
6862 for_each_from(cell, inh, 1)
6863 {
6864 Relation childrel;
6865
6866 /* find_all_inheritors already got lock */
6867 childrel = table_open(lfirst_oid(cell), NoLock);
6868 CheckAlterTableIsSafe(childrel);
6869 table_close(childrel, NoLock);
6870 }
6871 list_free(inh);
6872 }
6873}
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 14671 of file tablecmds.c.

14672{
14673 Assert(expr != NULL);
14674
14675 for (;;)
14676 {
14677 /* only one varno, so no need to check that */
14678 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14679 return false;
14680 else if (IsA(expr, RelabelType))
14681 expr = (Node *) ((RelabelType *) expr)->arg;
14682 else if (IsA(expr, CoerceToDomain))
14683 {
14684 CoerceToDomain *d = (CoerceToDomain *) expr;
14685
14687 return true;
14688 expr = (Node *) d->arg;
14689 }
14690 else if (IsA(expr, FuncExpr))
14691 {
14692 FuncExpr *f = (FuncExpr *) expr;
14693
14694 switch (f->funcid)
14695 {
14696 case F_TIMESTAMPTZ_TIMESTAMP:
14697 case F_TIMESTAMP_TIMESTAMPTZ:
14699 return true;
14700 else
14701 expr = linitial(f->args);
14702 break;
14703 default:
14704 return true;
14705 }
14706 }
14707 else
14708 return true;
14709 }
14710}
bool TimestampTimestampTzRequiresRewrite(void)
Definition: timestamp.c:6417
void * arg
#define linitial(l)
Definition: pg_list.h:178
Oid funcid
Definition: primnodes.h:782
List * args
Definition: primnodes.h:800
Definition: primnodes.h:262
bool DomainHasConstraints(Oid type_id)
Definition: typcache.c:1488

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

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

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

21932{
21933 List *constraints;
21934 ListCell *cell;
21935
21936 constraints = GetParentedForeignKeyRefs(partition);
21937
21938 foreach(cell, constraints)
21939 {
21940 Oid constrOid = lfirst_oid(cell);
21941 HeapTuple tuple;
21942 Form_pg_constraint constrForm;
21943 Relation rel;
21944 Trigger trig = {0};
21945
21946 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21947 if (!HeapTupleIsValid(tuple))
21948 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21949 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21950
21951 Assert(OidIsValid(constrForm->conparentid));
21952 Assert(constrForm->confrelid == RelationGetRelid(partition));
21953
21954 /* prevent data changes into the referencing table until commit */
21955 rel = table_open(constrForm->conrelid, ShareLock);
21956
21957 trig.tgoid = InvalidOid;
21958 trig.tgname = NameStr(constrForm->conname);
21960 trig.tgisinternal = true;
21961 trig.tgconstrrelid = RelationGetRelid(partition);
21962 trig.tgconstrindid = constrForm->conindid;
21963 trig.tgconstraint = constrForm->oid;
21964 trig.tgdeferrable = false;
21965 trig.tginitdeferred = false;
21966 /* we needn't fill in remaining fields */
21967
21968 RI_PartitionRemove_Check(&trig, rel, partition);
21969
21970 ReleaseSysCache(tuple);
21971
21972 table_close(rel, NoLock);
21973 }
21974}
#define ShareLock
Definition: lockdefs.h:40
void RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1811
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:21878
#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 19449 of file tablecmds.c.

19451{
19452 ListCell *cur_item;
19453
19454 foreach(cur_item, on_commits)
19455 {
19456 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19457
19458 if (!isCommit && oc->creating_subid == mySubid)
19459 {
19460 /* cur_item must be removed */
19462 pfree(oc);
19463 }
19464 else
19465 {
19466 /* cur_item must be preserved */
19467 if (oc->creating_subid == mySubid)
19468 oc->creating_subid = parentSubid;
19469 if (oc->deleting_subid == mySubid)
19470 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19471 }
19472 }
19473}
#define InvalidSubTransactionId
Definition: c.h:666
#define foreach_delete_current(lst, var_or_cell)
Definition: pg_list.h:391
SubTransactionId creating_subid
Definition: tablecmds.c:128
SubTransactionId deleting_subid
Definition: tablecmds.c:129
static List * on_commits
Definition: tablecmds.c:132

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

19418{
19419 ListCell *cur_item;
19420
19421 foreach(cur_item, on_commits)
19422 {
19423 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19424
19425 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19427 {
19428 /* cur_item must be removed */
19430 pfree(oc);
19431 }
19432 else
19433 {
19434 /* cur_item must be preserved */
19437 }
19438 }
19439}

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

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

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

9791{
9793
9794 Assert(IsA(newConstraint, Constraint));
9795
9796 /*
9797 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9798 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9799 * parse_utilcmd.c).
9800 */
9801 switch (newConstraint->contype)
9802 {
9803 case CONSTR_CHECK:
9804 case CONSTR_NOTNULL:
9805 address =
9806 ATAddCheckNNConstraint(wqueue, tab, rel,
9807 newConstraint, recurse, false, is_readd,
9808 lockmode);
9809 break;
9810
9811 case CONSTR_FOREIGN:
9812
9813 /*
9814 * Assign or validate constraint name
9815 */
9816 if (newConstraint->conname)
9817 {
9819 RelationGetRelid(rel),
9820 newConstraint->conname))
9821 ereport(ERROR,
9823 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9824 newConstraint->conname,
9826 }
9827 else
9828 newConstraint->conname =
9831 "fkey",
9833 NIL);
9834
9835 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9836 newConstraint,
9837 recurse, false,
9838 lockmode);
9839 break;
9840
9841 default:
9842 elog(ERROR, "unrecognized constraint type: %d",
9843 (int) newConstraint->contype);
9844 }
9845
9846 return address;
9847}
@ CONSTR_CHECK
Definition: parsenodes.h:2805
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition: tablecmds.c:9862
static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:10059

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

8234{
8235 Relation attrelation;
8236 HeapTuple tuple;
8237 Form_pg_attribute attTup;
8239 ObjectAddress address;
8240 ColumnDef *cdef = castNode(ColumnDef, def);
8241 bool ispartitioned;
8242
8243 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8244 if (ispartitioned && !recurse)
8245 ereport(ERROR,
8246 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8247 errmsg("cannot add identity to a column of only the partitioned table"),
8248 errhint("Do not specify the ONLY keyword.")));
8249
8250 if (rel->rd_rel->relispartition && !recursing)
8251 ereport(ERROR,
8252 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8253 errmsg("cannot add identity to a column of a partition"));
8254
8255 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8256
8257 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8258 if (!HeapTupleIsValid(tuple))
8259 ereport(ERROR,
8260 (errcode(ERRCODE_UNDEFINED_COLUMN),
8261 errmsg("column \"%s\" of relation \"%s\" does not exist",
8262 colName, RelationGetRelationName(rel))));
8263 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8264 attnum = attTup->attnum;
8265
8266 /* Can't alter a system attribute */
8267 if (attnum <= 0)
8268 ereport(ERROR,
8269 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8270 errmsg("cannot alter system column \"%s\"",
8271 colName)));
8272
8273 /*
8274 * Creating a column as identity implies NOT NULL, so adding the identity
8275 * to an existing column that is not NOT NULL would create a state that
8276 * cannot be reproduced without contortions.
8277 */
8278 if (!attTup->attnotnull)
8279 ereport(ERROR,
8280 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8281 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8282 colName, RelationGetRelationName(rel))));
8283
8284 /*
8285 * On the other hand, if a not-null constraint exists, then verify that
8286 * it's compatible.
8287 */
8288 if (attTup->attnotnull)
8289 {
8290 HeapTuple contup;
8291 Form_pg_constraint conForm;
8292
8294 attnum);
8295 if (!HeapTupleIsValid(contup))
8296 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8297 colName, RelationGetRelationName(rel));
8298
8299 conForm = (Form_pg_constraint) GETSTRUCT(contup);
8300 if (!conForm->convalidated)
8301 ereport(ERROR,
8302 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8303 errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8304 NameStr(conForm->conname), RelationGetRelationName(rel)),
8305 errhint("You might need to validate it using %s.",
8306 "ALTER TABLE ... VALIDATE CONSTRAINT"));
8307 }
8308
8309 if (attTup->attidentity)
8310 ereport(ERROR,
8311 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8312 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8313 colName, RelationGetRelationName(rel))));
8314
8315 if (attTup->atthasdef)
8316 ereport(ERROR,
8317 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8318 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8319 colName, RelationGetRelationName(rel))));
8320
8321 attTup->attidentity = cdef->identity;
8322 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8323
8324 InvokeObjectPostAlterHook(RelationRelationId,
8325 RelationGetRelid(rel),
8326 attTup->attnum);
8327 ObjectAddressSubSet(address, RelationRelationId,
8328 RelationGetRelid(rel), attnum);
8329 heap_freetuple(tuple);
8330
8331 table_close(attrelation, RowExclusiveLock);
8332
8333 /*
8334 * Recurse to propagate the identity column to partitions. Identity is
8335 * not inherited in regular inheritance children.
8336 */
8337 if (recurse && ispartitioned)
8338 {
8339 List *children;
8340 ListCell *lc;
8341
8342 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8343
8344 foreach(lc, children)
8345 {
8346 Relation childrel;
8347
8348 childrel = table_open(lfirst_oid(lc), NoLock);
8349 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8350 table_close(childrel, NoLock);
8351 }
8352 }
8353
8354 return address;
8355}
int errhint(const char *fmt,...)
Definition: elog.c:1330
HeapTuple findNotNullConstraintAttnum(Oid relid, AttrNumber attnum)
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8232

References ATExecAddIdentity(), attnum, castNode, CatalogTupleUpdate(), elog, ereport, errcode(), errhint(), errmsg(), ERROR, find_inheritance_children(), findNotNullConstraintAttnum(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, ColumnDef::identity, InvokeObjectPostAlterHook, lfirst_oid, NameStr, 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 9612 of file tablecmds.c.

9614{
9615 bool check_rights;
9616 bool skip_build;
9617 bool quiet;
9618 ObjectAddress address;
9619
9621 Assert(!stmt->concurrent);
9622
9623 /* The IndexStmt has already been through transformIndexStmt */
9624 Assert(stmt->transformed);
9625
9626 /* suppress schema rights check when rebuilding existing index */
9627 check_rights = !is_rebuild;
9628 /* skip index build if phase 3 will do it or we're reusing an old one */
9629 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9630 /* suppress notices when rebuilding existing index */
9631 quiet = is_rebuild;
9632
9633 address = DefineIndex(RelationGetRelid(rel),
9634 stmt,
9635 InvalidOid, /* no predefined OID */
9636 InvalidOid, /* no parent index */
9637 InvalidOid, /* no parent constraint */
9638 -1, /* total_parts unknown */
9639 true, /* is_alter_table */
9640 check_rights,
9641 false, /* check_not_in_use - we did it already */
9642 skip_build,
9643 quiet);
9644
9645 /*
9646 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9647 * new index instead of building from scratch. Restore associated fields.
9648 * This may store InvalidSubTransactionId in both fields, in which case
9649 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9650 * this after the CCI that made catalog rows visible to any rebuild. The
9651 * DROP of the old edition of this index will have scheduled the storage
9652 * for deletion at commit, so cancel that pending deletion.
9653 */
9654 if (RelFileNumberIsValid(stmt->oldNumber))
9655 {
9656 Relation irel = index_open(address.objectId, NoLock);
9657
9658 irel->rd_createSubid = stmt->oldCreateSubid;
9659 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9661 index_close(irel, NoLock);
9662 }
9663
9664 return address;
9665}
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:541
#define RelFileNumberIsValid(relnumber)
Definition: relpath.h:27
void RelationPreserveStorage(RelFileLocator rlocator, bool atCommit)
Definition: storage.c:252
SubTransactionId rd_firstRelfilelocatorSubid
Definition: rel.h:106
SubTransactionId rd_createSubid
Definition: rel.h:103
RelFileLocator rd_locator
Definition: rel.h:57

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

Referenced by ATExecCmd().

◆ ATExecAddIndexConstraint()

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

Definition at line 9696 of file tablecmds.c.

9698{
9699 Oid index_oid = stmt->indexOid;
9700 Relation indexRel;
9701 char *indexName;
9702 IndexInfo *indexInfo;
9703 char *constraintName;
9704 char constraintType;
9705 ObjectAddress address;
9706 bits16 flags;
9707
9709 Assert(OidIsValid(index_oid));
9710 Assert(stmt->isconstraint);
9711
9712 /*
9713 * Doing this on partitioned tables is not a simple feature to implement,
9714 * so let's punt for now.
9715 */
9716 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9717 ereport(ERROR,
9718 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9719 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9720
9721 indexRel = index_open(index_oid, AccessShareLock);
9722
9723 indexName = pstrdup(RelationGetRelationName(indexRel));
9724
9725 indexInfo = BuildIndexInfo(indexRel);
9726
9727 /* this should have been checked at parse time */
9728 if (!indexInfo->ii_Unique)
9729 elog(ERROR, "index \"%s\" is not unique", indexName);
9730
9731 /*
9732 * Determine name to assign to constraint. We require a constraint to
9733 * have the same name as the underlying index; therefore, use the index's
9734 * existing name as the default constraint name, and if the user
9735 * explicitly gives some other name for the constraint, rename the index
9736 * to match.
9737 */
9738 constraintName = stmt->idxname;
9739 if (constraintName == NULL)
9740 constraintName = indexName;
9741 else if (strcmp(constraintName, indexName) != 0)
9742 {
9744 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9745 indexName, constraintName)));
9746 RenameRelationInternal(index_oid, constraintName, false, true);
9747 }
9748
9749 /* Extra checks needed if making primary key */
9750 if (stmt->primary)
9751 index_check_primary_key(rel, indexInfo, true, stmt);
9752
9753 /* Note we currently don't support EXCLUSION constraints here */
9754 if (stmt->primary)
9755 constraintType = CONSTRAINT_PRIMARY;
9756 else
9757 constraintType = CONSTRAINT_UNIQUE;
9758
9759 /* Create the catalog entries for the constraint */
9762 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9763 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9765
9766 address = index_constraint_create(rel,
9767 index_oid,
9768 InvalidOid,
9769 indexInfo,
9770 constraintName,
9771 constraintType,
9772 flags,
9774 false); /* is_internal */
9775
9776 index_close(indexRel, NoLock);
9777
9778 return address;
9779}
uint16 bits16
Definition: c.h:549
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:200
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:4262

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

17254{
17255 Relation parent_rel;
17256 List *children;
17257 ObjectAddress address;
17258 const char *trigger_name;
17259
17260 /*
17261 * A self-exclusive lock is needed here. See the similar case in
17262 * MergeAttributes() for a full explanation.
17263 */
17264 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17265
17266 /*
17267 * Must be owner of both parent and child -- child was checked by
17268 * ATSimplePermissions call in ATPrepCmd
17269 */
17272
17273 /* Permanent rels cannot inherit from temporary ones */
17274 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17275 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17276 ereport(ERROR,
17277 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17278 errmsg("cannot inherit from temporary relation \"%s\"",
17279 RelationGetRelationName(parent_rel))));
17280
17281 /* If parent rel is temp, it must belong to this session */
17282 if (RELATION_IS_OTHER_TEMP(parent_rel))
17283 ereport(ERROR,
17284 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17285 errmsg("cannot inherit from temporary relation of another session")));
17286
17287 /* Ditto for the child */
17288 if (RELATION_IS_OTHER_TEMP(child_rel))
17289 ereport(ERROR,
17290 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17291 errmsg("cannot inherit to temporary relation of another session")));
17292
17293 /* Prevent partitioned tables from becoming inheritance parents */
17294 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17295 ereport(ERROR,
17296 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17297 errmsg("cannot inherit from partitioned table \"%s\"",
17298 parent->relname)));
17299
17300 /* Likewise for partitions */
17301 if (parent_rel->rd_rel->relispartition)
17302 ereport(ERROR,
17303 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17304 errmsg("cannot inherit from a partition")));
17305
17306 /*
17307 * Prevent circularity by seeing if proposed parent inherits from child.
17308 * (In particular, this disallows making a rel inherit from itself.)
17309 *
17310 * This is not completely bulletproof because of race conditions: in
17311 * multi-level inheritance trees, someone else could concurrently be
17312 * making another inheritance link that closes the loop but does not join
17313 * either of the rels we have locked. Preventing that seems to require
17314 * exclusive locks on the entire inheritance tree, which is a cure worse
17315 * than the disease. find_all_inheritors() will cope with circularity
17316 * anyway, so don't sweat it too much.
17317 *
17318 * We use weakest lock we can on child's children, namely AccessShareLock.
17319 */
17320 children = find_all_inheritors(RelationGetRelid(child_rel),
17321 AccessShareLock, NULL);
17322
17323 if (list_member_oid(children, RelationGetRelid(parent_rel)))
17324 ereport(ERROR,
17325 (errcode(ERRCODE_DUPLICATE_TABLE),
17326 errmsg("circular inheritance not allowed"),
17327 errdetail("\"%s\" is already a child of \"%s\".",
17328 parent->relname,
17329 RelationGetRelationName(child_rel))));
17330
17331 /*
17332 * If child_rel has row-level triggers with transition tables, we
17333 * currently don't allow it to become an inheritance child. See also
17334 * prohibitions in ATExecAttachPartition() and CreateTrigger().
17335 */
17336 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17337 if (trigger_name != NULL)
17338 ereport(ERROR,
17339 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17340 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17341 trigger_name, RelationGetRelationName(child_rel)),
17342 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17343
17344 /* OK to create inheritance */
17345 CreateInheritance(child_rel, parent_rel, false);
17346
17347 ObjectAddressSet(address, RelationRelationId,
17348 RelationGetRelid(parent_rel));
17349
17350 /* keep our lock on the parent relation until commit */
17351 table_close(parent_rel, NoLock);
17352
17353 return address;
17354}
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:668
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:17364
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_rel, RELATION_IS_OTHER_TEMP, 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 18206 of file tablecmds.c.

18207{
18208 Oid relid = RelationGetRelid(rel);
18209 Type typetuple;
18210 Form_pg_type typeform;
18211 Oid typeid;
18212 Relation inheritsRelation,
18213 relationRelation;
18214 SysScanDesc scan;
18216 AttrNumber table_attno,
18217 type_attno;
18218 TupleDesc typeTupleDesc,
18219 tableTupleDesc;
18220 ObjectAddress tableobj,
18221 typeobj;
18222 HeapTuple classtuple;
18223
18224 /* Validate the type. */
18225 typetuple = typenameType(NULL, ofTypename, NULL);
18226 check_of_type(typetuple);
18227 typeform = (Form_pg_type) GETSTRUCT(typetuple);
18228 typeid = typeform->oid;
18229
18230 /* Fail if the table has any inheritance parents. */
18231 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18233 Anum_pg_inherits_inhrelid,
18234 BTEqualStrategyNumber, F_OIDEQ,
18235 ObjectIdGetDatum(relid));
18236 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18237 true, NULL, 1, &key);
18239 ereport(ERROR,
18240 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18241 errmsg("typed tables cannot inherit")));
18242 systable_endscan(scan);
18243 table_close(inheritsRelation, AccessShareLock);
18244
18245 /*
18246 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18247 * require that the order also match. However, attnotnull need not match.
18248 */
18249 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18250 tableTupleDesc = RelationGetDescr(rel);
18251 table_attno = 1;
18252 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18253 {
18254 Form_pg_attribute type_attr,
18255 table_attr;
18256 const char *type_attname,
18257 *table_attname;
18258
18259 /* Get the next non-dropped type attribute. */
18260 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18261 if (type_attr->attisdropped)
18262 continue;
18263 type_attname = NameStr(type_attr->attname);
18264
18265 /* Get the next non-dropped table attribute. */
18266 do
18267 {
18268 if (table_attno > tableTupleDesc->natts)
18269 ereport(ERROR,
18270 (errcode(ERRCODE_DATATYPE_MISMATCH),
18271 errmsg("table is missing column \"%s\"",
18272 type_attname)));
18273 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18274 table_attno++;
18275 } while (table_attr->attisdropped);
18276 table_attname = NameStr(table_attr->attname);
18277
18278 /* Compare name. */
18279 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18280 ereport(ERROR,
18281 (errcode(ERRCODE_DATATYPE_MISMATCH),
18282 errmsg("table has column \"%s\" where type requires \"%s\"",
18283 table_attname, type_attname)));
18284
18285 /* Compare type. */
18286 if (table_attr->atttypid != type_attr->atttypid ||
18287 table_attr->atttypmod != type_attr->atttypmod ||
18288 table_attr->attcollation != type_attr->attcollation)
18289 ereport(ERROR,
18290 (errcode(ERRCODE_DATATYPE_MISMATCH),
18291 errmsg("table \"%s\" has different type for column \"%s\"",
18292 RelationGetRelationName(rel), type_attname)));
18293 }
18294 ReleaseTupleDesc(typeTupleDesc);
18295
18296 /* Any remaining columns at the end of the table had better be dropped. */
18297 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18298 {
18299 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18300 table_attno - 1);
18301
18302 if (!table_attr->attisdropped)
18303 ereport(ERROR,
18304 (errcode(ERRCODE_DATATYPE_MISMATCH),
18305 errmsg("table has extra column \"%s\"",
18306 NameStr(table_attr->attname))));
18307 }
18308
18309 /* If the table was already typed, drop the existing dependency. */
18310 if (rel->rd_rel->reloftype)
18311 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18313
18314 /* Record a dependency on the new type. */
18315 tableobj.classId = RelationRelationId;
18316 tableobj.objectId = relid;
18317 tableobj.objectSubId = 0;
18318 typeobj.classId = TypeRelationId;
18319 typeobj.objectId = typeid;
18320 typeobj.objectSubId = 0;
18321 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18322
18323 /* Update pg_class.reloftype */
18324 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18325 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18326 if (!HeapTupleIsValid(classtuple))
18327 elog(ERROR, "cache lookup failed for relation %u", relid);
18328 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18329 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18330
18331 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18332
18333 heap_freetuple(classtuple);
18334 table_close(relationRelation, RowExclusiveLock);
18335
18336 ReleaseSysCache(typetuple);
18337
18338 return typeobj;
18339}
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:18154
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:7135
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:219
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1921

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

9677{
9678 ObjectAddress address;
9679
9681
9682 /* The CreateStatsStmt has already been through transformStatsStmt */
9683 Assert(stmt->transformed);
9684
9685 address = CreateStatistics(stmt, !is_rebuild);
9686
9687 return address;
9688}
ObjectAddress CreateStatistics(CreateStatsStmt *stmt, bool check_rights)
Definition: statscmds.c:63

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

Referenced by ATExecCmd().

◆ ATExecAlterColumnGenericOptions()

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

Definition at line 15947 of file tablecmds.c.

15951{
15952 Relation ftrel;
15953 Relation attrel;
15954 ForeignServer *server;
15955 ForeignDataWrapper *fdw;
15956 HeapTuple tuple;
15957 HeapTuple newtuple;
15958 bool isnull;
15959 Datum repl_val[Natts_pg_attribute];
15960 bool repl_null[Natts_pg_attribute];
15961 bool repl_repl[Natts_pg_attribute];
15962 Datum datum;
15963 Form_pg_foreign_table fttableform;
15964 Form_pg_attribute atttableform;
15966 ObjectAddress address;
15967
15968 if (options == NIL)
15969 return InvalidObjectAddress;
15970
15971 /* First, determine FDW validator associated to the foreign table. */
15972 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15973 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15974 if (!HeapTupleIsValid(tuple))
15975 ereport(ERROR,
15976 (errcode(ERRCODE_UNDEFINED_OBJECT),
15977 errmsg("foreign table \"%s\" does not exist",
15979 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15980 server = GetForeignServer(fttableform->ftserver);
15981 fdw = GetForeignDataWrapper(server->fdwid);
15982
15984 ReleaseSysCache(tuple);
15985
15986 attrel = table_open(AttributeRelationId, RowExclusiveLock);
15987 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15988 if (!HeapTupleIsValid(tuple))
15989 ereport(ERROR,
15990 (errcode(ERRCODE_UNDEFINED_COLUMN),
15991 errmsg("column \"%s\" of relation \"%s\" does not exist",
15992 colName, RelationGetRelationName(rel))));
15993
15994 /* Prevent them from altering a system attribute */
15995 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15996 attnum = atttableform->attnum;
15997 if (attnum <= 0)
15998 ereport(ERROR,
15999 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16000 errmsg("cannot alter system column \"%s\"", colName)));
16001
16002
16003 /* Initialize buffers for new tuple values */
16004 memset(repl_val, 0, sizeof(repl_val));
16005 memset(repl_null, false, sizeof(repl_null));
16006 memset(repl_repl, false, sizeof(repl_repl));
16007
16008 /* Extract the current options */
16009 datum = SysCacheGetAttr(ATTNAME,
16010 tuple,
16011 Anum_pg_attribute_attfdwoptions,
16012 &isnull);
16013 if (isnull)
16014 datum = PointerGetDatum(NULL);
16015
16016 /* Transform the options */
16017 datum = transformGenericOptions(AttributeRelationId,
16018 datum,
16019 options,
16020 fdw->fdwvalidator);
16021
16022 if (DatumGetPointer(datum) != NULL)
16023 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
16024 else
16025 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
16026
16027 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
16028
16029 /* Everything looks good - update the tuple */
16030
16031 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16032 repl_val, repl_null, repl_repl);
16033
16034 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16035
16036 InvokeObjectPostAlterHook(RelationRelationId,
16037 RelationGetRelid(rel),
16038 atttableform->attnum);
16039 ObjectAddressSubSet(address, RelationRelationId,
16040 RelationGetRelid(rel), attnum);
16041
16042 ReleaseSysCache(tuple);
16043
16045
16046 heap_freetuple(newtuple);
16047
16048 return address;
16049}
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition: foreign.c:38
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:112
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
Definition: foreigncmds.c:121
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:332
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:322
Oid rd_id
Definition: rel.h:113
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:595
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition: syscache.c:475

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

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

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

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

Referenced by ATExecCmd().

◆ ATExecAlterConstraintInternal()

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

Definition at line 12333 of file tablecmds.c.

12337{
12338 Form_pg_constraint currcon;
12339 bool changed = false;
12340 List *otherrelids = NIL;
12341
12342 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12343
12344 /*
12345 * Do the catalog work for the enforceability or deferrability change,
12346 * recurse if necessary.
12347 *
12348 * Note that even if deferrability is requested to be altered along with
12349 * enforceability, we don't need to explicitly update multiple entries in
12350 * pg_trigger related to deferrability.
12351 *
12352 * Modifying enforceability involves either creating or dropping the
12353 * trigger, during which the deferrability setting will be adjusted
12354 * automatically.
12355 */
12356 if (cmdcon->alterEnforceability &&
12357 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12358 currcon->conrelid, currcon->confrelid,
12359 contuple, lockmode, InvalidOid,
12361 changed = true;
12362
12363 else if (cmdcon->alterDeferrability &&
12364 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12365 contuple, recurse, &otherrelids,
12366 lockmode))
12367 {
12368 /*
12369 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12370 * the relations having the constraint itself; here we also invalidate
12371 * for relations that have any triggers that are part of the
12372 * constraint.
12373 */
12374 foreach_oid(relid, otherrelids)
12376
12377 changed = true;
12378 }
12379
12380 /*
12381 * Do the catalog work for the inheritability change.
12382 */
12383 if (cmdcon->alterInheritability &&
12384 ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12385 lockmode))
12386 changed = true;
12387
12388 return changed;
12389}
#define foreach_oid(var, lst)
Definition: pg_list.h:471
static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12607

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

Referenced by ATExecAlterConstraint().

◆ ATExecAlterConstrDeferrability()

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

Definition at line 12550 of file tablecmds.c.

12554{
12555 Form_pg_constraint currcon;
12556 Oid refrelid;
12557 bool changed = false;
12558
12559 /* since this function recurses, it could be driven to stack overflow */
12561
12562 Assert(cmdcon->alterDeferrability);
12563
12564 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12565 refrelid = currcon->confrelid;
12566
12567 /* Should be foreign key constraint */
12568 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12569
12570 /*
12571 * If called to modify a constraint that's already in the desired state,
12572 * silently do nothing.
12573 */
12574 if (currcon->condeferrable != cmdcon->deferrable ||
12575 currcon->condeferred != cmdcon->initdeferred)
12576 {
12577 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12578 changed = true;
12579
12580 /*
12581 * Now we need to update the multiple entries in pg_trigger that
12582 * implement the constraint.
12583 */
12584 AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12585 cmdcon->deferrable,
12586 cmdcon->initdeferred, otherrelids);
12587 }
12588
12589 /*
12590 * If the table at either end of the constraint is partitioned, we need to
12591 * handle every constraint that is a child of this one.
12592 */
12593 if (recurse && changed &&
12594 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12595 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12596 AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12597 contuple, recurse, otherrelids,
12598 lockmode);
12599
12600 return changed;
12601}
static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)
Definition: tablecmds.c:12846
static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12804
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
Definition: tablecmds.c:12686

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

Referenced by AlterConstrDeferrabilityRecurse(), and ATExecAlterConstraintInternal().

◆ ATExecAlterConstrEnforceability()

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

Definition at line 12404 of file tablecmds.c.

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

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

Referenced by AlterConstrEnforceabilityRecurse(), and ATExecAlterConstraintInternal().

◆ ATExecAlterConstrInheritability()

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

Definition at line 12607 of file tablecmds.c.

12610{
12611 Form_pg_constraint currcon;
12612 AttrNumber colNum;
12613 char *colName;
12614 List *children;
12615
12616 Assert(cmdcon->alterInheritability);
12617
12618 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12619
12620 /* The current implementation only works for NOT NULL constraints */
12621 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12622
12623 /*
12624 * If called to modify a constraint that's already in the desired state,
12625 * silently do nothing.
12626 */
12627 if (cmdcon->noinherit == currcon->connoinherit)
12628 return false;
12629
12630 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12632
12633 /* Fetch the column number and name */
12634 colNum = extractNotNullColumn(contuple);
12635 colName = get_attname(currcon->conrelid, colNum, false);
12636
12637 /*
12638 * Propagate the change to children. For this subcommand type we don't
12639 * recursively affect children, just the immediate level.
12640 */
12642 lockmode);
12643 foreach_oid(childoid, children)
12644 {
12645 ObjectAddress addr;
12646
12647 if (cmdcon->noinherit)
12648 {
12649 HeapTuple childtup;
12650 Form_pg_constraint childcon;
12651
12652 childtup = findNotNullConstraint(childoid, colName);
12653 if (!childtup)
12654 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12655 colName, childoid);
12656 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12657 Assert(childcon->coninhcount > 0);
12658 childcon->coninhcount--;
12659 childcon->conislocal = true;
12660 CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12661 heap_freetuple(childtup);
12662 }
12663 else
12664 {
12665 Relation childrel = table_open(childoid, NoLock);
12666
12667 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12668 colName, true, true, lockmode);
12669 if (OidIsValid(addr.objectId))
12671 table_close(childrel, NoLock);
12672 }
12673 }
12674
12675 return true;
12676}
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:920
HeapTuple findNotNullConstraint(Oid relid, const char *colname)
AttrNumber extractNotNullColumn(HeapTuple constrTup)
static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:7905

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

Referenced by ATExecAlterConstraintInternal().

◆ ATExecAttachPartition()

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

Definition at line 20240 of file tablecmds.c.

20242{
20243 Relation attachrel,
20244 catalog;
20245 List *attachrel_children;
20246 List *partConstraint;
20247 SysScanDesc scan;
20248 ScanKeyData skey;
20249 AttrNumber attno;
20250 int natts;
20251 TupleDesc tupleDesc;
20252 ObjectAddress address;
20253 const char *trigger_name;
20254 Oid defaultPartOid;
20255 List *partBoundConstraint;
20256 ParseState *pstate = make_parsestate(NULL);
20257
20258 pstate->p_sourcetext = context->queryString;
20259
20260 /*
20261 * We must lock the default partition if one exists, because attaching a
20262 * new partition will change its partition constraint.
20263 */
20264 defaultPartOid =
20266 if (OidIsValid(defaultPartOid))
20267 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20268
20269 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20270
20271 /*
20272 * XXX I think it'd be a good idea to grab locks on all tables referenced
20273 * by FKs at this point also.
20274 */
20275
20276 /*
20277 * Must be owner of both parent and source table -- parent was checked by
20278 * ATSimplePermissions call in ATPrepCmd
20279 */
20282
20283 /* A partition can only have one parent */
20284 if (attachrel->rd_rel->relispartition)
20285 ereport(ERROR,
20286 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20287 errmsg("\"%s\" is already a partition",
20288 RelationGetRelationName(attachrel))));
20289
20290 if (OidIsValid(attachrel->rd_rel->reloftype))
20291 ereport(ERROR,
20292 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20293 errmsg("cannot attach a typed table as partition")));
20294
20295 /*
20296 * Table being attached should not already be part of inheritance; either
20297 * as a child table...
20298 */
20299 catalog = table_open(InheritsRelationId, AccessShareLock);
20300 ScanKeyInit(&skey,
20301 Anum_pg_inherits_inhrelid,
20302 BTEqualStrategyNumber, F_OIDEQ,
20304 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20305 NULL, 1, &skey);
20307 ereport(ERROR,
20308 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20309 errmsg("cannot attach inheritance child as partition")));
20310 systable_endscan(scan);
20311
20312 /* ...or as a parent table (except the case when it is partitioned) */
20313 ScanKeyInit(&skey,
20314 Anum_pg_inherits_inhparent,
20315 BTEqualStrategyNumber, F_OIDEQ,
20317 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20318 1, &skey);
20320 attachrel->rd_rel->relkind == RELKIND_RELATION)
20321 ereport(ERROR,
20322 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20323 errmsg("cannot attach inheritance parent as partition")));
20324 systable_endscan(scan);
20325 table_close(catalog, AccessShareLock);
20326
20327 /*
20328 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20329 * particular, this disallows making a rel a partition of itself.)
20330 *
20331 * We do that by checking if rel is a member of the list of attachrel's
20332 * partitions provided the latter is partitioned at all. We want to avoid
20333 * having to construct this list again, so we request the strongest lock
20334 * on all partitions. We need the strongest lock, because we may decide
20335 * to scan them if we find out that the table being attached (or its leaf
20336 * partitions) may contain rows that violate the partition constraint. If
20337 * the table has a constraint that would prevent such rows, which by
20338 * definition is present in all the partitions, we need not scan the
20339 * table, nor its partitions. But we cannot risk a deadlock by taking a
20340 * weaker lock now and the stronger one only when needed.
20341 */
20342 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20343 AccessExclusiveLock, NULL);
20344 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20345 ereport(ERROR,
20346 (errcode(ERRCODE_DUPLICATE_TABLE),
20347 errmsg("circular inheritance not allowed"),
20348 errdetail("\"%s\" is already a child of \"%s\".",
20350 RelationGetRelationName(attachrel))));
20351
20352 /* If the parent is permanent, so must be all of its partitions. */
20353 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20354 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20355 ereport(ERROR,
20356 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20357 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20359
20360 /* Temp parent cannot have a partition that is itself not a temp */
20361 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20362 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20363 ereport(ERROR,
20364 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20365 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20367
20368 /* If the parent is temp, it must belong to this session */
20369 if (RELATION_IS_OTHER_TEMP(rel))
20370 ereport(ERROR,
20371 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20372 errmsg("cannot attach as partition of temporary relation of another session")));
20373
20374 /* Ditto for the partition */
20375 if (RELATION_IS_OTHER_TEMP(attachrel))
20376 ereport(ERROR,
20377 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20378 errmsg("cannot attach temporary relation of another session as partition")));
20379
20380 /*
20381 * Check if attachrel has any identity columns or any columns that aren't
20382 * in the parent.
20383 */
20384 tupleDesc = RelationGetDescr(attachrel);
20385 natts = tupleDesc->natts;
20386 for (attno = 1; attno <= natts; attno++)
20387 {
20388 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20389 char *attributeName = NameStr(attribute->attname);
20390
20391 /* Ignore dropped */
20392 if (attribute->attisdropped)
20393 continue;
20394
20395 if (attribute->attidentity)
20396 ereport(ERROR,
20397 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20398 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20399 RelationGetRelationName(attachrel), attributeName),
20400 errdetail("The new partition may not contain an identity column."));
20401
20402 /* Try to find the column in parent (matching on column name) */
20403 if (!SearchSysCacheExists2(ATTNAME,
20405 CStringGetDatum(attributeName)))
20406 ereport(ERROR,
20407 (errcode(ERRCODE_DATATYPE_MISMATCH),
20408 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20409 RelationGetRelationName(attachrel), attributeName,
20411 errdetail("The new partition may contain only the columns present in parent.")));
20412 }
20413
20414 /*
20415 * If child_rel has row-level triggers with transition tables, we
20416 * currently don't allow it to become a partition. See also prohibitions
20417 * in ATExecAddInherit() and CreateTrigger().
20418 */
20419 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20420 if (trigger_name != NULL)
20421 ereport(ERROR,
20422 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20423 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20424 trigger_name, RelationGetRelationName(attachrel)),
20425 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20426
20427 /*
20428 * Check that the new partition's bound is valid and does not overlap any
20429 * of existing partitions of the parent - note that it does not return on
20430 * error.
20431 */
20433 cmd->bound, pstate);
20434
20435 /* OK to create inheritance. Rest of the checks performed there */
20436 CreateInheritance(attachrel, rel, true);
20437
20438 /* Update the pg_class entry. */
20439 StorePartitionBound(attachrel, rel, cmd->bound);
20440
20441 /* Ensure there exists a correct set of indexes in the partition. */
20442 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20443
20444 /* and triggers */
20445 CloneRowTriggersToPartition(rel, attachrel);
20446
20447 /*
20448 * Clone foreign key constraints. Callee is responsible for setting up
20449 * for phase 3 constraint verification.
20450 */
20451 CloneForeignKeyConstraints(wqueue, rel, attachrel);
20452
20453 /*
20454 * Generate partition constraint from the partition bound specification.
20455 * If the parent itself is a partition, make sure to include its
20456 * constraint as well.
20457 */
20458 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20459
20460 /*
20461 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20462 * since it's needed later to construct the constraint expression for
20463 * validating against the default partition, if any.
20464 */
20465 partConstraint = list_concat_copy(partBoundConstraint,
20467
20468 /* Skip validation if there are no constraints to validate. */
20469 if (partConstraint)
20470 {
20471 /*
20472 * Run the partition quals through const-simplification similar to
20473 * check constraints. We skip canonicalize_qual, though, because
20474 * partition quals should be in canonical form already.
20475 */
20476 partConstraint =
20478 (Node *) partConstraint);
20479
20480 /* XXX this sure looks wrong */
20481 partConstraint = list_make1(make_ands_explicit(partConstraint));
20482
20483 /*
20484 * Adjust the generated constraint to match this partition's attribute
20485 * numbers.
20486 */
20487 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20488 rel);
20489
20490 /* Validate partition constraints against the table being attached. */
20491 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20492 false);
20493 }
20494
20495 /*
20496 * If we're attaching a partition other than the default partition and a
20497 * default one exists, then that partition's partition constraint changes,
20498 * so add an entry to the work queue to validate it, too. (We must not do
20499 * this when the partition being attached is the default one; we already
20500 * did it above!)
20501 */
20502 if (OidIsValid(defaultPartOid))
20503 {
20504 Relation defaultrel;
20505 List *defPartConstraint;
20506
20507 Assert(!cmd->bound->is_default);
20508
20509 /* we already hold a lock on the default partition */
20510 defaultrel = table_open(defaultPartOid, NoLock);
20511 defPartConstraint =
20512 get_proposed_default_constraint(partBoundConstraint);
20513
20514 /*
20515 * Map the Vars in the constraint expression from rel's attnos to
20516 * defaultrel's.
20517 */
20518 defPartConstraint =
20519 map_partition_varattnos(defPartConstraint,
20520 1, defaultrel, rel);
20521 QueuePartitionConstraintValidation(wqueue, defaultrel,
20522 defPartConstraint, true);
20523
20524 /* keep our lock until commit. */
20525 table_close(defaultrel, NoLock);
20526 }
20527
20528 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20529
20530 /*
20531 * If the partition we just attached is partitioned itself, invalidate
20532 * relcache for all descendent partitions too to ensure that their
20533 * rd_partcheck expression trees are rebuilt; partitions already locked at
20534 * the beginning of this function.
20535 */
20536 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20537 {
20538 ListCell *l;
20539
20540 foreach(l, attachrel_children)
20541 {
20543 }
20544 }
20545
20546 /* keep our lock until commit */
20547 table_close(attachrel, NoLock);
20548
20549 return address;
20550}
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2270
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition: heap.c:4050
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:2897
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:195
PartitionBoundSpec * bound
Definition: parsenodes.h:975
RangeVar * name
Definition: parsenodes.h:974
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition: syscache.h:102
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
Definition: tablecmds.c:20743
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:20561
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11209
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
Definition: tablecmds.c:20167

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_rel, RELATION_IS_OTHER_TEMP, 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 21568 of file tablecmds.c.

21569{
21570 Relation partIdx;
21571 Relation partTbl;
21572 Relation parentTbl;
21573 ObjectAddress address;
21574 Oid partIdxId;
21575 Oid currParent;
21577
21578 /*
21579 * We need to obtain lock on the index 'name' to modify it, but we also
21580 * need to read its owning table's tuple descriptor -- so we need to lock
21581 * both. To avoid deadlocks, obtain lock on the table before doing so on
21582 * the index. Furthermore, we need to examine the parent table of the
21583 * partition, so lock that one too.
21584 */
21585 state.partitionOid = InvalidOid;
21586 state.parentTblOid = parentIdx->rd_index->indrelid;
21587 state.lockedParentTbl = false;
21588 partIdxId =
21591 &state);
21592 /* Not there? */
21593 if (!OidIsValid(partIdxId))
21594 ereport(ERROR,
21595 (errcode(ERRCODE_UNDEFINED_OBJECT),
21596 errmsg("index \"%s\" does not exist", name->relname)));
21597
21598 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21599 partIdx = relation_open(partIdxId, AccessExclusiveLock);
21600
21601 /* we already hold locks on both tables, so this is safe: */
21602 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21603 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21604
21605 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21606
21607 /* Silently do nothing if already in the right state */
21608 currParent = partIdx->rd_rel->relispartition ?
21609 get_partition_parent(partIdxId, false) : InvalidOid;
21610 if (currParent != RelationGetRelid(parentIdx))
21611 {
21612 IndexInfo *childInfo;
21613 IndexInfo *parentInfo;
21614 AttrMap *attmap;
21615 bool found;
21616 int i;
21617 PartitionDesc partDesc;
21618 Oid constraintOid,
21619 cldConstrId = InvalidOid;
21620
21621 /*
21622 * If this partition already has an index attached, refuse the
21623 * operation.
21624 */
21625 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21626
21627 if (OidIsValid(currParent))
21628 ereport(ERROR,
21629 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21630 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21631 RelationGetRelationName(partIdx),
21632 RelationGetRelationName(parentIdx)),
21633 errdetail("Index \"%s\" is already attached to another index.",
21634 RelationGetRelationName(partIdx))));
21635
21636 /* Make sure it indexes a partition of the other index's table */
21637 partDesc = RelationGetPartitionDesc(parentTbl, true);
21638 found = false;
21639 for (i = 0; i < partDesc->nparts; i++)
21640 {
21641 if (partDesc->oids[i] == state.partitionOid)
21642 {
21643 found = true;
21644 break;
21645 }
21646 }
21647 if (!found)
21648 ereport(ERROR,
21649 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21650 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21651 RelationGetRelationName(partIdx),
21652 RelationGetRelationName(parentIdx)),
21653 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21654 RelationGetRelationName(partIdx),
21655 RelationGetRelationName(parentTbl))));
21656
21657 /* Ensure the indexes are compatible */
21658 childInfo = BuildIndexInfo(partIdx);
21659 parentInfo = BuildIndexInfo(parentIdx);
21660 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21661 RelationGetDescr(parentTbl),
21662 false);
21663 if (!CompareIndexInfo(childInfo, parentInfo,
21664 partIdx->rd_indcollation,
21665 parentIdx->rd_indcollation,
21666 partIdx->rd_opfamily,
21667 parentIdx->rd_opfamily,
21668 attmap))
21669 ereport(ERROR,
21670 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21671 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21672 RelationGetRelationName(partIdx),
21673 RelationGetRelationName(parentIdx)),
21674 errdetail("The index definitions do not match.")));
21675
21676 /*
21677 * If there is a constraint in the parent, make sure there is one in
21678 * the child too.
21679 */
21680 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21681 RelationGetRelid(parentIdx));
21682
21683 if (OidIsValid(constraintOid))
21684 {
21686 partIdxId);
21687 if (!OidIsValid(cldConstrId))
21688 ereport(ERROR,
21689 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21690 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21691 RelationGetRelationName(partIdx),
21692 RelationGetRelationName(parentIdx)),
21693 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21694 RelationGetRelationName(parentIdx),
21695 RelationGetRelationName(parentTbl),
21696 RelationGetRelationName(partIdx))));
21697 }
21698
21699 /*
21700 * If it's a primary key, make sure the columns in the partition are
21701 * NOT NULL.
21702 */
21703 if (parentIdx->rd_index->indisprimary)
21704 verifyPartitionIndexNotNull(childInfo, partTbl);
21705
21706 /* All good -- do it */
21707 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21708 if (OidIsValid(constraintOid))
21709 ConstraintSetParentConstraint(cldConstrId, constraintOid,
21710 RelationGetRelid(partTbl));
21711
21712 free_attrmap(attmap);
21713
21714 validatePartitionedIndex(parentIdx, parentTbl);
21715 }
21716
21717 relation_close(parentTbl, AccessShareLock);
21718 /* keep these locks till commit */
21719 relation_close(partTbl, NoLock);
21720 relation_close(partIdx, NoLock);
21721
21722 return address;
21723}
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:4445
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition: partition.c:53
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
Form_pg_index rd_index
Definition: rel.h:192
Oid * rd_opfamily
Definition: rel.h:207
Oid * rd_indcollation
Definition: rel.h:217
Definition: regguts.h:323
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:21514
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
Definition: tablecmds.c:21754
static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
Definition: tablecmds.c:21856
static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
Definition: tablecmds.c:21730
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 16064 of file tablecmds.c.

16065{
16066 Relation target_rel;
16067 Relation class_rel;
16068 HeapTuple tuple;
16069 Form_pg_class tuple_class;
16070
16071 /*
16072 * Get exclusive lock till end of transaction on the target table. Use
16073 * relation_open so that we can work on indexes and sequences.
16074 */
16075 target_rel = relation_open(relationOid, lockmode);
16076
16077 /* Get its pg_class tuple, too */
16078 class_rel = table_open(RelationRelationId, RowExclusiveLock);
16079
16080 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16081 if (!HeapTupleIsValid(tuple))
16082 elog(ERROR, "cache lookup failed for relation %u", relationOid);
16083 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16084
16085 /* Can we change the ownership of this tuple? */
16086 switch (tuple_class->relkind)
16087 {
16088 case RELKIND_RELATION:
16089 case RELKIND_VIEW:
16090 case RELKIND_MATVIEW:
16091 case RELKIND_FOREIGN_TABLE:
16092 case RELKIND_PARTITIONED_TABLE:
16093 /* ok to change owner */
16094 break;
16095 case RELKIND_INDEX:
16096 if (!recursing)
16097 {
16098 /*
16099 * Because ALTER INDEX OWNER used to be allowed, and in fact
16100 * is generated by old versions of pg_dump, we give a warning
16101 * and do nothing rather than erroring out. Also, to avoid
16102 * unnecessary chatter while restoring those old dumps, say
16103 * nothing at all if the command would be a no-op anyway.
16104 */
16105 if (tuple_class->relowner != newOwnerId)
16107 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16108 errmsg("cannot change owner of index \"%s\"",
16109 NameStr(tuple_class->relname)),
16110 errhint("Change the ownership of the index's table instead.")));
16111 /* quick hack to exit via the no-op path */
16112 newOwnerId = tuple_class->relowner;
16113 }
16114 break;
16115 case RELKIND_PARTITIONED_INDEX:
16116 if (recursing)
16117 break;
16118 ereport(ERROR,
16119 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16120 errmsg("cannot change owner of index \"%s\"",
16121 NameStr(tuple_class->relname)),
16122 errhint("Change the ownership of the index's table instead.")));
16123 break;
16124 case RELKIND_SEQUENCE:
16125 if (!recursing &&
16126 tuple_class->relowner != newOwnerId)
16127 {
16128 /* if it's an owned sequence, disallow changing it by itself */
16129 Oid tableId;
16130 int32 colId;
16131
16132 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16133 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16134 ereport(ERROR,
16135 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16136 errmsg("cannot change owner of sequence \"%s\"",
16137 NameStr(tuple_class->relname)),
16138 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16139 NameStr(tuple_class->relname),
16140 get_rel_name(tableId))));
16141 }
16142 break;
16143 case RELKIND_COMPOSITE_TYPE:
16144 if (recursing)
16145 break;
16146 ereport(ERROR,
16147 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16148 errmsg("\"%s\" is a composite type",
16149 NameStr(tuple_class->relname)),
16150 /* translator: %s is an SQL ALTER command */
16151 errhint("Use %s instead.",
16152 "ALTER TYPE")));
16153 break;
16154 case RELKIND_TOASTVALUE:
16155 if (recursing)
16156 break;
16157 /* FALL THRU */
16158 default:
16159 ereport(ERROR,
16160 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16161 errmsg("cannot change owner of relation \"%s\"",
16162 NameStr(tuple_class->relname)),
16163 errdetail_relkind_not_supported(tuple_class->relkind)));
16164 }
16165
16166 /*
16167 * If the new owner is the same as the existing owner, consider the
16168 * command to have succeeded. This is for dump restoration purposes.
16169 */
16170 if (tuple_class->relowner != newOwnerId)
16171 {
16172 Datum repl_val[Natts_pg_class];
16173 bool repl_null[Natts_pg_class];
16174 bool repl_repl[Natts_pg_class];
16175 Acl *newAcl;
16176 Datum aclDatum;
16177 bool isNull;
16178 HeapTuple newtuple;
16179
16180 /* skip permission checks when recursing to index or toast table */
16181 if (!recursing)
16182 {
16183 /* Superusers can always do it */
16184 if (!superuser())
16185 {
16186 Oid namespaceOid = tuple_class->relnamespace;
16187 AclResult aclresult;
16188
16189 /* Otherwise, must be owner of the existing object */
16190 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16192 RelationGetRelationName(target_rel));
16193
16194 /* Must be able to become new owner */
16195 check_can_set_role(GetUserId(), newOwnerId);
16196
16197 /* New owner must have CREATE privilege on namespace */
16198 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16199 ACL_CREATE);
16200 if (aclresult != ACLCHECK_OK)
16201 aclcheck_error(aclresult, OBJECT_SCHEMA,
16202 get_namespace_name(namespaceOid));
16203 }
16204 }
16205
16206 memset(repl_null, false, sizeof(repl_null));
16207 memset(repl_repl, false, sizeof(repl_repl));
16208
16209 repl_repl[Anum_pg_class_relowner - 1] = true;
16210 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16211
16212 /*
16213 * Determine the modified ACL for the new owner. This is only
16214 * necessary when the ACL is non-null.
16215 */
16216 aclDatum = SysCacheGetAttr(RELOID, tuple,
16217 Anum_pg_class_relacl,
16218 &isNull);
16219 if (!isNull)
16220 {
16221 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16222 tuple_class->relowner, newOwnerId);
16223 repl_repl[Anum_pg_class_relacl - 1] = true;
16224 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16225 }
16226
16227 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16228
16229 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16230
16231 heap_freetuple(newtuple);
16232
16233 /*
16234 * We must similarly update any per-column ACLs to reflect the new
16235 * owner; for neatness reasons that's split out as a subroutine.
16236 */
16237 change_owner_fix_column_acls(relationOid,
16238 tuple_class->relowner,
16239 newOwnerId);
16240
16241 /*
16242 * Update owner dependency reference, if any. A composite type has
16243 * none, because it's tracked for the pg_type entry instead of here;
16244 * indexes and TOAST tables don't have their own entries either.
16245 */
16246 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16247 tuple_class->relkind != RELKIND_INDEX &&
16248 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16249 tuple_class->relkind != RELKIND_TOASTVALUE)
16250 changeDependencyOnOwner(RelationRelationId, relationOid,
16251 newOwnerId);
16252
16253 /*
16254 * Also change the ownership of the table's row type, if it has one
16255 */
16256 if (OidIsValid(tuple_class->reltype))
16257 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16258
16259 /*
16260 * If we are operating on a table or materialized view, also change
16261 * the ownership of any indexes and sequences that belong to the
16262 * relation, as well as its toast table (if it has one).
16263 */
16264 if (tuple_class->relkind == RELKIND_RELATION ||
16265 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16266 tuple_class->relkind == RELKIND_MATVIEW ||
16267 tuple_class->relkind == RELKIND_TOASTVALUE)
16268 {
16269 List *index_oid_list;
16270 ListCell *i;
16271
16272 /* Find all the indexes belonging to this relation */
16273 index_oid_list = RelationGetIndexList(target_rel);
16274
16275 /* For each index, recursively change its ownership */
16276 foreach(i, index_oid_list)
16277 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16278
16279 list_free(index_oid_list);
16280 }
16281
16282 /* If it has a toast table, recurse to change its ownership */
16283 if (tuple_class->reltoastrelid != InvalidOid)
16284 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16285 true, lockmode);
16286
16287 /* If it has dependent sequences, recurse to change them too */
16288 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16289 }
16290
16291 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16292
16293 ReleaseSysCache(tuple);
16294 table_close(class_rel, RowExclusiveLock);
16295 relation_close(target_rel, NoLock);
16296}
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition: acl.c:1119
void check_can_set_role(Oid member, Oid role)
Definition: acl.c:5341
#define DatumGetAclP(X)
Definition: acl.h:120
#define WARNING
Definition: elog.h:36
@ OBJECT_SCHEMA
Definition: parsenodes.h:2361
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:16064
static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
Definition: tablecmds.c:16370
static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
Definition: tablecmds.c:16305
void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
Definition: typecmds.c:4000

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

16440{
16441 Oid indexOid;
16442 ObjectAddress address;
16443
16444 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16445
16446 if (!OidIsValid(indexOid))
16447 ereport(ERROR,
16448 (errcode(ERRCODE_UNDEFINED_OBJECT),
16449 errmsg("index \"%s\" for table \"%s\" does not exist",
16450 indexName, RelationGetRelationName(rel))));
16451
16452 /* Check index is valid to cluster on */
16453 check_index_is_clusterable(rel, indexOid, lockmode);
16454
16455 /* And do the work */
16456 mark_index_clustered(rel, indexOid, false);
16457
16458 ObjectAddressSet(address,
16459 RelationRelationId, indexOid);
16460
16461 return address;
16462}
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 5368 of file tablecmds.c.

5371{
5373 Relation rel = tab->rel;
5374
5375 switch (cmd->subtype)
5376 {
5377 case AT_AddColumn: /* ADD COLUMN */
5378 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5379 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5380 cmd->recurse, false,
5381 lockmode, cur_pass, context);
5382 break;
5383 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5384 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5385 break;
5386 case AT_CookedColumnDefault: /* add a pre-cooked default */
5387 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5388 break;
5389 case AT_AddIdentity:
5390 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5391 cur_pass, context);
5392 Assert(cmd != NULL);
5393 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5394 break;
5395 case AT_SetIdentity:
5396 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5397 cur_pass, context);
5398 Assert(cmd != NULL);
5399 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5400 break;
5401 case AT_DropIdentity:
5402 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5403 break;
5404 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5405 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5406 break;
5407 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5408 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5409 cmd->recurse, false, lockmode);
5410 break;
5411 case AT_SetExpression:
5412 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5413 break;
5414 case AT_DropExpression:
5415 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5416 break;
5417 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5418 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5419 break;
5420 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5421 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5422 break;
5423 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5424 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5425 break;
5426 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5427 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5428 break;
5429 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5430 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5431 lockmode);
5432 break;
5433 case AT_DropColumn: /* DROP COLUMN */
5434 address = ATExecDropColumn(wqueue, rel, cmd->name,
5435 cmd->behavior, cmd->recurse, false,
5436 cmd->missing_ok, lockmode,
5437 NULL);
5438 break;
5439 case AT_AddIndex: /* ADD INDEX */
5440 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5441 lockmode);
5442 break;
5443 case AT_ReAddIndex: /* ADD INDEX */
5444 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5445 lockmode);
5446 break;
5447 case AT_ReAddStatistics: /* ADD STATISTICS */
5448 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5449 true, lockmode);
5450 break;
5451 case AT_AddConstraint: /* ADD CONSTRAINT */
5452 /* Transform the command only during initial examination */
5453 if (cur_pass == AT_PASS_ADD_CONSTR)
5454 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5455 cmd->recurse, lockmode,
5456 cur_pass, context);
5457 /* Depending on constraint type, might be no more work to do now */
5458 if (cmd != NULL)
5459 address =
5460 ATExecAddConstraint(wqueue, tab, rel,
5461 (Constraint *) cmd->def,
5462 cmd->recurse, false, lockmode);
5463 break;
5464 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5465 address =
5466 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5467 true, true, lockmode);
5468 break;
5469 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5470 * constraint */
5471 address =
5472 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5473 ((AlterDomainStmt *) cmd->def)->def,
5474 NULL);
5475 break;
5476 case AT_ReAddComment: /* Re-add existing comment */
5477 address = CommentObject((CommentStmt *) cmd->def);
5478 break;
5479 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5480 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5481 lockmode);
5482 break;
5483 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5484 address = ATExecAlterConstraint(wqueue, rel,
5486 cmd->recurse, lockmode);
5487 break;
5488 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5489 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5490 false, lockmode);
5491 break;
5492 case AT_DropConstraint: /* DROP CONSTRAINT */
5493 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5494 cmd->recurse,
5495 cmd->missing_ok, lockmode);
5496 break;
5497 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5498 /* parse transformation was done earlier */
5499 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5500 break;
5501 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5502 address =
5504 (List *) cmd->def, lockmode);
5505 break;
5506 case AT_ChangeOwner: /* ALTER OWNER */
5508 get_rolespec_oid(cmd->newowner, false),
5509 false, lockmode);
5510 break;
5511 case AT_ClusterOn: /* CLUSTER ON */
5512 address = ATExecClusterOn(rel, cmd->name, lockmode);
5513 break;
5514 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5515 ATExecDropCluster(rel, lockmode);
5516 break;
5517 case AT_SetLogged: /* SET LOGGED */
5518 case AT_SetUnLogged: /* SET UNLOGGED */
5519 break;
5520 case AT_DropOids: /* SET WITHOUT OIDS */
5521 /* nothing to do here, oid columns don't exist anymore */
5522 break;
5523 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5524
5525 /*
5526 * Only do this for partitioned tables, for which this is just a
5527 * catalog change. Tables with storage are handled by Phase 3.
5528 */
5529 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5530 tab->chgAccessMethod)
5532 break;
5533 case AT_SetTableSpace: /* SET TABLESPACE */
5534
5535 /*
5536 * Only do this for partitioned tables and indexes, for which this
5537 * is just a catalog change. Other relation types which have
5538 * storage are handled by Phase 3.
5539 */
5540 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5541 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5543
5544 break;
5545 case AT_SetRelOptions: /* SET (...) */
5546 case AT_ResetRelOptions: /* RESET (...) */
5547 case AT_ReplaceRelOptions: /* replace entire option list */
5548 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5549 break;
5550 case AT_EnableTrig: /* ENABLE TRIGGER name */
5553 cmd->recurse,
5554 lockmode);
5555 break;
5556 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5558 TRIGGER_FIRES_ALWAYS, false,
5559 cmd->recurse,
5560 lockmode);
5561 break;
5562 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5565 cmd->recurse,
5566 lockmode);
5567 break;
5568 case AT_DisableTrig: /* DISABLE TRIGGER name */
5570 TRIGGER_DISABLED, false,
5571 cmd->recurse,
5572 lockmode);
5573 break;
5574 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5577 cmd->recurse,
5578 lockmode);
5579 break;
5580 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5582 TRIGGER_DISABLED, false,
5583 cmd->recurse,
5584 lockmode);
5585 break;
5586 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5589 cmd->recurse,
5590 lockmode);
5591 break;
5592 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5594 TRIGGER_DISABLED, true,
5595 cmd->recurse,
5596 lockmode);
5597 break;
5598
5599 case AT_EnableRule: /* ENABLE RULE name */
5600 ATExecEnableDisableRule(rel, cmd->name,
5601 RULE_FIRES_ON_ORIGIN, lockmode);
5602 break;
5603 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5604 ATExecEnableDisableRule(rel, cmd->name,
5605 RULE_FIRES_ALWAYS, lockmode);
5606 break;
5607 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5608 ATExecEnableDisableRule(rel, cmd->name,
5609 RULE_FIRES_ON_REPLICA, lockmode);
5610 break;
5611 case AT_DisableRule: /* DISABLE RULE name */
5612 ATExecEnableDisableRule(rel, cmd->name,
5613 RULE_DISABLED, lockmode);
5614 break;
5615
5616 case AT_AddInherit:
5617 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5618 break;
5619 case AT_DropInherit:
5620 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5621 break;
5622 case AT_AddOf:
5623 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5624 break;
5625 case AT_DropOf:
5626 ATExecDropOf(rel, lockmode);
5627 break;
5628 case AT_ReplicaIdentity:
5629 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5630 break;
5632 ATExecSetRowSecurity(rel, true);
5633 break;
5635 ATExecSetRowSecurity(rel, false);
5636 break;
5639 break;
5642 break;
5643 case AT_GenericOptions:
5644 ATExecGenericOptions(rel, (List *) cmd->def);
5645 break;
5646 case AT_AttachPartition:
5647 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5648 cur_pass, context);
5649 Assert(cmd != NULL);
5650 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5651 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5652 context);
5653 else
5654 address = ATExecAttachPartitionIdx(wqueue, rel,
5655 ((PartitionCmd *) cmd->def)->name);
5656 break;
5657 case AT_DetachPartition:
5658 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5659 cur_pass, context);
5660 Assert(cmd != NULL);
5661 /* ATPrepCmd ensures it must be a table */
5662 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5663 address = ATExecDetachPartition(wqueue, tab, rel,
5664 ((PartitionCmd *) cmd->def)->name,
5665 ((PartitionCmd *) cmd->def)->concurrent);
5666 break;
5668 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5669 break;
5670 default: /* oops */
5671 elog(ERROR, "unrecognized alter table type: %d",
5672 (int) cmd->subtype);
5673 break;
5674 }
5675
5676 /*
5677 * Report the subcommand to interested event triggers.
5678 */
5679 if (cmd)
5680 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5681
5682 /*
5683 * Bump the command counter to ensure the next subcommand in the sequence
5684 * can see the changes so far
5685 */
5687}
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition: acl.c:5586
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:2493
DropBehavior behavior
Definition: parsenodes.h:2496
bool chgAccessMethod
Definition: tablecmds.c:193
Relation rel
Definition: tablecmds.c:183
static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName, List *options, LOCKMODE lockmode)
Definition: tablecmds.c:15947
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17253
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:18348
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12190
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:8794
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition: tablecmds.c:8118
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8480
static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:12900
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:7734
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9788
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
Definition: tablecmds.c:20900
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:18624
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:17196
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
Definition: tablecmds.c:9043
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:9276
static void ATExecSetRowSecurity(Relation rel, bool rls)
Definition: tablecmds.c:18594
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition: tablecmds.c:8203
static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
Definition: tablecmds.c:16637
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8363
static void ATExecGenericOptions(Relation rel, List *options)
Definition: tablecmds.c:18653
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8898
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode)
Definition: tablecmds.c:14718
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:16471
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
Definition: tablecmds.c:8594
static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
Definition: tablecmds.c:18206
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17815
static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:9696
static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:18480
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:21407
static ObjectAddress ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:9185
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:20240
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
Definition: tablecmds.c:16517
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14004
static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9612
static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:18734
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:16938
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:21568
static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9675
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
Definition: tablecmds.c:16439
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:17214
#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:2938

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

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

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

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

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

20902{
20903 Relation partRel;
20904 ObjectAddress address;
20905 Oid defaultPartOid;
20906
20907 /*
20908 * We must lock the default partition, because detaching this partition
20909 * will change its partition constraint.
20910 */
20911 defaultPartOid =
20913 if (OidIsValid(defaultPartOid))
20914 {
20915 /*
20916 * Concurrent detaching when a default partition exists is not
20917 * supported. The main problem is that the default partition
20918 * constraint would change. And there's a definitional problem: what
20919 * should happen to the tuples that are being inserted that belong to
20920 * the partition being detached? Putting them on the partition being
20921 * detached would be wrong, since they'd become "lost" after the
20922 * detaching completes but we cannot put them in the default partition
20923 * either until we alter its partition constraint.
20924 *
20925 * I think we could solve this problem if we effected the constraint
20926 * change before committing the first transaction. But the lock would
20927 * have to remain AEL and it would cause concurrent query planning to
20928 * be blocked, so changing it that way would be even worse.
20929 */
20930 if (concurrent)
20931 ereport(ERROR,
20932 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20933 errmsg("cannot detach partitions concurrently when a default partition exists")));
20934 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20935 }
20936
20937 /*
20938 * In concurrent mode, the partition is locked with share-update-exclusive
20939 * in the first transaction. This allows concurrent transactions to be
20940 * doing DML to the partition.
20941 */
20942 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20944
20945 /*
20946 * Check inheritance conditions and either delete the pg_inherits row (in
20947 * non-concurrent mode) or just set the inhdetachpending flag.
20948 */
20949 if (!concurrent)
20950 RemoveInheritance(partRel, rel, false);
20951 else
20952 MarkInheritDetached(partRel, rel);
20953
20954 /*
20955 * Ensure that foreign keys still hold after this detach. This keeps
20956 * locks on the referencing tables, which prevents concurrent transactions
20957 * from adding rows that we wouldn't see. For this to work in concurrent
20958 * mode, it is critical that the partition appears as no longer attached
20959 * for the RI queries as soon as the first transaction commits.
20960 */
20962
20963 /*
20964 * Concurrent mode has to work harder; first we add a new constraint to
20965 * the partition that matches the partition constraint. Then we close our
20966 * existing transaction, and in a new one wait for all processes to catch
20967 * up on the catalog updates we've done so far; at that point we can
20968 * complete the operation.
20969 */
20970 if (concurrent)
20971 {
20972 Oid partrelid,
20973 parentrelid;
20974 LOCKTAG tag;
20975 char *parentrelname;
20976 char *partrelname;
20977
20978 /*
20979 * We're almost done now; the only traces that remain are the
20980 * pg_inherits tuple and the partition's relpartbounds. Before we can
20981 * remove those, we need to wait until all transactions that know that
20982 * this is a partition are gone.
20983 */
20984
20985 /*
20986 * Remember relation OIDs to re-acquire them later; and relation names
20987 * too, for error messages if something is dropped in between.
20988 */
20989 partrelid = RelationGetRelid(partRel);
20990 parentrelid = RelationGetRelid(rel);
20991 parentrelname = MemoryContextStrdup(PortalContext,
20993 partrelname = MemoryContextStrdup(PortalContext,
20994 RelationGetRelationName(partRel));
20995
20996 /* Invalidate relcache entries for the parent -- must be before close */
20998
20999 table_close(partRel, NoLock);
21000 table_close(rel, NoLock);
21001 tab->rel = NULL;
21002
21003 /* Make updated catalog entry visible */
21006
21008
21009 /*
21010 * Now wait. This ensures that all queries that were planned
21011 * including the partition are finished before we remove the rest of
21012 * catalog entries. We don't need or indeed want to acquire this
21013 * lock, though -- that would block later queries.
21014 *
21015 * We don't need to concern ourselves with waiting for a lock on the
21016 * partition itself, since we will acquire AccessExclusiveLock below.
21017 */
21018 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
21020
21021 /*
21022 * Now acquire locks in both relations again. Note they may have been
21023 * removed in the meantime, so care is required.
21024 */
21025 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
21026 partRel = try_relation_open(partrelid, AccessExclusiveLock);
21027
21028 /* If the relations aren't there, something bad happened; bail out */
21029 if (rel == NULL)
21030 {
21031 if (partRel != NULL) /* shouldn't happen */
21032 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21033 partrelname);
21034 ereport(ERROR,
21035 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21036 errmsg("partitioned table \"%s\" was removed concurrently",
21037 parentrelname)));
21038 }
21039 if (partRel == NULL)
21040 ereport(ERROR,
21041 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21042 errmsg("partition \"%s\" was removed concurrently", partrelname)));
21043
21044 tab->rel = rel;
21045 }
21046
21047 /*
21048 * Detaching the partition might involve TOAST table access, so ensure we
21049 * have a valid snapshot.
21050 */
21052
21053 /* Do the final part of detaching */
21054 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21055
21057
21058 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21059
21060 /* keep our lock until commit */
21061 table_close(partRel, NoLock);
21062
21063 return address;
21064}
Oid MyDatabaseId
Definition: globals.c:94
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1631
void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
Definition: lmgr.c:911
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition: lock.h:183
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:1746
MemoryContext PortalContext
Definition: mcxt.c:175
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:272
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:682
void PopActiveSnapshot(void)
Definition: snapmgr.c:775
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:88
Definition: lock.h:167
static void MarkInheritDetached(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17857
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:17940
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:21073
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21931
void StartTransactionCommand(void)
Definition: xact.c:3077
void CommitTransactionCommand(void)
Definition: xact.c:3175

References AccessExclusiveLock, ATDetachCheckNoForeignKeyRefs(), CacheInvalidateRelcache(), CommitTransactionCommand(), DetachPartitionFinalize(), elog, ereport, errcode(), errmsg(), ERROR, get_default_oid_from_partdesc(), GetTransactionSnapshot(), list_make1, LockRelationOid(), MarkInheritDetached(), MemoryContextStrdup(), MyDatabaseId, name, NoLock, ObjectAddressSet, OidIsValid, PopActiveSnapshot(), PortalContext, PushActiveSnapshot(), 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 21407 of file tablecmds.c.

21408{
21409 Relation partRel;
21410 ObjectAddress address;
21411 Snapshot snap = GetActiveSnapshot();
21412
21414
21415 /*
21416 * Wait until existing snapshots are gone. This is important if the
21417 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21418 * user could immediately run DETACH FINALIZE without actually waiting for
21419 * existing transactions. We must not complete the detach action until
21420 * all such queries are complete (otherwise we would present them with an
21421 * inconsistent view of catalogs).
21422 */
21423 WaitForOlderSnapshots(snap->xmin, false);
21424
21425 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21426
21427 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21428
21429 table_close(partRel, NoLock);
21430
21431 return address;
21432}
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:434
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:800
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 16471 of file tablecmds.c.

16472{
16473 mark_index_clustered(rel, InvalidOid, false);
16474}

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

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

14007{
14008 Relation conrel;
14009 SysScanDesc scan;
14010 ScanKeyData skey[3];
14011 HeapTuple tuple;
14012 bool found = false;
14013
14014 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14015
14016 /*
14017 * Find and drop the target constraint
14018 */
14019 ScanKeyInit(&skey[0],
14020 Anum_pg_constraint_conrelid,
14021 BTEqualStrategyNumber, F_OIDEQ,
14023 ScanKeyInit(&skey[1],
14024 Anum_pg_constraint_contypid,
14025 BTEqualStrategyNumber, F_OIDEQ,
14027 ScanKeyInit(&skey[2],
14028 Anum_pg_constraint_conname,
14029 BTEqualStrategyNumber, F_NAMEEQ,
14030 CStringGetDatum(constrName));
14031 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14032 true, NULL, 3, skey);
14033
14034 /* There can be at most one matching row */
14035 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14036 {
14037 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14038 missing_ok, lockmode);
14039 found = true;
14040 }
14041
14042 systable_endscan(scan);
14043
14044 if (!found)
14045 {
14046 if (!missing_ok)
14047 ereport(ERROR,
14048 errcode(ERRCODE_UNDEFINED_OBJECT),
14049 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14050 constrName, RelationGetRelationName(rel)));
14051 else
14053 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14054 constrName, RelationGetRelationName(rel)));
14055 }
14056
14058}
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14069

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

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

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

8482{
8483 HeapTuple tuple;
8484 Form_pg_attribute attTup;
8486 Relation attrelation;
8487 ObjectAddress address;
8488 Oid seqid;
8489 ObjectAddress seqaddress;
8490 bool ispartitioned;
8491
8492 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8493 if (ispartitioned && !recurse)
8494 ereport(ERROR,
8495 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8496 errmsg("cannot drop identity from a column of only the partitioned table"),
8497 errhint("Do not specify the ONLY keyword.")));
8498
8499 if (rel->rd_rel->relispartition && !recursing)
8500 ereport(ERROR,
8501 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8502 errmsg("cannot drop identity from a column of a partition"));
8503
8504 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8505 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8506 if (!HeapTupleIsValid(tuple))
8507 ereport(ERROR,
8508 (errcode(ERRCODE_UNDEFINED_COLUMN),
8509 errmsg("column \"%s\" of relation \"%s\" does not exist",
8510 colName, RelationGetRelationName(rel))));
8511
8512 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8513 attnum = attTup->attnum;
8514
8515 if (attnum <= 0)
8516 ereport(ERROR,
8517 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8518 errmsg("cannot alter system column \"%s\"",
8519 colName)));
8520
8521 if (!attTup->attidentity)
8522 {
8523 if (!missing_ok)
8524 ereport(ERROR,
8525 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8526 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8527 colName, RelationGetRelationName(rel))));
8528 else
8529 {
8531 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8532 colName, RelationGetRelationName(rel))));
8533 heap_freetuple(tuple);
8534 table_close(attrelation, RowExclusiveLock);
8535 return InvalidObjectAddress;
8536 }
8537 }
8538
8539 attTup->attidentity = '\0';
8540 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8541
8542 InvokeObjectPostAlterHook(RelationRelationId,
8543 RelationGetRelid(rel),
8544 attTup->attnum);
8545 ObjectAddressSubSet(address, RelationRelationId,
8546 RelationGetRelid(rel), attnum);
8547 heap_freetuple(tuple);
8548
8549 table_close(attrelation, RowExclusiveLock);
8550
8551 /*
8552 * Recurse to drop the identity from column in partitions. Identity is
8553 * not inherited in regular inheritance children so ignore them.
8554 */
8555 if (recurse && ispartitioned)
8556 {
8557 List *children;
8558 ListCell *lc;
8559
8560 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8561
8562 foreach(lc, children)
8563 {
8564 Relation childrel;
8565
8566 childrel = table_open(lfirst_oid(lc), NoLock);
8567 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8568 table_close(childrel, NoLock);
8569 }
8570 }
8571
8572 if (!recursing)
8573 {
8574 /* drop the internal sequence */
8575 seqid = getIdentitySequence(rel, attnum, false);
8576 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8577 RelationRelationId, DEPENDENCY_INTERNAL);
8579 seqaddress.classId = RelationRelationId;
8580 seqaddress.objectId = seqid;
8581 seqaddress.objectSubId = 0;
8583 }
8584
8585 return address;
8586}
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:274
#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 17815 of file tablecmds.c.

17816{
17817 ObjectAddress address;
17818 Relation parent_rel;
17819
17820 if (rel->rd_rel->relispartition)
17821 ereport(ERROR,
17822 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17823 errmsg("cannot change inheritance of a partition")));
17824
17825 /*
17826 * AccessShareLock on the parent is probably enough, seeing that DROP
17827 * TABLE doesn't lock parent tables at all. We need some lock since we'll
17828 * be inspecting the parent's schema.
17829 */
17830 parent_rel = table_openrv(parent, AccessShareLock);
17831
17832 /*
17833 * We don't bother to check ownership of the parent table --- ownership of
17834 * the child is presumed enough rights.
17835 */
17836
17837 /* Off to RemoveInheritance() where most of the work happens */
17838 RemoveInheritance(rel, parent_rel, false);
17839
17840 ObjectAddressSet(address, RelationRelationId,
17841 RelationGetRelid(parent_rel));
17842
17843 /* keep our lock on the parent relation until commit */
17844 table_close(parent_rel, NoLock);
17845
17846 return address;
17847}

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

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

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

18349{
18350 Oid relid = RelationGetRelid(rel);
18351 Relation relationRelation;
18352 HeapTuple tuple;
18353
18354 if (!OidIsValid(rel->rd_rel->reloftype))
18355 ereport(ERROR,
18356 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18357 errmsg("\"%s\" is not a typed table",
18359
18360 /*
18361 * We don't bother to check ownership of the type --- ownership of the
18362 * table is presumed enough rights. No lock required on the type, either.
18363 */
18364
18365 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18367
18368 /* Clear pg_class.reloftype */
18369 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18370 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18371 if (!HeapTupleIsValid(tuple))
18372 elog(ERROR, "cache lookup failed for relation %u", relid);
18373 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18374 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18375
18376 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18377
18378 heap_freetuple(tuple);
18379 table_close(relationRelation, RowExclusiveLock);
18380}

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

17216{
17217 EnableDisableRule(rel, rulename, fires_when);
17218
17219 InvokeObjectPostAlterHook(RelationRelationId,
17220 RelationGetRelid(rel), 0);
17221}
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 17196 of file tablecmds.c.

17199{
17200 EnableDisableTrigger(rel, trigname, InvalidOid,
17201 fires_when, skip_system, recurse,
17202 lockmode);
17203
17204 InvokeObjectPostAlterHook(RelationRelationId,
17205 RelationGetRelid(rel), 0);
17206}
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 18624 of file tablecmds.c.

18625{
18626 Relation pg_class;
18627 Oid relid;
18628 HeapTuple tuple;
18629
18630 relid = RelationGetRelid(rel);
18631
18632 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18633
18634 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18635
18636 if (!HeapTupleIsValid(tuple))
18637 elog(ERROR, "cache lookup failed for relation %u", relid);
18638
18639 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18640 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18641
18642 InvokeObjectPostAlterHook(RelationRelationId,
18643 RelationGetRelid(rel), 0);
18644
18645 table_close(pg_class, RowExclusiveLock);
18646 heap_freetuple(tuple);
18647}

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

18654{
18655 Relation ftrel;
18656 ForeignServer *server;
18657 ForeignDataWrapper *fdw;
18658 HeapTuple tuple;
18659 bool isnull;
18660 Datum repl_val[Natts_pg_foreign_table];
18661 bool repl_null[Natts_pg_foreign_table];
18662 bool repl_repl[Natts_pg_foreign_table];
18663 Datum datum;
18664 Form_pg_foreign_table tableform;
18665
18666 if (options == NIL)
18667 return;
18668
18669 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18670
18671 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18672 ObjectIdGetDatum(rel->rd_id));
18673 if (!HeapTupleIsValid(tuple))
18674 ereport(ERROR,
18675 (errcode(ERRCODE_UNDEFINED_OBJECT),
18676 errmsg("foreign table \"%s\" does not exist",
18678 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18679 server = GetForeignServer(tableform->ftserver);
18680 fdw = GetForeignDataWrapper(server->fdwid);
18681
18682 memset(repl_val, 0, sizeof(repl_val));
18683 memset(repl_null, false, sizeof(repl_null));
18684 memset(repl_repl, false, sizeof(repl_repl));
18685
18686 /* Extract the current options */
18687 datum = SysCacheGetAttr(FOREIGNTABLEREL,
18688 tuple,
18689 Anum_pg_foreign_table_ftoptions,
18690 &isnull);
18691 if (isnull)
18692 datum = PointerGetDatum(NULL);
18693
18694 /* Transform the options */
18695 datum = transformGenericOptions(ForeignTableRelationId,
18696 datum,
18697 options,
18698 fdw->fdwvalidator);
18699
18700 if (DatumGetPointer(datum) != NULL)
18701 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18702 else
18703 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18704
18705 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18706
18707 /* Everything looks good - update the tuple */
18708
18709 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18710 repl_val, repl_null, repl_repl);
18711
18712 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18713
18714 /*
18715 * Invalidate relcache so that all sessions will refresh any cached plans
18716 * that might depend on the old options.
18717 */
18719
18720 InvokeObjectPostAlterHook(ForeignTableRelationId,
18721 RelationGetRelid(rel), 0);
18722
18724
18725 heap_freetuple(tuple);
18726}

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

18481{
18482 Oid indexOid;
18483 Relation indexRel;
18484 int key;
18485
18486 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18487 {
18488 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18489 return;
18490 }
18491 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18492 {
18493 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18494 return;
18495 }
18496 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18497 {
18498 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18499 return;
18500 }
18501 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18502 {
18503 /* fallthrough */ ;
18504 }
18505 else
18506 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18507
18508 /* Check that the index exists */
18509 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18510 if (!OidIsValid(indexOid))
18511 ereport(ERROR,
18512 (errcode(ERRCODE_UNDEFINED_OBJECT),
18513 errmsg("index \"%s\" for table \"%s\" does not exist",
18514 stmt->name, RelationGetRelationName(rel))));
18515
18516 indexRel = index_open(indexOid, ShareLock);
18517
18518 /* Check that the index is on the relation we're altering. */
18519 if (indexRel->rd_index == NULL ||
18520 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18521 ereport(ERROR,
18522 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18523 errmsg("\"%s\" is not an index for table \"%s\"",
18524 RelationGetRelationName(indexRel),
18526
18527 /*
18528 * The AM must support uniqueness, and the index must in fact be unique.
18529 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18530 * exclusion), we can use that too.
18531 */
18532 if ((!indexRel->rd_indam->amcanunique ||
18533 !indexRel->rd_index->indisunique) &&
18534 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18535 ereport(ERROR,
18536 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18537 errmsg("cannot use non-unique index \"%s\" as replica identity",
18538 RelationGetRelationName(indexRel))));
18539 /* Deferred indexes are not guaranteed to be always unique. */
18540 if (!indexRel->rd_index->indimmediate)
18541 ereport(ERROR,
18542 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18543 errmsg("cannot use non-immediate index \"%s\" as replica identity",
18544 RelationGetRelationName(indexRel))));
18545 /* Expression indexes aren't supported. */
18546 if (RelationGetIndexExpressions(indexRel) != NIL)
18547 ereport(ERROR,
18548 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18549 errmsg("cannot use expression index \"%s\" as replica identity",
18550 RelationGetRelationName(indexRel))));
18551 /* Predicate indexes aren't supported. */
18552 if (RelationGetIndexPredicate(indexRel) != NIL)
18553 ereport(ERROR,
18554 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18555 errmsg("cannot use partial index \"%s\" as replica identity",
18556 RelationGetRelationName(indexRel))));
18557
18558 /* Check index for nullable columns. */
18559 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18560 {
18561 int16 attno = indexRel->rd_index->indkey.values[key];
18562 Form_pg_attribute attr;
18563
18564 /*
18565 * Reject any other system columns. (Going forward, we'll disallow
18566 * indexes containing such columns in the first place, but they might
18567 * exist in older branches.)
18568 */
18569 if (attno <= 0)
18570 ereport(ERROR,
18571 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18572 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18573 RelationGetRelationName(indexRel), attno)));
18574
18575 attr = TupleDescAttr(rel->rd_att, attno - 1);
18576 if (!attr->attnotnull)
18577 ereport(ERROR,
18578 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18579 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18580 RelationGetRelationName(indexRel),
18581 NameStr(attr->attname))));
18582 }
18583
18584 /* This index is suitable for use as a replica identity. Mark it. */
18585 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18586
18587 index_close(indexRel, NoLock);
18588}
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:534
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5210
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5097
bool amcanunique
Definition: amapi.h:258
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:18392

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

16518{
16519 Relation pg_class;
16520 Oid oldAccessMethodId;
16521 HeapTuple tuple;
16522 Form_pg_class rd_rel;
16523 Oid reloid = RelationGetRelid(rel);
16524
16525 /*
16526 * Shouldn't be called on relations having storage; these are processed in
16527 * phase 3.
16528 */
16529 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16530
16531 /* Get a modifiable copy of the relation's pg_class row. */
16532 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16533
16534 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16535 if (!HeapTupleIsValid(tuple))
16536 elog(ERROR, "cache lookup failed for relation %u", reloid);
16537 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16538
16539 /* Update the pg_class row. */
16540 oldAccessMethodId = rd_rel->relam;
16541 rd_rel->relam = newAccessMethodId;
16542
16543 /* Leave if no update required */
16544 if (rd_rel->relam == oldAccessMethodId)
16545 {
16546 heap_freetuple(tuple);
16547 table_close(pg_class, RowExclusiveLock);
16548 return;
16549 }
16550
16551 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16552
16553 /*
16554 * Update the dependency on the new access method. No dependency is added
16555 * if the new access method is InvalidOid (default case). Be very careful
16556 * that this has to compare the previous value stored in pg_class with the
16557 * new one.
16558 */
16559 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16560 {
16561 ObjectAddress relobj,
16562 referenced;
16563
16564 /*
16565 * New access method is defined and there was no dependency
16566 * previously, so record a new one.
16567 */
16568 ObjectAddressSet(relobj, RelationRelationId, reloid);
16569 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16570 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16571 }
16572 else if (OidIsValid(oldAccessMethodId) &&
16573 !OidIsValid(rd_rel->relam))
16574 {
16575 /*
16576 * There was an access method defined, and no new one, so just remove
16577 * the existing dependency.
16578 */
16579 deleteDependencyRecordsForClass(RelationRelationId, reloid,
16580 AccessMethodRelationId,
16582 }
16583 else
16584 {
16585 Assert(OidIsValid(oldAccessMethodId) &&
16586 OidIsValid(rd_rel->relam));
16587
16588 /* Both are valid, so update the dependency */
16589 changeDependencyFor(RelationRelationId, reloid,
16590 AccessMethodRelationId,
16591 oldAccessMethodId, rd_rel->relam);
16592 }
16593
16594 /* make the relam and dependency changes visible */
16596
16597 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16598
16599 heap_freetuple(tuple);
16600 table_close(pg_class, RowExclusiveLock);
16601}

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

18738{
18739 Relation attrel;
18740 HeapTuple tuple;
18741 Form_pg_attribute atttableform;
18743 char *compression;
18744 char cmethod;
18745 ObjectAddress address;
18746
18747 compression = strVal(newValue);
18748
18749 attrel = table_open(AttributeRelationId, RowExclusiveLock);
18750
18751 /* copy the cache entry so we can scribble on it below */
18752 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18753 if (!HeapTupleIsValid(tuple))
18754 ereport(ERROR,
18755 (errcode(ERRCODE_UNDEFINED_COLUMN),
18756 errmsg("column \"%s\" of relation \"%s\" does not exist",
18757 column, RelationGetRelationName(rel))));
18758
18759 /* prevent them from altering a system attribute */
18760 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18761 attnum = atttableform->attnum;
18762 if (attnum <= 0)
18763 ereport(ERROR,
18764 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18765 errmsg("cannot alter system column \"%s\"", column)));
18766
18767 /*
18768 * Check that column type is compressible, then get the attribute
18769 * compression method code
18770 */
18771 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18772
18773 /* update pg_attribute entry */
18774 atttableform->attcompression = cmethod;
18775 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18776
18777 InvokeObjectPostAlterHook(RelationRelationId,
18778 RelationGetRelid(rel),
18779 attnum);
18780
18781 /*
18782 * Apply the change to indexes as well (only for simple index columns,
18783 * matching behavior of index.c ConstructTupleDescriptor()).
18784 */
18785 SetIndexStorageProperties(rel, attrel, attnum,
18786 false, 0,
18787 true, cmethod,
18788 lockmode);
18789
18790 heap_freetuple(tuple);
18791
18793
18794 /* make changes visible */
18796
18797 ObjectAddressSubSet(address, RelationRelationId,
18798 RelationGetRelid(rel), attnum);
18799 return address;
18800}
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:21980
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:9122

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

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

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

Referenced by ATExecCmd().

◆ ATExecSetIdentity()

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

Definition at line 8363 of file tablecmds.c.

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

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

Referenced by ATExecCmd(), and ATExecSetIdentity().

◆ ATExecSetNotNull()

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

Definition at line 7905 of file tablecmds.c.

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

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

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

◆ ATExecSetOptions()

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

Definition at line 9043 of file tablecmds.c.

9045{
9046 Relation attrelation;
9047 HeapTuple tuple,
9048 newtuple;
9049 Form_pg_attribute attrtuple;
9051 Datum datum,
9052 newOptions;
9053 bool isnull;
9054 ObjectAddress address;
9055 Datum repl_val[Natts_pg_attribute];
9056 bool repl_null[Natts_pg_attribute];
9057 bool repl_repl[Natts_pg_attribute];
9058
9059 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9060
9061 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9062
9063 if (!HeapTupleIsValid(tuple))
9064 ereport(ERROR,
9065 (errcode(ERRCODE_UNDEFINED_COLUMN),
9066 errmsg("column \"%s\" of relation \"%s\" does not exist",
9067 colName, RelationGetRelationName(rel))));
9068 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9069
9070 attnum = attrtuple->attnum;
9071 if (attnum <= 0)
9072 ereport(ERROR,
9073 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9074 errmsg("cannot alter system column \"%s\"",
9075 colName)));
9076
9077 /* Generate new proposed attoptions (text array) */
9078 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9079 &isnull);
9080 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9081 castNode(List, options), NULL, NULL,
9082 false, isReset);
9083 /* Validate new options */
9084 (void) attribute_reloptions(newOptions, true);
9085
9086 /* Build new tuple. */
9087 memset(repl_null, false, sizeof(repl_null));
9088 memset(repl_repl, false, sizeof(repl_repl));
9089 if (newOptions != (Datum) 0)
9090 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9091 else
9092 repl_null[Anum_pg_attribute_attoptions - 1] = true;
9093 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9094 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9095 repl_val, repl_null, repl_repl);
9096
9097 /* Update system catalog. */
9098 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9099
9100 InvokeObjectPostAlterHook(RelationRelationId,
9101 RelationGetRelid(rel),
9102 attrtuple->attnum);
9103 ObjectAddressSubSet(address, RelationRelationId,
9104 RelationGetRelid(rel), attnum);
9105
9106 heap_freetuple(newtuple);
9107
9108 ReleaseSysCache(tuple);
9109
9110 table_close(attrelation, RowExclusiveLock);
9111
9112 return address;
9113}
Datum transformRelOptions(Datum oldOptions, List *defList, const char *nameSpace, const char *const validnsps[], bool acceptOidsOff, bool isReset)
Definition: reloptions.c:1176
bytea * attribute_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2116

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

16639{
16640 Oid relid;
16641 Relation pgclass;
16642 HeapTuple tuple;
16643 HeapTuple newtuple;
16644 Datum datum;
16645 Datum newOptions;
16646 Datum repl_val[Natts_pg_class];
16647 bool repl_null[Natts_pg_class];
16648 bool repl_repl[Natts_pg_class];
16649 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16650
16651 if (defList == NIL && operation != AT_ReplaceRelOptions)
16652 return; /* nothing to do */
16653
16654 pgclass = table_open(RelationRelationId, RowExclusiveLock);
16655
16656 /* Fetch heap tuple */
16657 relid = RelationGetRelid(rel);
16658 tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16659 if (!HeapTupleIsValid(tuple))
16660 elog(ERROR, "cache lookup failed for relation %u", relid);
16661
16662 if (operation == AT_ReplaceRelOptions)
16663 {
16664 /*
16665 * If we're supposed to replace the reloptions list, we just pretend
16666 * there were none before.
16667 */
16668 datum = (Datum) 0;
16669 }
16670 else
16671 {
16672 bool isnull;
16673
16674 /* Get the old reloptions */
16675 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16676 &isnull);
16677 if (isnull)
16678 datum = (Datum) 0;
16679 }
16680
16681 /* Generate new proposed reloptions (text array) */
16682 newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16683 operation == AT_ResetRelOptions);
16684
16685 /* Validate */
16686 switch (rel->rd_rel->relkind)
16687 {
16688 case RELKIND_RELATION:
16689 case RELKIND_MATVIEW:
16690 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16691 break;
16692 case RELKIND_PARTITIONED_TABLE:
16693 (void) partitioned_table_reloptions(newOptions, true);
16694 break;
16695 case RELKIND_VIEW:
16696 (void) view_reloptions(newOptions, true);
16697 break;
16698 case RELKIND_INDEX:
16699 case RELKIND_PARTITIONED_INDEX:
16700 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16701 break;
16702 case RELKIND_TOASTVALUE:
16703 /* fall through to error -- shouldn't ever get here */
16704 default:
16705 ereport(ERROR,
16706 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16707 errmsg("cannot set options for relation \"%s\"",
16709 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16710 break;
16711 }
16712
16713 /* Special-case validation of view options */
16714 if (rel->rd_rel->relkind == RELKIND_VIEW)
16715 {
16716 Query *view_query = get_view_query(rel);
16717 List *view_options = untransformRelOptions(newOptions);
16718 ListCell *cell;
16719 bool check_option = false;
16720
16721 foreach(cell, view_options)
16722 {
16723 DefElem *defel = (DefElem *) lfirst(cell);
16724
16725 if (strcmp(defel->defname, "check_option") == 0)
16726 check_option = true;
16727 }
16728
16729 /*
16730 * If the check option is specified, look to see if the view is
16731 * actually auto-updatable or not.
16732 */
16733 if (check_option)
16734 {
16735 const char *view_updatable_error =
16736 view_query_is_auto_updatable(view_query, true);
16737
16738 if (view_updatable_error)
16739 ereport(ERROR,
16740 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16741 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16742 errhint("%s", _(view_updatable_error))));
16743 }
16744 }
16745
16746 /*
16747 * All we need do here is update the pg_class row; the new options will be
16748 * propagated into relcaches during post-commit cache inval.
16749 */
16750 memset(repl_val, 0, sizeof(repl_val));
16751 memset(repl_null, false, sizeof(repl_null));
16752 memset(repl_repl, false, sizeof(repl_repl));
16753
16754 if (newOptions != (Datum) 0)
16755 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16756 else
16757 repl_null[Anum_pg_class_reloptions - 1] = true;
16758
16759 repl_repl[Anum_pg_class_reloptions - 1] = true;
16760
16761 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16762 repl_val, repl_null, repl_repl);
16763
16764 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16765 UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16766
16767 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16768
16769 heap_freetuple(newtuple);
16770
16771 ReleaseSysCache(tuple);
16772
16773 /* repeat the whole exercise for the toast table, if there's one */
16774 if (OidIsValid(rel->rd_rel->reltoastrelid))
16775 {
16776 Relation toastrel;
16777 Oid toastid = rel->rd_rel->reltoastrelid;
16778
16779 toastrel = table_open(toastid, lockmode);
16780
16781 /* Fetch heap tuple */
16782 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16783 if (!HeapTupleIsValid(tuple))
16784 elog(ERROR, "cache lookup failed for relation %u", toastid);
16785
16786 if (operation == AT_ReplaceRelOptions)
16787 {
16788 /*
16789 * If we're supposed to replace the reloptions list, we just
16790 * pretend there were none before.
16791 */
16792 datum = (Datum) 0;
16793 }
16794 else
16795 {
16796 bool isnull;
16797
16798 /* Get the old reloptions */
16799 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16800 &isnull);
16801 if (isnull)
16802 datum = (Datum) 0;
16803 }
16804
16805 newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16806 false, operation == AT_ResetRelOptions);
16807
16808 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16809
16810 memset(repl_val, 0, sizeof(repl_val));
16811 memset(repl_null, false, sizeof(repl_null));
16812 memset(repl_repl, false, sizeof(repl_repl));
16813
16814 if (newOptions != (Datum) 0)
16815 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16816 else
16817 repl_null[Anum_pg_class_reloptions - 1] = true;
16818
16819 repl_repl[Anum_pg_class_reloptions - 1] = true;
16820
16821 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16822 repl_val, repl_null, repl_repl);
16823
16824 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16825
16826 InvokeObjectPostAlterHookArg(RelationRelationId,
16827 RelationGetRelid(toastrel), 0,
16828 InvalidOid, true);
16829
16830 heap_freetuple(newtuple);
16831
16832 ReleaseSysCache(tuple);
16833
16834 table_close(toastrel, NoLock);
16835 }
16836
16837 table_close(pgclass, RowExclusiveLock);
16838}
#define _(x)
Definition: elog.c:91
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:200
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1360
bytea * view_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2045
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
Definition: reloptions.c:2101
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2031
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
Definition: reloptions.c:2066
#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:304
HeapTuple SearchSysCacheLocked1(int cacheId, Datum key1)
Definition: syscache.c:282

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

18595{
18596 Relation pg_class;
18597 Oid relid;
18598 HeapTuple tuple;
18599
18600 relid = RelationGetRelid(rel);
18601
18602 /* Pull the record for this relation and update it */
18603 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18604
18605 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18606
18607 if (!HeapTupleIsValid(tuple))
18608 elog(ERROR, "cache lookup failed for relation %u", relid);
18609
18610 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18611 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18612
18613 InvokeObjectPostAlterHook(RelationRelationId,
18614 RelationGetRelid(rel), 0);
18615
18616 table_close(pg_class, RowExclusiveLock);
18617 heap_freetuple(tuple);
18618}

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

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

References attnum, CatalogTupleUpdate(), ereport, errcode(), errhint(), errmsg(), ERROR, GETSTRUCT(), heap_freetuple(), heap_modify_tuple(), HeapTupleIsValid, Int16GetDatum(), 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 9185 of file tablecmds.c.

9186{
9187 Relation attrelation;
9188 HeapTuple tuple;
9189 Form_pg_attribute attrtuple;
9191 ObjectAddress address;
9192
9193 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9194
9195 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9196
9197 if (!HeapTupleIsValid(tuple))
9198 ereport(ERROR,
9199 (errcode(ERRCODE_UNDEFINED_COLUMN),
9200 errmsg("column \"%s\" of relation \"%s\" does not exist",
9201 colName, RelationGetRelationName(rel))));
9202 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9203
9204 attnum = attrtuple->attnum;
9205 if (attnum <= 0)
9206 ereport(ERROR,
9207 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9208 errmsg("cannot alter system column \"%s\"",
9209 colName)));
9210
9211 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9212
9213 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9214
9215 InvokeObjectPostAlterHook(RelationRelationId,
9216 RelationGetRelid(rel),
9217 attrtuple->attnum);
9218
9219 /*
9220 * Apply the change to indexes as well (only for simple index columns,
9221 * matching behavior of index.c ConstructTupleDescriptor()).
9222 */
9223 SetIndexStorageProperties(rel, attrelation, attnum,
9224 true, attrtuple->attstorage,
9225 false, 0,
9226 lockmode);
9227
9228 heap_freetuple(tuple);
9229
9230 table_close(attrelation, RowExclusiveLock);
9231
9232 ObjectAddressSubSet(address, RelationRelationId,
9233 RelationGetRelid(rel), attnum);
9234 return address;
9235}
static char GetAttributeStorage(Oid atttypid, const char *storagemode)
Definition: tablecmds.c:22018

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

16846{
16847 Relation rel;
16848 Oid reltoastrelid;
16849 RelFileNumber newrelfilenumber;
16850 RelFileLocator newrlocator;
16851 List *reltoastidxids = NIL;
16852 ListCell *lc;
16853
16854 /*
16855 * Need lock here in case we are recursing to toast table or index
16856 */
16857 rel = relation_open(tableOid, lockmode);
16858
16859 /* Check first if relation can be moved to new tablespace */
16860 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16861 {
16862 InvokeObjectPostAlterHook(RelationRelationId,
16863 RelationGetRelid(rel), 0);
16864 relation_close(rel, NoLock);
16865 return;
16866 }
16867
16868 reltoastrelid = rel->rd_rel->reltoastrelid;
16869 /* Fetch the list of indexes on toast relation if necessary */
16870 if (OidIsValid(reltoastrelid))
16871 {
16872 Relation toastRel = relation_open(reltoastrelid, lockmode);
16873
16874 reltoastidxids = RelationGetIndexList(toastRel);
16875 relation_close(toastRel, lockmode);
16876 }
16877
16878 /*
16879 * Relfilenumbers are not unique in databases across tablespaces, so we
16880 * need to allocate a new one in the new tablespace.
16881 */
16882 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16883 rel->rd_rel->relpersistence);
16884
16885 /* Open old and new relation */
16886 newrlocator = rel->rd_locator;
16887 newrlocator.relNumber = newrelfilenumber;
16888 newrlocator.spcOid = newTableSpace;
16889
16890 /* hand off to AM to actually create new rel storage and copy the data */
16891 if (rel->rd_rel->relkind == RELKIND_INDEX)
16892 {
16893 index_copy_data(rel, newrlocator);
16894 }
16895 else
16896 {
16897 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16898 table_relation_copy_data(rel, &newrlocator);
16899 }
16900
16901 /*
16902 * Update the pg_class row.
16903 *
16904 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16905 * executed on pg_class or its indexes (the above copy wouldn't contain
16906 * the updated pg_class entry), but that's forbidden with
16907 * CheckRelationTableSpaceMove().
16908 */
16909 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16910
16911 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16912
16914
16915 relation_close(rel, NoLock);
16916
16917 /* Make sure the reltablespace change is visible */
16919
16920 /* Move associated toast relation and/or indexes, too */
16921 if (OidIsValid(reltoastrelid))
16922 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16923 foreach(lc, reltoastidxids)
16924 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16925
16926 /* Clean up */
16927 list_free(reltoastidxids);
16928}
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition: catalog.c:557
void RelationAssumeNewRelfilelocator(Relation relation)
Definition: relcache.c:3976
Oid RelFileNumber
Definition: relpath.h:25
RelFileNumber relNumber
static void table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
Definition: tableam.h:1630
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:16845
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3685
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3742
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:17139

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

16939{
16940 /*
16941 * Shouldn't be called on relations having storage; these are processed in
16942 * phase 3.
16943 */
16944 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16945
16946 /* check if relation can be moved to its new tablespace */
16947 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16948 {
16949 InvokeObjectPostAlterHook(RelationRelationId,
16950 RelationGetRelid(rel),
16951 0);
16952 return;
16953 }
16954
16955 /* Update can be done, so change reltablespace */
16956 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16957
16958 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16959
16960 /* Make sure the reltablespace change is visible */
16962}

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

12902{
12903 Relation conrel;
12904 SysScanDesc scan;
12905 ScanKeyData skey[3];
12906 HeapTuple tuple;
12908 ObjectAddress address;
12909
12910 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12911
12912 /*
12913 * Find and check the target constraint
12914 */
12915 ScanKeyInit(&skey[0],
12916 Anum_pg_constraint_conrelid,
12917 BTEqualStrategyNumber, F_OIDEQ,
12919 ScanKeyInit(&skey[1],
12920 Anum_pg_constraint_contypid,
12921 BTEqualStrategyNumber, F_OIDEQ,
12923 ScanKeyInit(&skey[2],
12924 Anum_pg_constraint_conname,
12925 BTEqualStrategyNumber, F_NAMEEQ,
12926 CStringGetDatum(constrName));
12927 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12928 true, NULL, 3, skey);
12929
12930 /* There can be at most one matching row */
12931 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12932 ereport(ERROR,
12933 (errcode(ERRCODE_UNDEFINED_OBJECT),
12934 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12935 constrName, RelationGetRelationName(rel))));
12936
12937 con = (Form_pg_constraint) GETSTRUCT(tuple);
12938 if (con->contype != CONSTRAINT_FOREIGN &&
12939 con->contype != CONSTRAINT_CHECK &&
12940 con->contype != CONSTRAINT_NOTNULL)
12941 ereport(ERROR,
12942 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12943 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
12944 constrName, RelationGetRelationName(rel)),
12945 errdetail("This operation is not supported for this type of constraint."));
12946
12947 if (!con->conenforced)
12948 ereport(ERROR,
12949 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12950 errmsg("cannot validate NOT ENFORCED constraint")));
12951
12952 if (!con->convalidated)
12953 {
12954 if (con->contype == CONSTRAINT_FOREIGN)
12955 {
12956 QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12957 tuple, lockmode);
12958 }
12959 else if (con->contype == CONSTRAINT_CHECK)
12960 {
12961 QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12962 tuple, recurse, recursing, lockmode);
12963 }
12964 else if (con->contype == CONSTRAINT_NOTNULL)
12965 {
12966 QueueNNConstraintValidation(wqueue, conrel, rel,
12967 tuple, recurse, recursing, lockmode);
12968 }
12969
12970 ObjectAddressSet(address, ConstraintRelationId, con->oid);
12971 }
12972 else
12973 address = InvalidObjectAddress; /* already validated */
12974
12975 systable_endscan(scan);
12976
12978
12979 return address;
12980}
static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12990
static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13212
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13109

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

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

◆ ATGetQueueEntry()

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

Definition at line 6554 of file tablecmds.c.

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

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

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

◆ ATParseTransformCmd()

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

Definition at line 5703 of file tablecmds.c.

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

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

15429{
15430 ObjectAddress obj;
15431 ObjectAddresses *objects;
15432 ListCell *def_item;
15433 ListCell *oid_item;
15434
15435 /*
15436 * Collect all the constraints and indexes to drop so we can process them
15437 * in a single call. That way we don't have to worry about dependencies
15438 * among them.
15439 */
15440 objects = new_object_addresses();
15441
15442 /*
15443 * Re-parse the index and constraint definitions, and attach them to the
15444 * appropriate work queue entries. We do this before dropping because in
15445 * the case of a constraint on another table, we might not yet have
15446 * exclusive lock on the table the constraint is attached to, and we need
15447 * to get that before reparsing/dropping. (That's possible at least for
15448 * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15449 * requires a dependency on the target table's composite type in the other
15450 * table's constraint expressions.)
15451 *
15452 * We can't rely on the output of deparsing to tell us which relation to
15453 * operate on, because concurrent activity might have made the name
15454 * resolve differently. Instead, we've got to use the OID of the
15455 * constraint or index we're processing to figure out which relation to
15456 * operate on.
15457 */
15458 forboth(oid_item, tab->changedConstraintOids,
15459 def_item, tab->changedConstraintDefs)
15460 {
15461 Oid oldId = lfirst_oid(oid_item);
15462 HeapTuple tup;
15464 Oid relid;
15465 Oid confrelid;
15466 bool conislocal;
15467
15468 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15469 if (!HeapTupleIsValid(tup)) /* should not happen */
15470 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15471 con = (Form_pg_constraint) GETSTRUCT(tup);
15472 if (OidIsValid(con->conrelid))
15473 relid = con->conrelid;
15474 else
15475 {
15476 /* must be a domain constraint */
15477 relid = get_typ_typrelid(getBaseType(con->contypid));
15478 if (!OidIsValid(relid))
15479 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15480 }
15481 confrelid = con->confrelid;
15482 conislocal = con->conislocal;
15483 ReleaseSysCache(tup);
15484
15485 ObjectAddressSet(obj, ConstraintRelationId, oldId);
15486 add_exact_object_address(&obj, objects);
15487
15488 /*
15489 * If the constraint is inherited (only), we don't want to inject a
15490 * new definition here; it'll get recreated when
15491 * ATAddCheckNNConstraint recurses from adding the parent table's
15492 * constraint. But we had to carry the info this far so that we can
15493 * drop the constraint below.
15494 */
15495 if (!conislocal)
15496 continue;
15497
15498 /*
15499 * When rebuilding another table's constraint that references the
15500 * table we're modifying, we might not yet have any lock on the other
15501 * table, so get one now. We'll need AccessExclusiveLock for the DROP
15502 * CONSTRAINT step, so there's no value in asking for anything weaker.
15503 */
15504 if (relid != tab->relid)
15506
15507 ATPostAlterTypeParse(oldId, relid, confrelid,
15508 (char *) lfirst(def_item),
15509 wqueue, lockmode, tab->rewrite);
15510 }
15511 forboth(oid_item, tab->changedIndexOids,
15512 def_item, tab->changedIndexDefs)
15513 {
15514 Oid oldId = lfirst_oid(oid_item);
15515 Oid relid;
15516
15517 relid = IndexGetRelation(oldId, false);
15518
15519 /*
15520 * As above, make sure we have lock on the index's table if it's not
15521 * the same table.
15522 */
15523 if (relid != tab->relid)
15525
15526 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15527 (char *) lfirst(def_item),
15528 wqueue, lockmode, tab->rewrite);
15529
15530 ObjectAddressSet(obj, RelationRelationId, oldId);
15531 add_exact_object_address(&obj, objects);
15532 }
15533
15534 /* add dependencies for new statistics */
15535 forboth(oid_item, tab->changedStatisticsOids,
15536 def_item, tab->changedStatisticsDefs)
15537 {
15538 Oid oldId = lfirst_oid(oid_item);
15539 Oid relid;
15540
15541 relid = StatisticsGetRelation(oldId, false);
15542
15543 /*
15544 * As above, make sure we have lock on the statistics object's table
15545 * if it's not the same table. However, we take
15546 * ShareUpdateExclusiveLock here, aligning with the lock level used in
15547 * CreateStatistics and RemoveStatisticsById.
15548 *
15549 * CAUTION: this should be done after all cases that grab
15550 * AccessExclusiveLock, else we risk causing deadlock due to needing
15551 * to promote our table lock.
15552 */
15553 if (relid != tab->relid)
15555
15556 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15557 (char *) lfirst(def_item),
15558 wqueue, lockmode, tab->rewrite);
15559
15560 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15561 add_exact_object_address(&obj, objects);
15562 }
15563
15564 /*
15565 * Queue up command to restore replica identity index marking
15566 */
15567 if (tab->replicaIdentityIndex)
15568 {
15571
15572 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15573 subcmd->name = tab->replicaIdentityIndex;
15575 cmd->def = (Node *) subcmd;
15576
15577 /* do it after indexes and constraints */
15580 }
15581
15582 /*
15583 * Queue up command to restore marking of index used for cluster.
15584 */
15585 if (tab->clusterOnIndex)
15586 {
15588
15589 cmd->subtype = AT_ClusterOn;
15590 cmd->name = tab->clusterOnIndex;
15591
15592 /* do it after indexes and constraints */
15595 }
15596
15597 /*
15598 * It should be okay to use DROP_RESTRICT here, since nothing else should
15599 * be depending on these objects.
15600 */
15602
15603 free_object_addresses(objects);
15604
15605 /*
15606 * The objects will get recreated during subsequent passes over the work
15607 * queue.
15608 */
15609}
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3583
Oid get_typ_typrelid(Oid typid)
Definition: lsyscache.c:2898
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition: statscmds.c:938
List * changedConstraintDefs
Definition: tablecmds.c:204
List * changedStatisticsDefs
Definition: tablecmds.c:210
char * clusterOnIndex
Definition: tablecmds.c:208
char * replicaIdentityIndex
Definition: tablecmds.c:207
List * changedStatisticsOids
Definition: tablecmds.c:209
List * changedIndexDefs
Definition: tablecmds.c:206
List * changedIndexOids
Definition: tablecmds.c:205
List * changedConstraintOids
Definition: tablecmds.c:203
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
Definition: tablecmds.c:15620

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

15622{
15623 List *raw_parsetree_list;
15624 List *querytree_list;
15625 ListCell *list_item;
15626 Relation rel;
15627
15628 /*
15629 * We expect that we will get only ALTER TABLE and CREATE INDEX
15630 * statements. Hence, there is no need to pass them through
15631 * parse_analyze_*() or the rewriter, but instead we need to pass them
15632 * through parse_utilcmd.c to make them ready for execution.
15633 */
15634 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15635 querytree_list = NIL;
15636 foreach(list_item, raw_parsetree_list)
15637 {
15638 RawStmt *rs = lfirst_node(RawStmt, list_item);
15639 Node *stmt = rs->stmt;
15640
15641 if (IsA(stmt, IndexStmt))
15642 querytree_list = lappend(querytree_list,
15643 transformIndexStmt(oldRelId,
15644 (IndexStmt *) stmt,
15645 cmd));
15646 else if (IsA(stmt, AlterTableStmt))
15647 {
15648 List *beforeStmts;
15649 List *afterStmts;
15650
15651 stmt = (Node *) transformAlterTableStmt(oldRelId,
15652 (AlterTableStmt *) stmt,
15653 cmd,
15654 &beforeStmts,
15655 &afterStmts);
15656 querytree_list = list_concat(querytree_list, beforeStmts);
15657 querytree_list = lappend(querytree_list, stmt);
15658 querytree_list = list_concat(querytree_list, afterStmts);
15659 }
15660 else if (IsA(stmt, CreateStatsStmt))
15661 querytree_list = lappend(querytree_list,
15662 transformStatsStmt(oldRelId,
15664 cmd));
15665 else
15666 querytree_list = lappend(querytree_list, stmt);
15667 }
15668
15669 /* Caller should already have acquired whatever lock we need. */
15670 rel = relation_open(oldRelId, NoLock);
15671
15672 /*
15673 * Attach each generated command to the proper place in the work queue.
15674 * Note this could result in creation of entirely new work-queue entries.
15675 *
15676 * Also note that we have to tweak the command subtypes, because it turns
15677 * out that re-creation of indexes and constraints has to act a bit
15678 * differently from initial creation.
15679 */
15680 foreach(list_item, querytree_list)
15681 {
15682 Node *stm = (Node *) lfirst(list_item);
15683 AlteredTableInfo *tab;
15684
15685 tab = ATGetQueueEntry(wqueue, rel);
15686
15687 if (IsA(stm, IndexStmt))
15688 {
15689 IndexStmt *stmt = (IndexStmt *) stm;
15690 AlterTableCmd *newcmd;
15691
15692 if (!rewrite)
15693 TryReuseIndex(oldId, stmt);
15694 stmt->reset_default_tblspc = true;
15695 /* keep the index's comment */
15696 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15697
15698 newcmd = makeNode(AlterTableCmd);
15699 newcmd->subtype = AT_ReAddIndex;
15700 newcmd->def = (Node *) stmt;
15702 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15703 }
15704 else if (IsA(stm, AlterTableStmt))
15705 {
15707 ListCell *lcmd;
15708
15709 foreach(lcmd, stmt->cmds)
15710 {
15712
15713 if (cmd->subtype == AT_AddIndex)
15714 {
15715 IndexStmt *indstmt;
15716 Oid indoid;
15717
15718 indstmt = castNode(IndexStmt, cmd->def);
15719 indoid = get_constraint_index(oldId);
15720
15721 if (!rewrite)
15722 TryReuseIndex(indoid, indstmt);
15723 /* keep any comment on the index */
15724 indstmt->idxcomment = GetComment(indoid,
15725 RelationRelationId, 0);
15726 indstmt->reset_default_tblspc = true;
15727
15728 cmd->subtype = AT_ReAddIndex;
15730 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15731
15732 /* recreate any comment on the constraint */
15735 oldId,
15736 rel,
15737 NIL,
15738 indstmt->idxname);
15739 }
15740 else if (cmd->subtype == AT_AddConstraint)
15741 {
15742 Constraint *con = castNode(Constraint, cmd->def);
15743
15744 con->old_pktable_oid = refRelId;
15745 /* rewriting neither side of a FK */
15746 if (con->contype == CONSTR_FOREIGN &&
15747 !rewrite && tab->rewrite == 0)
15748 TryReuseForeignKey(oldId, con);
15749 con->reset_default_tblspc = true;
15753
15754 /*
15755 * Recreate any comment on the constraint. If we have
15756 * recreated a primary key, then transformTableConstraint
15757 * has added an unnamed not-null constraint here; skip
15758 * this in that case.
15759 */
15760 if (con->conname)
15763 oldId,
15764 rel,
15765 NIL,
15766 con->conname);
15767 else
15768 Assert(con->contype == CONSTR_NOTNULL);
15769 }
15770 else
15771 elog(ERROR, "unexpected statement subtype: %d",
15772 (int) cmd->subtype);
15773 }
15774 }
15775 else if (IsA(stm, AlterDomainStmt))
15776 {
15778
15779 if (stmt->subtype == AD_AddConstraint)
15780 {
15781 Constraint *con = castNode(Constraint, stmt->def);
15783
15785 cmd->def = (Node *) stmt;
15788
15789 /* recreate any comment on the constraint */
15792 oldId,
15793 NULL,
15794 stmt->typeName,
15795 con->conname);
15796 }
15797 else
15798 elog(ERROR, "unexpected statement subtype: %d",
15799 (int) stmt->subtype);
15800 }
15801 else if (IsA(stm, CreateStatsStmt))
15802 {
15804 AlterTableCmd *newcmd;
15805
15806 /* keep the statistics object's comment */
15807 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15808
15809 newcmd = makeNode(AlterTableCmd);
15810 newcmd->subtype = AT_ReAddStatistics;
15811 newcmd->def = (Node *) stmt;
15812 tab->subcmds[AT_PASS_MISC] =
15813 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15814 }
15815 else
15816 elog(ERROR, "unexpected statement type: %d",
15817 (int) nodeTag(stm));
15818 }
15819
15820 relation_close(rel, NoLock);
15821}
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:1206
#define nodeTag(nodeptr)
Definition: nodes.h:139
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
CreateStatsStmt * transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
@ AD_AddConstraint
Definition: parsenodes.h:2547
@ RAW_PARSE_DEFAULT
Definition: parser.h:39
bool reset_default_tblspc
Definition: parsenodes.h:2858
bool reset_default_tblspc
Definition: parsenodes.h:3511
char * idxname
Definition: parsenodes.h:3485
char * idxcomment
Definition: parsenodes.h:3495
Node * stmt
Definition: parsenodes.h:2088
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:15878
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:15907
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:15834

References AD_AddConstraint, 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 7185 of file tablecmds.c.

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

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

17232{
17233 if (child_rel->rd_rel->reloftype)
17234 ereport(ERROR,
17235 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17236 errmsg("cannot change inheritance of typed table")));
17237
17238 if (child_rel->rd_rel->relispartition)
17239 ereport(ERROR,
17240 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17241 errmsg("cannot change inheritance of a partition")));
17242
17243 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17244 ereport(ERROR,
17245 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17246 errmsg("cannot change inheritance of partitioned table")));
17247}

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

9494{
9495 Constraint *pkconstr;
9496 List *children = NIL;
9497 bool got_children = false;
9498
9499 pkconstr = castNode(Constraint, cmd->def);
9500 if (pkconstr->contype != CONSTR_PRIMARY)
9501 return;
9502
9503 /* Verify that columns are not-null, or request that they be made so */
9504 foreach_node(String, column, pkconstr->keys)
9505 {
9506 AlterTableCmd *newcmd;
9507 Constraint *nnconstr;
9508 HeapTuple tuple;
9509
9510 /*
9511 * First check if a suitable constraint exists. If it does, we don't
9512 * need to request another one. We do need to bail out if it's not
9513 * valid, though.
9514 */
9515 tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9516 if (tuple != NULL)
9517 {
9518 verifyNotNullPKCompatible(tuple, strVal(column));
9519
9520 /* All good with this one; don't request another */
9521 heap_freetuple(tuple);
9522 continue;
9523 }
9524 else if (!recurse)
9525 {
9526 /*
9527 * No constraint on this column. Asked not to recurse, we won't
9528 * create one here, but verify that all children have one.
9529 */
9530 if (!got_children)
9531 {
9533 lockmode);
9534 /* only search for children on the first time through */
9535 got_children = true;
9536 }
9537
9538 foreach_oid(childrelid, children)
9539 {
9540 HeapTuple tup;
9541
9542 tup = findNotNullConstraint(childrelid, strVal(column));
9543 if (!tup)
9544 ereport(ERROR,
9545 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9546 strVal(column), get_rel_name(childrelid)));
9547 /* verify it's good enough */
9548 verifyNotNullPKCompatible(tup, strVal(column));
9549 }
9550 }
9551
9552 /* This column is not already not-null, so add it to the queue */
9553 nnconstr = makeNotNullConstraint(column);
9554
9555 newcmd = makeNode(AlterTableCmd);
9556 newcmd->subtype = AT_AddConstraint;
9557 /* note we force recurse=true here; see above */
9558 newcmd->recurse = true;
9559 newcmd->def = (Node *) nnconstr;
9560
9561 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9562 }
9563}
List * keys
Definition: parsenodes.h:2848
Definition: value.h:64
static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
Definition: tablecmds.c:9570

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

Referenced by ATPrepCmd().

◆ ATPrepAlterColumnType()

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

Definition at line 14365 of file tablecmds.c.

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

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(), CHKATYPE_IS_VIRTUAL, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, ColumnDef::cooked_default, copyObject, AlterTableCmd::def, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, expand_generated_columns_in_expr(), 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 18811 of file tablecmds.c.

18812{
18813 Relation pg_constraint;
18814 HeapTuple tuple;
18815 SysScanDesc scan;
18816 ScanKeyData skey[1];
18817
18818 /*
18819 * Disallow changing status for a temp table. Also verify whether we can
18820 * get away with doing nothing; in such cases we don't need to run the
18821 * checks below, either.
18822 */
18823 switch (rel->rd_rel->relpersistence)
18824 {
18825 case RELPERSISTENCE_TEMP:
18826 ereport(ERROR,
18827 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18828 errmsg("cannot change logged status of table \"%s\" because it is temporary",
18830 errtable(rel)));
18831 break;
18832 case RELPERSISTENCE_PERMANENT:
18833 if (toLogged)
18834 /* nothing to do */
18835 return;
18836 break;
18837 case RELPERSISTENCE_UNLOGGED:
18838 if (!toLogged)
18839 /* nothing to do */
18840 return;
18841 break;
18842 }
18843
18844 /*
18845 * Check that the table is not part of any publication when changing to
18846 * UNLOGGED, as UNLOGGED tables can't be published.
18847 */
18848 if (!toLogged &&
18850 ereport(ERROR,
18851 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18852 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18854 errdetail("Unlogged relations cannot be replicated.")));
18855
18856 /*
18857 * Check existing foreign key constraints to preserve the invariant that
18858 * permanent tables cannot reference unlogged ones. Self-referencing
18859 * foreign keys can safely be ignored.
18860 */
18861 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18862
18863 /*
18864 * Scan conrelid if changing to permanent, else confrelid. This also
18865 * determines whether a useful index exists.
18866 */
18867 ScanKeyInit(&skey[0],
18868 toLogged ? Anum_pg_constraint_conrelid :
18869 Anum_pg_constraint_confrelid,
18870 BTEqualStrategyNumber, F_OIDEQ,
18872 scan = systable_beginscan(pg_constraint,
18873 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18874 true, NULL, 1, skey);
18875
18876 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18877 {
18879
18880 if (con->contype == CONSTRAINT_FOREIGN)
18881 {
18882 Oid foreignrelid;
18883 Relation foreignrel;
18884
18885 /* the opposite end of what we used as scankey */
18886 foreignrelid = toLogged ? con->confrelid : con->conrelid;
18887
18888 /* ignore if self-referencing */
18889 if (RelationGetRelid(rel) == foreignrelid)
18890 continue;
18891
18892 foreignrel = relation_open(foreignrelid, AccessShareLock);
18893
18894 if (toLogged)
18895 {
18896 if (!RelationIsPermanent(foreignrel))
18897 ereport(ERROR,
18898 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18899 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18901 RelationGetRelationName(foreignrel)),
18902 errtableconstraint(rel, NameStr(con->conname))));
18903 }
18904 else
18905 {
18906 if (RelationIsPermanent(foreignrel))
18907 ereport(ERROR,
18908 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18909 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18911 RelationGetRelationName(foreignrel)),
18912 errtableconstraint(rel, NameStr(con->conname))));
18913 }
18914
18915 relation_close(foreignrel, AccessShareLock);
18916 }
18917 }
18918
18919 systable_endscan(scan);
18920
18921 table_close(pg_constraint, AccessShareLock);
18922
18923 /* force rewrite if necessary; see comment in ATRewriteTables */
18925 if (toLogged)
18926 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18927 else
18928 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18929 tab->chgPersistence = true;
18930}
#define AT_REWRITE_ALTER_PERSISTENCE
Definition: event_trigger.h:40
int errtableconstraint(Relation rel, const char *conname)
Definition: relcache.c:6103
int errtable(Relation rel)
Definition: relcache.c:6049

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

Referenced by ATPrepCmd().

◆ ATPrepCmd()

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

Definition at line 4897 of file tablecmds.c.

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

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

9251{
9252 if (rel->rd_rel->reloftype && !recursing)
9253 ereport(ERROR,
9254 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9255 errmsg("cannot drop column from typed table")));
9256
9257 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9258 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9259
9260 if (recurse)
9261 cmd->recurse = true;
9262}

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

8749{
8750 /*
8751 * Reject ONLY if there are child tables. We could implement this, but it
8752 * is a bit complicated. GENERATED clauses must be attached to the column
8753 * definition and cannot be added later like DEFAULT, so if a child table
8754 * has a generation expression that the parent does not have, the child
8755 * column will necessarily be an attislocal column. So to implement ONLY
8756 * here, we'd need extra code to update attislocal of the direct child
8757 * tables, somewhat similar to how DROP COLUMN does it, so that the
8758 * resulting state can be properly dumped and restored.
8759 */
8760 if (!recurse &&
8762 ereport(ERROR,
8763 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8764 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8765
8766 /*
8767 * Cannot drop generation expression from inherited columns.
8768 */
8769 if (!recursing)
8770 {
8771 HeapTuple tuple;
8772 Form_pg_attribute attTup;
8773
8775 if (!HeapTupleIsValid(tuple))
8776 ereport(ERROR,
8777 (errcode(ERRCODE_UNDEFINED_COLUMN),
8778 errmsg("column \"%s\" of relation \"%s\" does not exist",
8779 cmd->name, RelationGetRelationName(rel))));
8780
8781 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8782
8783 if (attTup->attinhcount > 0)
8784 ereport(ERROR,
8785 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8786 errmsg("cannot drop generation expression from inherited column")));
8787 }
8788}

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

16484{
16485 Oid amoid;
16486
16487 /*
16488 * Look up the access method name and check that it differs from the
16489 * table's current AM. If DEFAULT was specified for a partitioned table
16490 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16491 */
16492 if (amname != NULL)
16493 amoid = get_table_am_oid(amname, false);
16494 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16495 amoid = InvalidOid;
16496 else
16498
16499 /* if it's a match, phase 3 doesn't need to do anything */
16500 if (rel->rd_rel->relam == amoid)
16501 return;
16502
16503 /* Save info for Phase 3 to do the real work */
16505 tab->newAccessMethod = amoid;
16506 tab->chgAccessMethod = true;
16507}
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 16607 of file tablecmds.c.

16608{
16609 Oid tablespaceId;
16610
16611 /* Check that the tablespace exists */
16612 tablespaceId = get_tablespace_oid(tablespacename, false);
16613
16614 /* Check permissions except when moving to database's default */
16615 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16616 {
16617 AclResult aclresult;
16618
16619 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16620 if (aclresult != ACLCHECK_OK)
16621 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16622 }
16623
16624 /* Save info for Phase 3 to do the real work */
16625 if (OidIsValid(tab->newTableSpace))
16626 ereport(ERROR,
16627 (errcode(ERRCODE_SYNTAX_ERROR),
16628 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16629
16630 tab->newTableSpace = tablespaceId;
16631}

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

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

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

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

Referenced by ATRewriteTables().

◆ ATRewriteTables()

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

Definition at line 5830 of file tablecmds.c.

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

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

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

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

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

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

20562{
20563 List *idxes;
20564 List *attachRelIdxs;
20565 Relation *attachrelIdxRels;
20566 IndexInfo **attachInfos;
20567 ListCell *cell;
20568 MemoryContext cxt;
20569 MemoryContext oldcxt;
20570
20572 "AttachPartitionEnsureIndexes",
20574 oldcxt = MemoryContextSwitchTo(cxt);
20575
20576 idxes = RelationGetIndexList(rel);
20577 attachRelIdxs = RelationGetIndexList(attachrel);
20578 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
20579 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
20580
20581 /* Build arrays of all existing indexes and their IndexInfos */
20582 foreach_oid(cldIdxId, attachRelIdxs)
20583 {
20584 int i = foreach_current_index(cldIdxId);
20585
20586 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20587 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20588 }
20589
20590 /*
20591 * If we're attaching a foreign table, we must fail if any of the indexes
20592 * is a constraint index; otherwise, there's nothing to do here. Do this
20593 * before starting work, to avoid wasting the effort of building a few
20594 * non-unique indexes before coming across a unique one.
20595 */
20596 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20597 {
20598 foreach(cell, idxes)
20599 {
20600 Oid idx = lfirst_oid(cell);
20602
20603 if (idxRel->rd_index->indisunique ||
20604 idxRel->rd_index->indisprimary)
20605 ereport(ERROR,
20606 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20607 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20608 RelationGetRelationName(attachrel),
20610 errdetail("Partitioned table \"%s\" contains unique indexes.",
20613 }
20614
20615 goto out;
20616 }
20617
20618 /*
20619 * For each index on the partitioned table, find a matching one in the
20620 * partition-to-be; if one is not found, create one.
20621 */
20622 foreach(cell, idxes)
20623 {
20624 Oid idx = lfirst_oid(cell);
20626 IndexInfo *info;
20627 AttrMap *attmap;
20628 bool found = false;
20629 Oid constraintOid;
20630
20631 /*
20632 * Ignore indexes in the partitioned table other than partitioned
20633 * indexes.
20634 */
20635 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20636 {
20638 continue;
20639 }
20640
20641 /* construct an indexinfo to compare existing indexes against */
20642 info = BuildIndexInfo(idxRel);
20643 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20644 RelationGetDescr(rel),
20645 false);
20647
20648 /*
20649 * Scan the list of existing indexes in the partition-to-be, and mark
20650 * the first matching, valid, unattached one we find, if any, as
20651 * partition of the parent index. If we find one, we're done.
20652 */
20653 for (int i = 0; i < list_length(attachRelIdxs); i++)
20654 {
20655 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20656 Oid cldConstrOid = InvalidOid;
20657
20658 /* does this index have a parent? if so, can't use it */
20659 if (attachrelIdxRels[i]->rd_rel->relispartition)
20660 continue;
20661
20662 /* If this index is invalid, can't use it */
20663 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20664 continue;
20665
20666 if (CompareIndexInfo(attachInfos[i], info,
20667 attachrelIdxRels[i]->rd_indcollation,
20668 idxRel->rd_indcollation,
20669 attachrelIdxRels[i]->rd_opfamily,
20670 idxRel->rd_opfamily,
20671 attmap))
20672 {
20673 /*
20674 * If this index is being created in the parent because of a
20675 * constraint, then the child needs to have a constraint also,
20676 * so look for one. If there is no such constraint, this
20677 * index is no good, so keep looking.
20678 */
20679 if (OidIsValid(constraintOid))
20680 {
20681 cldConstrOid =
20683 cldIdxId);
20684 /* no dice */
20685 if (!OidIsValid(cldConstrOid))
20686 continue;
20687
20688 /* Ensure they're both the same type of constraint */
20689 if (get_constraint_type(constraintOid) !=
20690 get_constraint_type(cldConstrOid))
20691 continue;
20692 }
20693
20694 /* bingo. */
20695 IndexSetParentIndex(attachrelIdxRels[i], idx);
20696 if (OidIsValid(constraintOid))
20697 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20698 RelationGetRelid(attachrel));
20699 found = true;
20700
20702 break;
20703 }
20704 }
20705
20706 /*
20707 * If no suitable index was found in the partition-to-be, create one
20708 * now. Note that if this is a PK, not-null constraints must already
20709 * exist.
20710 */
20711 if (!found)
20712 {
20713 IndexStmt *stmt;
20714 Oid conOid;
20715
20717 idxRel, attmap,
20718 &conOid);
20720 RelationGetRelid(idxRel),
20721 conOid,
20722 -1,
20723 true, false, false, false, false);
20724 }
20725
20727 }
20728
20729out:
20730 /* Clean up. */
20731 for (int i = 0; i < list_length(attachRelIdxs); i++)
20732 index_close(attachrelIdxRels[i], AccessShareLock);
20733 MemoryContextSwitchTo(oldcxt);
20735}
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:262
char get_constraint_type(Oid conoid)
Definition: lsyscache.c:1236
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469
#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 11782 of file tablecmds.c.

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

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

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

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

◆ BuildDescForRelation()

TupleDesc BuildDescForRelation ( const List columns)

Definition at line 1370 of file tablecmds.c.

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

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

16306{
16307 Relation attRelation;
16308 SysScanDesc scan;
16309 ScanKeyData key[1];
16310 HeapTuple attributeTuple;
16311
16312 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16313 ScanKeyInit(&key[0],
16314 Anum_pg_attribute_attrelid,
16315 BTEqualStrategyNumber, F_OIDEQ,
16316 ObjectIdGetDatum(relationOid));
16317 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16318 true, NULL, 1, key);
16319 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16320 {
16321 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16322 Datum repl_val[Natts_pg_attribute];
16323 bool repl_null[Natts_pg_attribute];
16324 bool repl_repl[Natts_pg_attribute];
16325 Acl *newAcl;
16326 Datum aclDatum;
16327 bool isNull;
16328 HeapTuple newtuple;
16329
16330 /* Ignore dropped columns */
16331 if (att->attisdropped)
16332 continue;
16333
16334 aclDatum = heap_getattr(attributeTuple,
16335 Anum_pg_attribute_attacl,
16336 RelationGetDescr(attRelation),
16337 &isNull);
16338 /* Null ACLs do not require changes */
16339 if (isNull)
16340 continue;
16341
16342 memset(repl_null, false, sizeof(repl_null));
16343 memset(repl_repl, false, sizeof(repl_repl));
16344
16345 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16346 oldOwnerId, newOwnerId);
16347 repl_repl[Anum_pg_attribute_attacl - 1] = true;
16348 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16349
16350 newtuple = heap_modify_tuple(attributeTuple,
16351 RelationGetDescr(attRelation),
16352 repl_val, repl_null, repl_repl);
16353
16354 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16355
16356 heap_freetuple(newtuple);
16357 }
16358 systable_endscan(scan);
16359 table_close(attRelation, RowExclusiveLock);
16360}

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

16371{
16372 Relation depRel;
16373 SysScanDesc scan;
16374 ScanKeyData key[2];
16375 HeapTuple tup;
16376
16377 /*
16378 * SERIAL sequences are those having an auto dependency on one of the
16379 * table's columns (we don't care *which* column, exactly).
16380 */
16381 depRel = table_open(DependRelationId, AccessShareLock);
16382
16383 ScanKeyInit(&key[0],
16384 Anum_pg_depend_refclassid,
16385 BTEqualStrategyNumber, F_OIDEQ,
16386 ObjectIdGetDatum(RelationRelationId));
16387 ScanKeyInit(&key[1],
16388 Anum_pg_depend_refobjid,
16389 BTEqualStrategyNumber, F_OIDEQ,
16390 ObjectIdGetDatum(relationOid));
16391 /* we leave refobjsubid unspecified */
16392
16393 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16394 NULL, 2, key);
16395
16396 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16397 {
16398 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16399 Relation seqRel;
16400
16401 /* skip dependencies other than auto dependencies on columns */
16402 if (depForm->refobjsubid == 0 ||
16403 depForm->classid != RelationRelationId ||
16404 depForm->objsubid != 0 ||
16405 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16406 continue;
16407
16408 /* Use relation_open just in case it's an index */
16409 seqRel = relation_open(depForm->objid, lockmode);
16410
16411 /* skip non-sequence relations */
16412 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16413 {
16414 /* No need to keep the lock */
16415 relation_close(seqRel, lockmode);
16416 continue;
16417 }
16418
16419 /* We don't need to close the sequence while we alter it. */
16420 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16421
16422 /* Now we can close it. Keep the lock till end of transaction. */
16423 relation_close(seqRel, NoLock);
16424 }
16425
16426 systable_endscan(scan);
16427
16429}

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

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

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

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

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

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

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

13658{
13659 Oid roleid = GetUserId();
13660 AclResult aclresult;
13661 int i;
13662
13663 /* Okay if we have relation-level REFERENCES permission */
13664 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13666 if (aclresult == ACLCHECK_OK)
13667 return;
13668 /* Else we must have REFERENCES on each column */
13669 for (i = 0; i < natts; i++)
13670 {
13671 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13672 roleid, ACL_REFERENCES);
13673 if (aclresult != ACLCHECK_OK)
13674 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13676 }
13677}
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3866
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4037
#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 3685 of file tablecmds.c.

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

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

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

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

9863{
9864 char buf[NAMEDATALEN * 2];
9865 int buflen = 0;
9866 ListCell *lc;
9867
9868 buf[0] = '\0';
9869 foreach(lc, colnames)
9870 {
9871 const char *name = strVal(lfirst(lc));
9872
9873 if (buflen > 0)
9874 buf[buflen++] = '_'; /* insert _ between names */
9875
9876 /*
9877 * At this point we have buflen <= NAMEDATALEN. name should be less
9878 * than NAMEDATALEN already, but use strlcpy for paranoia.
9879 */
9880 strlcpy(buf + buflen, name, NAMEDATALEN);
9881 buflen += strlen(buf + buflen);
9882 if (buflen >= NAMEDATALEN)
9883 break;
9884 }
9885 return pstrdup(buf);
9886}
static char * buf
Definition: pg_test_fsync.c:72
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45

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

Referenced by ATExecAddConstraint().

◆ CloneFkReferenced()

static void CloneFkReferenced ( Relation  parentRel,
Relation  partitionRel 
)
static

Definition at line 11238 of file tablecmds.c.

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

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

Referenced by CloneForeignKeyConstraints().

◆ CloneFkReferencing()

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

Definition at line 11439 of file tablecmds.c.

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

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

Referenced by CloneForeignKeyConstraints().

◆ CloneForeignKeyConstraints()

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

Definition at line 11209 of file tablecmds.c.

11211{
11212 /* This only works for declarative partitioning */
11213 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11214
11215 /*
11216 * First, clone constraints where the parent is on the referencing side.
11217 */
11218 CloneFkReferencing(wqueue, parentRel, partitionRel);
11219
11220 /*
11221 * Clone constraints for which the parent is on the referenced side.
11222 */
11223 CloneFkReferenced(parentRel, partitionRel);
11224}
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11439
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11238

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

20744{
20745 Relation pg_trigger;
20747 SysScanDesc scan;
20748 HeapTuple tuple;
20749 MemoryContext perTupCxt;
20750
20751 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20752 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20753 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20754 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20755 true, NULL, 1, &key);
20756
20758 "clone trig", ALLOCSET_SMALL_SIZES);
20759
20760 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20761 {
20762 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20763 CreateTrigStmt *trigStmt;
20764 Node *qual = NULL;
20765 Datum value;
20766 bool isnull;
20767 List *cols = NIL;
20768 List *trigargs = NIL;
20769 MemoryContext oldcxt;
20770
20771 /*
20772 * Ignore statement-level triggers; those are not cloned.
20773 */
20774 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20775 continue;
20776
20777 /*
20778 * Don't clone internal triggers, because the constraint cloning code
20779 * will.
20780 */
20781 if (trigForm->tgisinternal)
20782 continue;
20783
20784 /*
20785 * Complain if we find an unexpected trigger type.
20786 */
20787 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20788 !TRIGGER_FOR_AFTER(trigForm->tgtype))
20789 elog(ERROR, "unexpected trigger \"%s\" found",
20790 NameStr(trigForm->tgname));
20791
20792 /* Use short-lived context for CREATE TRIGGER */
20793 oldcxt = MemoryContextSwitchTo(perTupCxt);
20794
20795 /*
20796 * If there is a WHEN clause, generate a 'cooked' version of it that's
20797 * appropriate for the partition.
20798 */
20799 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20800 RelationGetDescr(pg_trigger), &isnull);
20801 if (!isnull)
20802 {
20804 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20805 partition, parent);
20806 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20807 partition, parent);
20808 }
20809
20810 /*
20811 * If there is a column list, transform it to a list of column names.
20812 * Note we don't need to map this list in any way ...
20813 */
20814 if (trigForm->tgattr.dim1 > 0)
20815 {
20816 int i;
20817
20818 for (i = 0; i < trigForm->tgattr.dim1; i++)
20819 {
20821
20822 col = TupleDescAttr(parent->rd_att,
20823 trigForm->tgattr.values[i] - 1);
20824 cols = lappend(cols,
20825 makeString(pstrdup(NameStr(col->attname))));
20826 }
20827 }
20828
20829 /* Reconstruct trigger arguments list. */
20830 if (trigForm->tgnargs > 0)
20831 {
20832 char *p;
20833
20834 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20835 RelationGetDescr(pg_trigger), &isnull);
20836 if (isnull)
20837 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20838 NameStr(trigForm->tgname), RelationGetRelationName(partition));
20839
20840 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20841
20842 for (int i = 0; i < trigForm->tgnargs; i++)
20843 {
20844 trigargs = lappend(trigargs, makeString(pstrdup(p)));
20845 p += strlen(p) + 1;
20846 }
20847 }
20848
20849 trigStmt = makeNode(CreateTrigStmt);
20850 trigStmt->replace = false;
20851 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20852 trigStmt->trigname = NameStr(trigForm->tgname);
20853 trigStmt->relation = NULL;
20854 trigStmt->funcname = NULL; /* passed separately */
20855 trigStmt->args = trigargs;
20856 trigStmt->row = true;
20857 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20858 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20859 trigStmt->columns = cols;
20860 trigStmt->whenClause = NULL; /* passed separately */
20861 trigStmt->transitionRels = NIL; /* not supported at present */
20862 trigStmt->deferrable = trigForm->tgdeferrable;
20863 trigStmt->initdeferred = trigForm->tginitdeferred;
20864 trigStmt->constrrel = NULL; /* passed separately */
20865
20866 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20867 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20868 trigForm->tgfoid, trigForm->oid, qual,
20869 false, true, trigForm->tgenabled);
20870
20871 MemoryContextSwitchTo(oldcxt);
20872 MemoryContextReset(perTupCxt);
20873 }
20874
20875 MemoryContextDelete(perTupCxt);
20876
20877 systable_endscan(scan);
20878 table_close(pg_trigger, RowExclusiveLock);
20879}
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define DatumGetByteaPP(X)
Definition: fmgr.h:291
static struct @171 value
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:400
#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:3121
List * transitionRels
Definition: parsenodes.h:3123
RangeVar * constrrel
Definition: parsenodes.h:3127
RangeVar * relation
Definition: parsenodes.h:3112
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
static char * VARDATA_ANY(const void *PTR)
Definition: varatt.h:486

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

19778{
19779 int attn;
19780 ListCell *lc;
19781 Oid am_oid;
19782
19783 attn = 0;
19784 foreach(lc, partParams)
19785 {
19787 Oid atttype;
19788 Oid attcollation;
19789
19790 if (pelem->name != NULL)
19791 {
19792 /* Simple attribute reference */
19793 HeapTuple atttuple;
19794 Form_pg_attribute attform;
19795
19797 pelem->name);
19798 if (!HeapTupleIsValid(atttuple))
19799 ereport(ERROR,
19800 (errcode(ERRCODE_UNDEFINED_COLUMN),
19801 errmsg("column \"%s\" named in partition key does not exist",
19802 pelem->name),
19803 parser_errposition(pstate, pelem->location)));
19804 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19805
19806 if (attform->attnum <= 0)
19807 ereport(ERROR,
19808 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19809 errmsg("cannot use system column \"%s\" in partition key",
19810 pelem->name),
19811 parser_errposition(pstate, pelem->location)));
19812
19813 /*
19814 * Stored generated columns cannot work: They are computed after
19815 * BEFORE triggers, but partition routing is done before all
19816 * triggers. Maybe virtual generated columns could be made to
19817 * work, but then they would need to be handled as an expression
19818 * below.
19819 */
19820 if (attform->attgenerated)
19821 ereport(ERROR,
19822 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19823 errmsg("cannot use generated column in partition key"),
19824 errdetail("Column \"%s\" is a generated column.",
19825 pelem->name),
19826 parser_errposition(pstate, pelem->location)));
19827
19828 partattrs[attn] = attform->attnum;
19829 atttype = attform->atttypid;
19830 attcollation = attform->attcollation;
19831 ReleaseSysCache(atttuple);
19832 }
19833 else
19834 {
19835 /* Expression */
19836 Node *expr = pelem->expr;
19837 char partattname[16];
19838 Bitmapset *expr_attrs = NULL;
19839 int i;
19840
19841 Assert(expr != NULL);
19842 atttype = exprType(expr);
19843 attcollation = exprCollation(expr);
19844
19845 /*
19846 * The expression must be of a storable type (e.g., not RECORD).
19847 * The test is the same as for whether a table column is of a safe
19848 * type (which is why we needn't check for the non-expression
19849 * case).
19850 */
19851 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19852 CheckAttributeType(partattname,
19853 atttype, attcollation,
19855
19856 /*
19857 * Strip any top-level COLLATE clause. This ensures that we treat
19858 * "x COLLATE y" and "(x COLLATE y)" alike.
19859 */
19860 while (IsA(expr, CollateExpr))
19861 expr = (Node *) ((CollateExpr *) expr)->arg;
19862
19863 /*
19864 * Examine all the columns in the partition key expression. When
19865 * the whole-row reference is present, examine all the columns of
19866 * the partitioned table.
19867 */
19868 pull_varattnos(expr, 1, &expr_attrs);
19870 {
19871 expr_attrs = bms_add_range(expr_attrs,
19874 expr_attrs = bms_del_member(expr_attrs, 0 - FirstLowInvalidHeapAttributeNumber);
19875 }
19876
19877 i = -1;
19878 while ((i = bms_next_member(expr_attrs, i)) >= 0)
19879 {
19881
19882 Assert(attno != 0);
19883
19884 /*
19885 * Cannot allow system column references, since that would
19886 * make partition routing impossible: their values won't be
19887 * known yet when we need to do that.
19888 */
19889 if (attno < 0)
19890 ereport(ERROR,
19891 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19892 errmsg("partition key expressions cannot contain system column references")));
19893
19894 /*
19895 * Stored generated columns cannot work: They are computed
19896 * after BEFORE triggers, but partition routing is done before
19897 * all triggers. Virtual generated columns could probably
19898 * work, but it would require more work elsewhere (for example
19899 * SET EXPRESSION would need to check whether the column is
19900 * used in partition keys). Seems safer to prohibit for now.
19901 */
19902 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19903 ereport(ERROR,
19904 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19905 errmsg("cannot use generated column in partition key"),
19906 errdetail("Column \"%s\" is a generated column.",
19907 get_attname(RelationGetRelid(rel), attno, false)),
19908 parser_errposition(pstate, pelem->location)));
19909 }
19910
19911 if (IsA(expr, Var) &&
19912 ((Var *) expr)->varattno > 0)
19913 {
19914
19915 /*
19916 * User wrote "(column)" or "(column COLLATE something)".
19917 * Treat it like simple attribute anyway.
19918 */
19919 partattrs[attn] = ((Var *) expr)->varattno;
19920 }
19921 else
19922 {
19923 partattrs[attn] = 0; /* marks the column as expression */
19924 *partexprs = lappend(*partexprs, expr);
19925
19926 /*
19927 * transformPartitionSpec() should have already rejected
19928 * subqueries, aggregates, window functions, and SRFs, based
19929 * on the EXPR_KIND_ for partition expressions.
19930 */
19931
19932 /*
19933 * Preprocess the expression before checking for mutability.
19934 * This is essential for the reasons described in
19935 * contain_mutable_functions_after_planning. However, we call
19936 * expression_planner for ourselves rather than using that
19937 * function, because if constant-folding reduces the
19938 * expression to a constant, we'd like to know that so we can
19939 * complain below.
19940 *
19941 * Like contain_mutable_functions_after_planning, assume that
19942 * expression_planner won't scribble on its input, so this
19943 * won't affect the partexprs entry we saved above.
19944 */
19945 expr = (Node *) expression_planner((Expr *) expr);
19946
19947 /*
19948 * Partition expressions cannot contain mutable functions,
19949 * because a given row must always map to the same partition
19950 * as long as there is no change in the partition boundary
19951 * structure.
19952 */
19953 if (contain_mutable_functions(expr))
19954 ereport(ERROR,
19955 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19956 errmsg("functions in partition key expression must be marked IMMUTABLE")));
19957
19958 /*
19959 * While it is not exactly *wrong* for a partition expression
19960 * to be a constant, it seems better to reject such keys.
19961 */
19962 if (IsA(expr, Const))
19963 ereport(ERROR,
19964 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19965 errmsg("cannot use constant expression as partition key")));
19966 }
19967 }
19968
19969 /*
19970 * Apply collation override if any
19971 */
19972 if (pelem->collation)
19973 attcollation = get_collation_oid(pelem->collation, false);
19974
19975 /*
19976 * Check we have a collation iff it's a collatable type. The only
19977 * expected failures here are (1) COLLATE applied to a noncollatable
19978 * type, or (2) partition expression had an unresolved collation. But
19979 * we might as well code this to be a complete consistency check.
19980 */
19981 if (type_is_collatable(atttype))
19982 {
19983 if (!OidIsValid(attcollation))
19984 ereport(ERROR,
19985 (errcode(ERRCODE_INDETERMINATE_COLLATION),
19986 errmsg("could not determine which collation to use for partition expression"),
19987 errhint("Use the COLLATE clause to set the collation explicitly.")));
19988 }
19989 else
19990 {
19991 if (OidIsValid(attcollation))
19992 ereport(ERROR,
19993 (errcode(ERRCODE_DATATYPE_MISMATCH),
19994 errmsg("collations are not supported by type %s",
19995 format_type_be(atttype))));
19996 }
19997
19998 partcollation[attn] = attcollation;
19999
20000 /*
20001 * Identify the appropriate operator class. For list and range
20002 * partitioning, we use a btree operator class; hash partitioning uses
20003 * a hash operator class.
20004 */
20005 if (strategy == PARTITION_STRATEGY_HASH)
20006 am_oid = HASH_AM_OID;
20007 else
20008 am_oid = BTREE_AM_OID;
20009
20010 if (!pelem->opclass)
20011 {
20012 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
20013
20014 if (!OidIsValid(partopclass[attn]))
20015 {
20016 if (strategy == PARTITION_STRATEGY_HASH)
20017 ereport(ERROR,
20018 (errcode(ERRCODE_UNDEFINED_OBJECT),
20019 errmsg("data type %s has no default operator class for access method \"%s\"",
20020 format_type_be(atttype), "hash"),
20021 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
20022 else
20023 ereport(ERROR,
20024 (errcode(ERRCODE_UNDEFINED_OBJECT),
20025 errmsg("data type %s has no default operator class for access method \"%s\"",
20026 format_type_be(atttype), "btree"),
20027 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
20028 }
20029 }
20030 else
20031 partopclass[attn] = ResolveOpClass(pelem->opclass,
20032 atttype,
20033 am_oid == HASH_AM_OID ? "hash" : "btree",
20034 am_oid);
20035
20036 attn++;
20037 }
20038}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1305
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition: bitmapset.c:1018
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:867
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
bool contain_mutable_functions(Node *clause)
Definition: clauses.c:382
#define CHKATYPE_IS_PARTKEY
Definition: heap.h:25
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2345
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition: indexcmds.c:2260
bool type_is_collatable(Oid typid)
Definition: lsyscache.c:3248
Oid get_collation_oid(List *collname, bool missing_ok)
Definition: namespace.c:4041
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:821
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:902
#define snprintf
Definition: port.h:260
#define RelationGetNumberOfAttributes(relation)
Definition: rel.h:521
List * collation
Definition: parsenodes.h:893
ParseLoc location
Definition: parsenodes.h:895
List * opclass
Definition: parsenodes.h:894
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:296

References arg, Assert(), bms_add_range(), bms_del_member(), 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, RelationGetNumberOfAttributes, 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 20104 of file tablecmds.c.

20105{
20106 List *existConstraint = list_copy(provenConstraint);
20107 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20108 int num_check,
20109 i;
20110
20111 num_check = (constr != NULL) ? constr->num_check : 0;
20112 for (i = 0; i < num_check; i++)
20113 {
20114 Node *cexpr;
20115
20116 /*
20117 * If this constraint hasn't been fully validated yet, we must ignore
20118 * it here.
20119 */
20120 if (!constr->check[i].ccvalid)
20121 continue;
20122
20123 /*
20124 * NOT ENFORCED constraints are always marked as invalid, which should
20125 * have been ignored.
20126 */
20127 Assert(constr->check[i].ccenforced);
20128
20129 cexpr = stringToNode(constr->check[i].ccbin);
20130
20131 /*
20132 * Run each expression through const-simplification and
20133 * canonicalization. It is necessary, because we will be comparing it
20134 * to similarly-processed partition constraint expressions, and may
20135 * fail to detect valid matches without this.
20136 */
20137 cexpr = eval_const_expressions(NULL, cexpr);
20138 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20139
20140 existConstraint = list_concat(existConstraint,
20141 make_ands_implicit((Expr *) cexpr));
20142 }
20143
20144 /*
20145 * Try to make the proof. Since we are comparing CHECK constraints, we
20146 * need to use weak implication, i.e., we assume existConstraint is
20147 * not-false and try to prove the same for testConstraint.
20148 *
20149 * Note that predicate_implied_by assumes its first argument is known
20150 * immutable. That should always be true for both NOT NULL and partition
20151 * constraints, so we don't test it here.
20152 */
20153 return predicate_implied_by(testConstraint, existConstraint, true);
20154}
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 17461 of file tablecmds.c.

17462{
17465
17466 if (acon->condeferrable != bcon->condeferrable ||
17467 acon->condeferred != bcon->condeferred ||
17468 strcmp(decompile_conbin(a, tupleDesc),
17469 decompile_conbin(b, tupleDesc)) != 0)
17470 return false;
17471 else
17472 return true;
17473}
int b
Definition: isn.c:74
int a
Definition: isn.c:73
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:17433

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

13789{
13790 ObjectAddress trigAddress;
13791 CreateTrigStmt *fk_trigger;
13792
13793 /*
13794 * Note: for a self-referential FK (referencing and referenced tables are
13795 * the same), it is important that the ON UPDATE action fires before the
13796 * CHECK action, since both triggers will fire on the same row during an
13797 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13798 * state of the row. Triggers fire in name order, so we ensure this by
13799 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13800 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13801 */
13802 fk_trigger = makeNode(CreateTrigStmt);
13803 fk_trigger->replace = false;
13804 fk_trigger->isconstraint = true;
13805 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13806 fk_trigger->relation = NULL;
13807
13808 /* Either ON INSERT or ON UPDATE */
13809 if (on_insert)
13810 {
13811 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13812 fk_trigger->events = TRIGGER_TYPE_INSERT;
13813 }
13814 else
13815 {
13816 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13817 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13818 }
13819
13820 fk_trigger->args = NIL;
13821 fk_trigger->row = true;
13822 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13823 fk_trigger->columns = NIL;
13824 fk_trigger->whenClause = NULL;
13825 fk_trigger->transitionRels = NIL;
13826 fk_trigger->deferrable = fkconstraint->deferrable;
13827 fk_trigger->initdeferred = fkconstraint->initdeferred;
13828 fk_trigger->constrrel = NULL;
13829
13830 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13831 constraintOid, indexOid, InvalidOid,
13832 parentTrigOid, NULL, true, false);
13833
13834 /* Make changes-so-far visible */
13836
13837 return trigAddress.objectId;
13838}
List * SystemFuncName(char *name)
ObjectAddress CreateTrigger(CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition)
Definition: trigger.c:160

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

Referenced by createForeignKeyCheckTriggers().

◆ createForeignKeyActionTriggers()

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

Definition at line 13849 of file tablecmds.c.

13853{
13854 CreateTrigStmt *fk_trigger;
13855 ObjectAddress trigAddress;
13856
13857 /*
13858 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13859 * DELETE action on the referenced table.
13860 */
13861 fk_trigger = makeNode(CreateTrigStmt);
13862 fk_trigger->replace = false;
13863 fk_trigger->isconstraint = true;
13864 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13865 fk_trigger->relation = NULL;
13866 fk_trigger->args = NIL;
13867 fk_trigger->row = true;
13868 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13869 fk_trigger->events = TRIGGER_TYPE_DELETE;
13870 fk_trigger->columns = NIL;
13871 fk_trigger->whenClause = NULL;
13872 fk_trigger->transitionRels = NIL;
13873 fk_trigger->constrrel = NULL;
13874
13875 switch (fkconstraint->fk_del_action)
13876 {
13878 fk_trigger->deferrable = fkconstraint->deferrable;
13879 fk_trigger->initdeferred = fkconstraint->initdeferred;
13880 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13881 break;
13883 fk_trigger->deferrable = false;
13884 fk_trigger->initdeferred = false;
13885 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13886 break;
13888 fk_trigger->deferrable = false;
13889 fk_trigger->initdeferred = false;
13890 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13891 break;
13893 fk_trigger->deferrable = false;
13894 fk_trigger->initdeferred = false;
13895 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13896 break;
13898 fk_trigger->deferrable = false;
13899 fk_trigger->initdeferred = false;
13900 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13901 break;
13902 default:
13903 elog(ERROR, "unrecognized FK action type: %d",
13904 (int) fkconstraint->fk_del_action);
13905 break;
13906 }
13907
13908 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13909 constraintOid, indexOid, InvalidOid,
13910 parentDelTrigger, NULL, true, false);
13911 if (deleteTrigOid)
13912 *deleteTrigOid = trigAddress.objectId;
13913
13914 /* Make changes-so-far visible */
13916
13917 /*
13918 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13919 * UPDATE action on the referenced table.
13920 */
13921 fk_trigger = makeNode(CreateTrigStmt);
13922 fk_trigger->replace = false;
13923 fk_trigger->isconstraint = true;
13924 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13925 fk_trigger->relation = NULL;
13926 fk_trigger->args = NIL;
13927 fk_trigger->row = true;
13928 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13929 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13930 fk_trigger->columns = NIL;
13931 fk_trigger->whenClause = NULL;
13932 fk_trigger->transitionRels = NIL;
13933 fk_trigger->constrrel = NULL;
13934
13935 switch (fkconstraint->fk_upd_action)
13936 {
13938 fk_trigger->deferrable = fkconstraint->deferrable;
13939 fk_trigger->initdeferred = fkconstraint->initdeferred;
13940 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13941 break;
13943 fk_trigger->deferrable = false;
13944 fk_trigger->initdeferred = false;
13945 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13946 break;
13948 fk_trigger->deferrable = false;
13949 fk_trigger->initdeferred = false;
13950 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13951 break;
13953 fk_trigger->deferrable = false;
13954 fk_trigger->initdeferred = false;
13955 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13956 break;
13958 fk_trigger->deferrable = false;
13959 fk_trigger->initdeferred = false;
13960 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13961 break;
13962 default:
13963 elog(ERROR, "unrecognized FK action type: %d",
13964 (int) fkconstraint->fk_upd_action);
13965 break;
13966 }
13967
13968 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13969 constraintOid, indexOid, InvalidOid,
13970 parentUpdTrigger, NULL, true, false);
13971 if (updateTrigOid)
13972 *updateTrigOid = trigAddress.objectId;
13973}
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2819

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

Referenced by addFkRecurseReferenced(), and ATExecAlterConstrEnforceability().

◆ createForeignKeyCheckTriggers()

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

Definition at line 13984 of file tablecmds.c.

13989{
13990 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13991 constraintOid, indexOid,
13992 parentInsTrigger, true);
13993 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13994 constraintOid, indexOid,
13995 parentUpdTrigger, false);
13996}
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:13786

References CreateFKCheckTrigger().

Referenced by addFkRecurseReferencing(), and ATExecAlterConstrEnforceability().

◆ CreateInheritance()

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

Definition at line 17364 of file tablecmds.c.

17365{
17366 Relation catalogRelation;
17367 SysScanDesc scan;
17369 HeapTuple inheritsTuple;
17370 int32 inhseqno;
17371
17372 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17373 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17374
17375 /*
17376 * Check for duplicates in the list of parents, and determine the highest
17377 * inhseqno already present; we'll use the next one for the new parent.
17378 * Also, if proposed child is a partition, it cannot already be
17379 * inheriting.
17380 *
17381 * Note: we do not reject the case where the child already inherits from
17382 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17383 */
17385 Anum_pg_inherits_inhrelid,
17386 BTEqualStrategyNumber, F_OIDEQ,
17388 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17389 true, NULL, 1, &key);
17390
17391 /* inhseqno sequences start at 1 */
17392 inhseqno = 0;
17393 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17394 {
17395 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17396
17397 if (inh->inhparent == RelationGetRelid(parent_rel))
17398 ereport(ERROR,
17399 (errcode(ERRCODE_DUPLICATE_TABLE),
17400 errmsg("relation \"%s\" would be inherited from more than once",
17401 RelationGetRelationName(parent_rel))));
17402
17403 if (inh->inhseqno > inhseqno)
17404 inhseqno = inh->inhseqno;
17405 }
17406 systable_endscan(scan);
17407
17408 /* Match up the columns and bump attinhcount as needed */
17409 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17410
17411 /* Match up the constraints and bump coninhcount as needed */
17412 MergeConstraintsIntoExisting(child_rel, parent_rel);
17413
17414 /*
17415 * OK, it looks valid. Make the catalog entries that show inheritance.
17416 */
17418 RelationGetRelid(parent_rel),
17419 inhseqno + 1,
17420 catalogRelation,
17421 parent_rel->rd_rel->relkind ==
17422 RELKIND_PARTITIONED_TABLE);
17423
17424 /* Now we're done with pg_inherits */
17425 table_close(catalogRelation, RowExclusiveLock);
17426}
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:17490
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3557
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17628

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

17434{
17436 bool isnull;
17437 Datum attr;
17438 Datum expr;
17439
17440 con = (Form_pg_constraint) GETSTRUCT(contup);
17441 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17442 if (isnull)
17443 elog(ERROR, "null conbin for constraint %u", con->oid);
17444
17445 expr = DirectFunctionCall2(pg_get_expr, attr,
17446 ObjectIdGetDatum(con->conrelid));
17447 return TextDatumGetCString(expr);
17448}
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:684
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition: ruleutils.c:2674

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

Referenced by constraints_equivalent().

◆ DefineRelation()

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

Definition at line 764 of file tablecmds.c.

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

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

◆ DetachPartitionFinalize()

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

Definition at line 21073 of file tablecmds.c.

21075{
21076 Relation classRel;
21077 List *fks;
21078 ListCell *cell;
21079 List *indexes;
21080 Datum new_val[Natts_pg_class];
21081 bool new_null[Natts_pg_class],
21082 new_repl[Natts_pg_class];
21083 HeapTuple tuple,
21084 newtuple;
21085 Relation trigrel = NULL;
21086 List *fkoids = NIL;
21087
21088 if (concurrent)
21089 {
21090 /*
21091 * We can remove the pg_inherits row now. (In the non-concurrent case,
21092 * this was already done).
21093 */
21094 RemoveInheritance(partRel, rel, true);
21095 }
21096
21097 /* Drop any triggers that were cloned on creation/attach. */
21099
21100 /*
21101 * Detach any foreign keys that are inherited. This includes creating
21102 * additional action triggers.
21103 */
21104 fks = copyObject(RelationGetFKeyList(partRel));
21105 if (fks != NIL)
21106 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21107
21108 /*
21109 * It's possible that the partition being detached has a foreign key that
21110 * references a partitioned table. When that happens, there are multiple
21111 * pg_constraint rows for the partition: one points to the partitioned
21112 * table itself, while the others point to each of its partitions. Only
21113 * the topmost one is to be considered here; the child constraints must be
21114 * left alone, because conceptually those aren't coming from our parent
21115 * partitioned table, but from this partition itself.
21116 *
21117 * We implement this by collecting all the constraint OIDs in a first scan
21118 * of the FK array, and skipping in the loop below those constraints whose
21119 * parents are listed here.
21120 */
21122 fkoids = lappend_oid(fkoids, fk->conoid);
21123
21124 foreach(cell, fks)
21125 {
21126 ForeignKeyCacheInfo *fk = lfirst(cell);
21127 HeapTuple contup;
21128 Form_pg_constraint conform;
21129
21130 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21131 if (!HeapTupleIsValid(contup))
21132 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21133 conform = (Form_pg_constraint) GETSTRUCT(contup);
21134
21135 /*
21136 * Consider only inherited foreign keys, and only if their parents
21137 * aren't in the list.
21138 */
21139 if (conform->contype != CONSTRAINT_FOREIGN ||
21140 !OidIsValid(conform->conparentid) ||
21141 list_member_oid(fkoids, conform->conparentid))
21142 {
21143 ReleaseSysCache(contup);
21144 continue;
21145 }
21146
21147 /*
21148 * The constraint on this table must be marked no longer a child of
21149 * the parent's constraint, as do its check triggers.
21150 */
21152
21153 /*
21154 * Also, look up the partition's "check" triggers corresponding to the
21155 * ENFORCED constraint being detached and detach them from the parent
21156 * triggers. NOT ENFORCED constraints do not have these triggers;
21157 * therefore, this step is not needed.
21158 */
21159 if (fk->conenforced)
21160 {
21161 Oid insertTriggerOid,
21162 updateTriggerOid;
21163
21165 fk->conoid, fk->confrelid, fk->conrelid,
21166 &insertTriggerOid, &updateTriggerOid);
21167 Assert(OidIsValid(insertTriggerOid));
21168 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21169 RelationGetRelid(partRel));
21170 Assert(OidIsValid(updateTriggerOid));
21171 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21172 RelationGetRelid(partRel));
21173 }
21174
21175 /*
21176 * Lastly, create the action triggers on the referenced table, using
21177 * addFkRecurseReferenced, which requires some elaborate setup (so put
21178 * it in a separate block). While at it, if the table is partitioned,
21179 * that function will recurse to create the pg_constraint rows and
21180 * action triggers for each partition.
21181 *
21182 * Note there's no need to do addFkConstraint() here, because the
21183 * pg_constraint row already exists.
21184 */
21185 {
21186 Constraint *fkconstraint;
21187 int numfks;
21188 AttrNumber conkey[INDEX_MAX_KEYS];
21189 AttrNumber confkey[INDEX_MAX_KEYS];
21190 Oid conpfeqop[INDEX_MAX_KEYS];
21191 Oid conppeqop[INDEX_MAX_KEYS];
21192 Oid conffeqop[INDEX_MAX_KEYS];
21193 int numfkdelsetcols;
21194 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21195 Relation refdRel;
21196
21198 &numfks,
21199 conkey,
21200 confkey,
21201 conpfeqop,
21202 conppeqop,
21203 conffeqop,
21204 &numfkdelsetcols,
21205 confdelsetcols);
21206
21207 /* Create a synthetic node we'll use throughout */
21208 fkconstraint = makeNode(Constraint);
21209 fkconstraint->contype = CONSTRAINT_FOREIGN;
21210 fkconstraint->conname = pstrdup(NameStr(conform->conname));
21211 fkconstraint->deferrable = conform->condeferrable;
21212 fkconstraint->initdeferred = conform->condeferred;
21213 fkconstraint->is_enforced = conform->conenforced;
21214 fkconstraint->skip_validation = true;
21215 fkconstraint->initially_valid = conform->convalidated;
21216 /* a few irrelevant fields omitted here */
21217 fkconstraint->pktable = NULL;
21218 fkconstraint->fk_attrs = NIL;
21219 fkconstraint->pk_attrs = NIL;
21220 fkconstraint->fk_matchtype = conform->confmatchtype;
21221 fkconstraint->fk_upd_action = conform->confupdtype;
21222 fkconstraint->fk_del_action = conform->confdeltype;
21223 fkconstraint->fk_del_set_cols = NIL;
21224 fkconstraint->old_conpfeqop = NIL;
21225 fkconstraint->old_pktable_oid = InvalidOid;
21226 fkconstraint->location = -1;
21227
21228 /* set up colnames, used to generate the constraint name */
21229 for (int i = 0; i < numfks; i++)
21230 {
21232
21233 att = TupleDescAttr(RelationGetDescr(partRel),
21234 conkey[i] - 1);
21235
21236 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21237 makeString(NameStr(att->attname)));
21238 }
21239
21241
21242 addFkRecurseReferenced(fkconstraint, partRel,
21243 refdRel,
21244 conform->conindid,
21245 fk->conoid,
21246 numfks,
21247 confkey,
21248 conkey,
21249 conpfeqop,
21250 conppeqop,
21251 conffeqop,
21252 numfkdelsetcols,
21253 confdelsetcols,
21254 true,
21256 conform->conperiod);
21257 table_close(refdRel, NoLock); /* keep lock till end of xact */
21258 }
21259
21260 ReleaseSysCache(contup);
21261 }
21262 list_free_deep(fks);
21263 if (trigrel)
21264 table_close(trigrel, RowExclusiveLock);
21265
21266 /*
21267 * Any sub-constraints that are in the referenced-side of a larger
21268 * constraint have to be removed. This partition is no longer part of the
21269 * key space of the constraint.
21270 */
21271 foreach(cell, GetParentedForeignKeyRefs(partRel))
21272 {
21273 Oid constrOid = lfirst_oid(cell);
21274 ObjectAddress constraint;
21275
21277 deleteDependencyRecordsForClass(ConstraintRelationId,
21278 constrOid,
21279 ConstraintRelationId,
21282
21283 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21284 performDeletion(&constraint, DROP_RESTRICT, 0);
21285 }
21286
21287 /* Now we can detach indexes */
21288 indexes = RelationGetIndexList(partRel);
21289 foreach(cell, indexes)
21290 {
21291 Oid idxid = lfirst_oid(cell);
21292 Oid parentidx;
21293 Relation idx;
21294 Oid constrOid;
21295 Oid parentConstrOid;
21296
21297 if (!has_superclass(idxid))
21298 continue;
21299
21300 parentidx = get_partition_parent(idxid, false);
21301 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21302
21305
21306 /*
21307 * If there's a constraint associated with the index, detach it too.
21308 * Careful: it is possible for a constraint index in a partition to be
21309 * the child of a non-constraint index, so verify whether the parent
21310 * index does actually have a constraint.
21311 */
21313 idxid);
21315 parentidx);
21316 if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21318
21320 }
21321
21322 /* Update pg_class tuple */
21323 classRel = table_open(RelationRelationId, RowExclusiveLock);
21324 tuple = SearchSysCacheCopy1(RELOID,
21326 if (!HeapTupleIsValid(tuple))
21327 elog(ERROR, "cache lookup failed for relation %u",
21328 RelationGetRelid(partRel));
21329 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21330
21331 /* Clear relpartbound and reset relispartition */
21332 memset(new_val, 0, sizeof(new_val));
21333 memset(new_null, false, sizeof(new_null));
21334 memset(new_repl, false, sizeof(new_repl));
21335 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21336 new_null[Anum_pg_class_relpartbound - 1] = true;
21337 new_repl[Anum_pg_class_relpartbound - 1] = true;
21338 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21339 new_val, new_null, new_repl);
21340
21341 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21342 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21343 heap_freetuple(newtuple);
21344 table_close(classRel, RowExclusiveLock);
21345
21346 /*
21347 * Drop identity property from all identity columns of partition.
21348 */
21349 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21350 {
21351 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21352
21353 if (!attr->attisdropped && attr->attidentity)
21354 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21355 AccessExclusiveLock, true, true);
21356 }
21357
21358 if (OidIsValid(defaultPartOid))
21359 {
21360 /*
21361 * If the relation being detached is the default partition itself,
21362 * remove it from the parent's pg_partitioned_table entry.
21363 *
21364 * If not, we must invalidate default partition's relcache entry, as
21365 * in StorePartitionBound: its partition constraint depends on every
21366 * other partition's partition constraint.
21367 */
21368 if (RelationGetRelid(partRel) == defaultPartOid)
21370 else
21371 CacheInvalidateRelcacheByRelid(defaultPartOid);
21372 }
21373
21374 /*
21375 * Invalidate the parent's relcache so that the partition is no longer
21376 * included in its partition descriptor.
21377 */
21379
21380 /*
21381 * If the partition we just detached is partitioned itself, invalidate
21382 * relcache for all descendent partitions too to ensure that their
21383 * rd_partcheck expression trees are rebuilt; must lock partitions before
21384 * doing so, using the same lockmode as what partRel has been locked with
21385 * by the caller.
21386 */
21387 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21388 {
21389 List *children;
21390
21391 children = find_all_inheritors(RelationGetRelid(partRel),
21392 AccessExclusiveLock, NULL);
21393 foreach(cell, children)
21394 {
21396 }
21397 }
21398}
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
bool conenforced
Definition: rel.h:288
static void DropClonedTriggersFromPartition(Oid partitionId)
Definition: tablecmds.c:21441

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

Referenced by ATExecDetachPartition(), and ATExecDetachPartitionFinalize().

◆ drop_parent_dependency()

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

Definition at line 18154 of file tablecmds.c.

18156{
18157 Relation catalogRelation;
18158 SysScanDesc scan;
18159 ScanKeyData key[3];
18160 HeapTuple depTuple;
18161
18162 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18163
18164 ScanKeyInit(&key[0],
18165 Anum_pg_depend_classid,
18166 BTEqualStrategyNumber, F_OIDEQ,
18167 ObjectIdGetDatum(RelationRelationId));
18168 ScanKeyInit(&key[1],
18169 Anum_pg_depend_objid,
18170 BTEqualStrategyNumber, F_OIDEQ,
18171 ObjectIdGetDatum(relid));
18172 ScanKeyInit(&key[2],
18173 Anum_pg_depend_objsubid,
18174 BTEqualStrategyNumber, F_INT4EQ,
18175 Int32GetDatum(0));
18176
18177 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18178 NULL, 3, key);
18179
18180 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18181 {
18182 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18183
18184 if (dep->refclassid == refclassid &&
18185 dep->refobjid == refobjid &&
18186 dep->refobjsubid == 0 &&
18187 dep->deptype == deptype)
18188 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18189 }
18190
18191 systable_endscan(scan);
18192 table_close(catalogRelation, RowExclusiveLock);
18193}

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

21442{
21443 ScanKeyData skey;
21444 SysScanDesc scan;
21445 HeapTuple trigtup;
21446 Relation tgrel;
21447 ObjectAddresses *objects;
21448
21449 objects = new_object_addresses();
21450
21451 /*
21452 * Scan pg_trigger to search for all triggers on this rel.
21453 */
21454 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21455 F_OIDEQ, ObjectIdGetDatum(partitionId));
21456 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21457 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21458 true, NULL, 1, &skey);
21459 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21460 {
21461 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21462 ObjectAddress trig;
21463
21464 /* Ignore triggers that weren't cloned */
21465 if (!OidIsValid(pg_trigger->tgparentid))
21466 continue;
21467
21468 /*
21469 * Ignore internal triggers that are implementation objects of foreign
21470 * keys, because these will be detached when the foreign keys
21471 * themselves are.
21472 */
21473 if (OidIsValid(pg_trigger->tgconstrrelid))
21474 continue;
21475
21476 /*
21477 * This is ugly, but necessary: remove the dependency markings on the
21478 * trigger so that it can be removed.
21479 */
21480 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21481 TriggerRelationId,
21483 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21484 RelationRelationId,
21486
21487 /* remember this trigger to remove it below */
21488 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21489 add_exact_object_address(&trig, objects);
21490 }
21491
21492 /* make the dependency removal visible to the deletion below */
21495
21496 /* done */
21497 free_object_addresses(objects);
21498 systable_endscan(scan);
21500}

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

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

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

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

◆ DropErrorMsgNonExistent()

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

Definition at line 1453 of file tablecmds.c.

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

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

Referenced by RemoveRelations().

◆ DropErrorMsgWrongType()

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

Definition at line 1501 of file tablecmds.c.

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

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

11997{
11999 SysScanDesc scan;
12000 HeapTuple trigtup;
12001
12003 Anum_pg_trigger_tgconstraint,
12004 BTEqualStrategyNumber, F_OIDEQ,
12005 ObjectIdGetDatum(conoid));
12006 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12007 NULL, 1, &key);
12008 while ((trigtup = systable_getnext(scan)) != NULL)
12009 {
12010 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12011 ObjectAddress trigger;
12012
12013 /* Invalid if trigger is not for a referential integrity constraint */
12014 if (!OidIsValid(trgform->tgconstrrelid))
12015 continue;
12016 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12017 continue;
12018 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12019 continue;
12020
12021 /* We should be dropping trigger related to foreign key constraint */
12022 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12023 trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12024 trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12025 trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12026 trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12027 trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12028 trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12029 trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12030 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12031 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12032 trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12033 trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12034
12035 /*
12036 * The constraint is originally set up to contain this trigger as an
12037 * implementation object, so there's a dependency record that links
12038 * the two; however, since the trigger is no longer needed, we remove
12039 * the dependency link in order to be able to drop the trigger while
12040 * keeping the constraint intact.
12041 */
12042 deleteDependencyRecordsFor(TriggerRelationId,
12043 trgform->oid,
12044 false);
12045 /* make dependency deletion visible to performDeletion */
12047 ObjectAddressSet(trigger, TriggerRelationId,
12048 trgform->oid);
12049 performDeletion(&trigger, DROP_RESTRICT, 0);
12050 /* make trigger drop visible, in case the loop iterates */
12052 }
12053
12054 systable_endscan(scan);
12055}

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

Referenced by ATExecAlterConstrEnforceability(), and AttachPartitionForeignKey().

◆ ExecuteTruncate()

void ExecuteTruncate ( TruncateStmt stmt)

Definition at line 1851 of file tablecmds.c.

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

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

Referenced by standard_ProcessUtility().

◆ ExecuteTruncateGuts()

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

Definition at line 1975 of file tablecmds.c.

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

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

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

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

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

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

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

References i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 13628 of file tablecmds.c.

13629{
13630 CoercionPathType ret;
13631
13632 if (targetTypeId == sourceTypeId)
13633 {
13635 *funcid = InvalidOid;
13636 }
13637 else
13638 {
13639 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13640 COERCION_IMPLICIT, funcid);
13641 if (ret == COERCION_PATH_NONE)
13642 /* A previously-relied-upon cast is now gone. */
13643 elog(ERROR, "could not find cast from %u to %u",
13644 sourceTypeId, targetTypeId);
13645 }
13646
13647 return ret;
13648}
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 21980 of file tablecmds.c.

21981{
21982 char cmethod;
21983
21984 if (compression == NULL || strcmp(compression, "default") == 0)
21986
21987 /*
21988 * To specify a nondefault method, the column data type must be toastable.
21989 * Note this says nothing about whether the column's attstorage setting
21990 * permits compression; we intentionally allow attstorage and
21991 * attcompression to be independent. But with a non-toastable type,
21992 * attstorage could not be set to a value that would permit compression.
21993 *
21994 * We don't actually need to enforce this, since nothing bad would happen
21995 * if attcompression were non-default; it would never be consulted. But
21996 * it seems more user-friendly to complain about a certainly-useless
21997 * attempt to set the property.
21998 */
21999 if (!TypeIsToastable(atttypid))
22000 ereport(ERROR,
22001 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22002 errmsg("column data type %s does not support compression",
22003 format_type_be(atttypid))));
22004
22005 cmethod = CompressionNameToMethod(compression);
22006 if (!CompressionMethodIsValid(cmethod))
22007 ereport(ERROR,
22008 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22009 errmsg("invalid compression method \"%s\"", compression)));
22010
22011 return cmethod;
22012}
#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 22018 of file tablecmds.c.

22019{
22020 char cstorage = 0;
22021
22022 if (pg_strcasecmp(storagemode, "plain") == 0)
22023 cstorage = TYPSTORAGE_PLAIN;
22024 else if (pg_strcasecmp(storagemode, "external") == 0)
22025 cstorage = TYPSTORAGE_EXTERNAL;
22026 else if (pg_strcasecmp(storagemode, "extended") == 0)
22027 cstorage = TYPSTORAGE_EXTENDED;
22028 else if (pg_strcasecmp(storagemode, "main") == 0)
22029 cstorage = TYPSTORAGE_MAIN;
22030 else if (pg_strcasecmp(storagemode, "default") == 0)
22031 cstorage = get_typstorage(atttypid);
22032 else
22033 ereport(ERROR,
22034 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22035 errmsg("invalid storage type \"%s\"",
22036 storagemode)));
22037
22038 /*
22039 * safety check: do not allow toasted storage modes unless column datatype
22040 * is TOAST-aware.
22041 */
22042 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22043 ereport(ERROR,
22044 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22045 errmsg("column data type %s can only have storage PLAIN",
22046 format_type_be(atttypid))));
22047
22048 return cstorage;
22049}
char get_typstorage(Oid typid)
Definition: lsyscache.c:2586
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:32

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

12067{
12069 SysScanDesc scan;
12070 HeapTuple trigtup;
12071
12072 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12074 Anum_pg_trigger_tgconstraint,
12075 BTEqualStrategyNumber, F_OIDEQ,
12076 ObjectIdGetDatum(conoid));
12077
12078 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12079 NULL, 1, &key);
12080 while ((trigtup = systable_getnext(scan)) != NULL)
12081 {
12082 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12083
12084 if (trgform->tgconstrrelid != conrelid)
12085 continue;
12086 if (trgform->tgrelid != confrelid)
12087 continue;
12088 /* Only ever look at "action" triggers on the PK side. */
12089 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12090 continue;
12091 if (TRIGGER_FOR_DELETE(trgform->tgtype))
12092 {
12093 Assert(*deleteTriggerOid == InvalidOid);
12094 *deleteTriggerOid = trgform->oid;
12095 }
12096 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12097 {
12098 Assert(*updateTriggerOid == InvalidOid);
12099 *updateTriggerOid = trgform->oid;
12100 }
12101#ifndef USE_ASSERT_CHECKING
12102 /* In an assert-enabled build, continue looking to find duplicates */
12103 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12104 break;
12105#endif
12106 }
12107
12108 if (!OidIsValid(*deleteTriggerOid))
12109 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12110 conoid);
12111 if (!OidIsValid(*updateTriggerOid))
12112 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12113 conoid);
12114
12115 systable_endscan(scan);
12116}
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3208
#define RI_TRIGGER_PK
Definition: trigger.h:284

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

12128{
12130 SysScanDesc scan;
12131 HeapTuple trigtup;
12132
12133 *insertTriggerOid = *updateTriggerOid = InvalidOid;
12135 Anum_pg_trigger_tgconstraint,
12136 BTEqualStrategyNumber, F_OIDEQ,
12137 ObjectIdGetDatum(conoid));
12138
12139 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12140 NULL, 1, &key);
12141 while ((trigtup = systable_getnext(scan)) != NULL)
12142 {
12143 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12144
12145 if (trgform->tgconstrrelid != confrelid)
12146 continue;
12147 if (trgform->tgrelid != conrelid)
12148 continue;
12149 /* Only ever look at "check" triggers on the FK side. */
12150 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12151 continue;
12152 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12153 {
12154 Assert(*insertTriggerOid == InvalidOid);
12155 *insertTriggerOid = trgform->oid;
12156 }
12157 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12158 {
12159 Assert(*updateTriggerOid == InvalidOid);
12160 *updateTriggerOid = trgform->oid;
12161 }
12162#ifndef USE_ASSERT_CHECKING
12163 /* In an assert-enabled build, continue looking to find duplicates. */
12164 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12165 break;
12166#endif
12167 }
12168
12169 if (!OidIsValid(*insertTriggerOid))
12170 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12171 conoid);
12172 if (!OidIsValid(*updateTriggerOid))
12173 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12174 conoid);
12175
12176 systable_endscan(scan);
12177}
#define RI_TRIGGER_FK
Definition: trigger.h:285

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

21879{
21880 Relation pg_constraint;
21881 HeapTuple tuple;
21882 SysScanDesc scan;
21883 ScanKeyData key[2];
21884 List *constraints = NIL;
21885
21886 /*
21887 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21888 * scan.
21889 */
21890 if (RelationGetIndexList(partition) == NIL ||
21893 return NIL;
21894
21895 /* Search for constraints referencing this table */
21896 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21897 ScanKeyInit(&key[0],
21898 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21899 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21900 ScanKeyInit(&key[1],
21901 Anum_pg_constraint_contype, BTEqualStrategyNumber,
21902 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21903
21904 /* XXX This is a seqscan, as we don't have a usable index */
21905 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21906 while ((tuple = systable_getnext(scan)) != NULL)
21907 {
21908 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21909
21910 /*
21911 * We only need to process constraints that are part of larger ones.
21912 */
21913 if (!OidIsValid(constrForm->conparentid))
21914 continue;
21915
21916 constraints = lappend_oid(constraints, constrForm->oid);
21917 }
21918
21919 systable_endscan(scan);
21920 table_close(pg_constraint, AccessShareLock);
21921
21922 return constraints;
21923}
#define bms_is_empty(a)
Definition: bitmapset.h:118
@ INDEX_ATTR_BITMAP_KEY
Definition: relcache.h:69

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

Referenced by ATDetachCheckNoForeignKeyRefs(), and DetachPartitionFinalize().

◆ index_copy_data()

static void index_copy_data ( Relation  rel,
RelFileLocator  newrlocator 
)
static

Definition at line 17139 of file tablecmds.c.

17140{
17141 SMgrRelation dstrel;
17142
17143 /*
17144 * Since we copy the file directly without looking at the shared buffers,
17145 * we'd better first flush out any pages of the source relation that are
17146 * in shared buffers. We assume no new changes will be made while we are
17147 * holding exclusive lock on the rel.
17148 */
17150
17151 /*
17152 * Create and copy all forks of the relation, and schedule unlinking of
17153 * old physical files.
17154 *
17155 * NOTE: any conflict in relfilenumber value will be caught in
17156 * RelationCreateStorage().
17157 */
17158 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17159
17160 /* copy main fork */
17162 rel->rd_rel->relpersistence);
17163
17164 /* copy those extra forks that exist */
17165 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17166 forkNum <= MAX_FORKNUM; forkNum++)
17167 {
17168 if (smgrexists(RelationGetSmgr(rel), forkNum))
17169 {
17170 smgrcreate(dstrel, forkNum, false);
17171
17172 /*
17173 * WAL log creation if the relation is persistent, or this is the
17174 * init fork of an unlogged relation.
17175 */
17176 if (RelationIsPermanent(rel) ||
17177 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17178 forkNum == INIT_FORKNUM))
17179 log_smgrcreate(&newrlocator, forkNum);
17180 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17181 rel->rd_rel->relpersistence);
17182 }
17183 }
17184
17185 /* drop old relation, and close new one */
17187 smgrclose(dstrel);
17188}
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:4942
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:577
ForkNumber
Definition: relpath.h:56
@ MAIN_FORKNUM
Definition: relpath.h:58
@ INIT_FORKNUM
Definition: relpath.h:61
#define MAX_FORKNUM
Definition: relpath.h:70
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition: smgr.c:481
void smgrclose(SMgrRelation reln)
Definition: smgr.c:374
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:462
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition: storage.c:478
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition: storage.c:122
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition: storage.c:187
void RelationDropStorage(Relation rel)
Definition: storage.c:207

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

Referenced by ATExecSetTableSpace().

◆ MarkInheritDetached()

static void MarkInheritDetached ( Relation  child_rel,
Relation  parent_rel 
)
static

Definition at line 17857 of file tablecmds.c.

17858{
17859 Relation catalogRelation;
17860 SysScanDesc scan;
17862 HeapTuple inheritsTuple;
17863 bool found = false;
17864
17865 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17866
17867 /*
17868 * Find pg_inherits entries by inhparent. (We need to scan them all in
17869 * order to verify that no other partition is pending detach.)
17870 */
17871 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17873 Anum_pg_inherits_inhparent,
17874 BTEqualStrategyNumber, F_OIDEQ,
17875 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17876 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17877 true, NULL, 1, &key);
17878
17879 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17880 {
17881 Form_pg_inherits inhForm;
17882
17883 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17884 if (inhForm->inhdetachpending)
17885 ereport(ERROR,
17886 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17887 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17888 get_rel_name(inhForm->inhrelid),
17889 get_namespace_name(parent_rel->rd_rel->relnamespace),
17890 RelationGetRelationName(parent_rel)),
17891 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17892
17893 if (inhForm->inhrelid == RelationGetRelid(child_rel))
17894 {
17895 HeapTuple newtup;
17896
17897 newtup = heap_copytuple(inheritsTuple);
17898 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17899
17900 CatalogTupleUpdate(catalogRelation,
17901 &inheritsTuple->t_self,
17902 newtup);
17903 found = true;
17904 heap_freetuple(newtup);
17905 /* keep looking, to ensure we catch others pending detach */
17906 }
17907 }
17908
17909 /* Done */
17910 systable_endscan(scan);
17911 table_close(catalogRelation, RowExclusiveLock);
17912
17913 if (!found)
17914 ereport(ERROR,
17916 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17917 RelationGetRelationName(child_rel),
17918 RelationGetRelationName(parent_rel))));
17919}
#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 2539 of file tablecmds.c.

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

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_rel, RELATION_IS_OTHER_TEMP, 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 17490 of file tablecmds.c.

17491{
17492 Relation attrrel;
17493 TupleDesc parent_desc;
17494
17495 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17496 parent_desc = RelationGetDescr(parent_rel);
17497
17498 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17499 {
17500 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17501 char *parent_attname = NameStr(parent_att->attname);
17502 HeapTuple tuple;
17503
17504 /* Ignore dropped columns in the parent. */
17505 if (parent_att->attisdropped)
17506 continue;
17507
17508 /* Find same column in child (matching on column name). */
17509 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17510 if (HeapTupleIsValid(tuple))
17511 {
17512 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17513
17514 if (parent_att->atttypid != child_att->atttypid ||
17515 parent_att->atttypmod != child_att->atttypmod)
17516 ereport(ERROR,
17517 (errcode(ERRCODE_DATATYPE_MISMATCH),
17518 errmsg("child table \"%s\" has different type for column \"%s\"",
17519 RelationGetRelationName(child_rel), parent_attname)));
17520
17521 if (parent_att->attcollation != child_att->attcollation)
17522 ereport(ERROR,
17523 (errcode(ERRCODE_COLLATION_MISMATCH),
17524 errmsg("child table \"%s\" has different collation for column \"%s\"",
17525 RelationGetRelationName(child_rel), parent_attname)));
17526
17527 /*
17528 * If the parent has a not-null constraint that's not NO INHERIT,
17529 * make sure the child has one too.
17530 *
17531 * Other constraints are checked elsewhere.
17532 */
17533 if (parent_att->attnotnull && !child_att->attnotnull)
17534 {
17535 HeapTuple contup;
17536
17537 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17538 parent_att->attnum);
17539 if (HeapTupleIsValid(contup) &&
17540 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17541 ereport(ERROR,
17542 errcode(ERRCODE_DATATYPE_MISMATCH),
17543 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17544 parent_attname, RelationGetRelationName(child_rel)));
17545 }
17546
17547 /*
17548 * Child column must be generated if and only if parent column is.
17549 */
17550 if (parent_att->attgenerated && !child_att->attgenerated)
17551 ereport(ERROR,
17552 (errcode(ERRCODE_DATATYPE_MISMATCH),
17553 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17554 if (child_att->attgenerated && !parent_att->attgenerated)
17555 ereport(ERROR,
17556 (errcode(ERRCODE_DATATYPE_MISMATCH),
17557 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17558
17559 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17560 ereport(ERROR,
17561 (errcode(ERRCODE_DATATYPE_MISMATCH),
17562 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17563 errdetail("Parent column is %s, child column is %s.",
17564 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17565 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17566
17567 /*
17568 * Regular inheritance children are independent enough not to
17569 * inherit identity columns. But partitions are integral part of
17570 * a partitioned table and inherit identity column.
17571 */
17572 if (ispartition)
17573 child_att->attidentity = parent_att->attidentity;
17574
17575 /*
17576 * OK, bump the child column's inheritance count. (If we fail
17577 * later on, this change will just roll back.)
17578 */
17579 if (pg_add_s16_overflow(child_att->attinhcount, 1,
17580 &child_att->attinhcount))
17581 ereport(ERROR,
17582 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17583 errmsg("too many inheritance parents"));
17584
17585 /*
17586 * In case of partitions, we must enforce that value of attislocal
17587 * is same in all partitions. (Note: there are only inherited
17588 * attributes in partitions)
17589 */
17590 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17591 {
17592 Assert(child_att->attinhcount == 1);
17593 child_att->attislocal = false;
17594 }
17595
17596 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17597 heap_freetuple(tuple);
17598 }
17599 else
17600 {
17601 ereport(ERROR,
17602 (errcode(ERRCODE_DATATYPE_MISMATCH),
17603 errmsg("child table is missing column \"%s\"", parent_attname)));
17604 }
17605 }
17606
17607 table_close(attrrel, RowExclusiveLock);
17608}

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

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

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

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

17629{
17630 Relation constraintrel;
17631 SysScanDesc parent_scan;
17632 ScanKeyData parent_key;
17633 HeapTuple parent_tuple;
17634 Oid parent_relid = RelationGetRelid(parent_rel);
17635 AttrMap *attmap;
17636
17637 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17638
17639 /* Outer loop scans through the parent's constraint definitions */
17640 ScanKeyInit(&parent_key,
17641 Anum_pg_constraint_conrelid,
17642 BTEqualStrategyNumber, F_OIDEQ,
17643 ObjectIdGetDatum(parent_relid));
17644 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17645 true, NULL, 1, &parent_key);
17646
17647 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17648 RelationGetDescr(child_rel),
17649 true);
17650
17651 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17652 {
17653 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17654 SysScanDesc child_scan;
17655 ScanKeyData child_key;
17656 HeapTuple child_tuple;
17657 AttrNumber parent_attno;
17658 bool found = false;
17659
17660 if (parent_con->contype != CONSTRAINT_CHECK &&
17661 parent_con->contype != CONSTRAINT_NOTNULL)
17662 continue;
17663
17664 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17665 if (parent_con->connoinherit)
17666 continue;
17667
17668 if (parent_con->contype == CONSTRAINT_NOTNULL)
17669 parent_attno = extractNotNullColumn(parent_tuple);
17670 else
17671 parent_attno = InvalidAttrNumber;
17672
17673 /* Search for a child constraint matching this one */
17674 ScanKeyInit(&child_key,
17675 Anum_pg_constraint_conrelid,
17676 BTEqualStrategyNumber, F_OIDEQ,
17678 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17679 true, NULL, 1, &child_key);
17680
17681 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17682 {
17683 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17684 HeapTuple child_copy;
17685
17686 if (child_con->contype != parent_con->contype)
17687 continue;
17688
17689 /*
17690 * CHECK constraint are matched by constraint name, NOT NULL ones
17691 * by attribute number.
17692 */
17693 if (child_con->contype == CONSTRAINT_CHECK)
17694 {
17695 if (strcmp(NameStr(parent_con->conname),
17696 NameStr(child_con->conname)) != 0)
17697 continue;
17698 }
17699 else if (child_con->contype == CONSTRAINT_NOTNULL)
17700 {
17701 Form_pg_attribute parent_attr;
17702 Form_pg_attribute child_attr;
17703 AttrNumber child_attno;
17704
17705 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17706 child_attno = extractNotNullColumn(child_tuple);
17707 if (parent_attno != attmap->attnums[child_attno - 1])
17708 continue;
17709
17710 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17711 /* there shouldn't be constraints on dropped columns */
17712 if (parent_attr->attisdropped || child_attr->attisdropped)
17713 elog(ERROR, "found not-null constraint on dropped columns");
17714 }
17715
17716 if (child_con->contype == CONSTRAINT_CHECK &&
17717 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17718 ereport(ERROR,
17719 (errcode(ERRCODE_DATATYPE_MISMATCH),
17720 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17721 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17722
17723 /*
17724 * If the child constraint is "no inherit" then cannot merge
17725 */
17726 if (child_con->connoinherit)
17727 ereport(ERROR,
17728 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17729 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17730 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17731
17732 /*
17733 * If the child constraint is "not valid" then cannot merge with a
17734 * valid parent constraint
17735 */
17736 if (parent_con->convalidated && child_con->conenforced &&
17737 !child_con->convalidated)
17738 ereport(ERROR,
17739 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17740 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17741 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17742
17743 /*
17744 * A NOT ENFORCED child constraint cannot be merged with an
17745 * ENFORCED parent constraint. However, the reverse is allowed,
17746 * where the child constraint is ENFORCED.
17747 */
17748 if (parent_con->conenforced && !child_con->conenforced)
17749 ereport(ERROR,
17750 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17751 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17752 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17753
17754 /*
17755 * OK, bump the child constraint's inheritance count. (If we fail
17756 * later on, this change will just roll back.)
17757 */
17758 child_copy = heap_copytuple(child_tuple);
17759 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17760
17761 if (pg_add_s16_overflow(child_con->coninhcount, 1,
17762 &child_con->coninhcount))
17763 ereport(ERROR,
17764 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17765 errmsg("too many inheritance parents"));
17766
17767 /*
17768 * In case of partitions, an inherited constraint must be
17769 * inherited only once since it cannot have multiple parents and
17770 * it is never considered local.
17771 */
17772 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17773 {
17774 Assert(child_con->coninhcount == 1);
17775 child_con->conislocal = false;
17776 }
17777
17778 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17779 heap_freetuple(child_copy);
17780
17781 found = true;
17782 break;
17783 }
17784
17785 systable_endscan(child_scan);
17786
17787 if (!found)
17788 {
17789 if (parent_con->contype == CONSTRAINT_NOTNULL)
17790 ereport(ERROR,
17791 errcode(ERRCODE_DATATYPE_MISMATCH),
17792 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17793 get_attname(parent_relid,
17794 extractNotNullColumn(parent_tuple),
17795 false),
17796 RelationGetRelationName(child_rel)));
17797
17798 ereport(ERROR,
17799 (errcode(ERRCODE_DATATYPE_MISMATCH),
17800 errmsg("child table is missing constraint \"%s\"",
17801 NameStr(parent_con->conname))));
17802 }
17803 }
17804
17805 systable_endscan(parent_scan);
17806 table_close(constraintrel, RowExclusiveLock);
17807}
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
Definition: tablecmds.c:17461

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

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

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

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

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

20051{
20052 List *existConstraint = NIL;
20053 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20054 int i;
20055
20056 if (constr && constr->has_not_null)
20057 {
20058 int natts = scanrel->rd_att->natts;
20059
20060 for (i = 1; i <= natts; i++)
20061 {
20062 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20063
20064 /* invalid not-null constraint must be ignored here */
20065 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20066 {
20067 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20068 NullTest *ntest = makeNode(NullTest);
20069
20070 ntest->arg = (Expr *) makeVar(1,
20071 i,
20072 wholeatt->atttypid,
20073 wholeatt->atttypmod,
20074 wholeatt->attcollation,
20075 0);
20076 ntest->nulltesttype = IS_NOT_NULL;
20077
20078 /*
20079 * argisrow=false is correct even for a composite column,
20080 * because attnotnull does not represent a SQL-spec IS NOT
20081 * NULL test in such a case, just IS DISTINCT FROM NULL.
20082 */
20083 ntest->argisrow = false;
20084 ntest->location = -1;
20085 existConstraint = lappend(existConstraint, ntest);
20086 }
20087 }
20088 }
20089
20090 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20091}

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

Referenced by check_default_partition_contents(), and QueuePartitionConstraintValidation().

◆ PreCommit_on_commit_actions()

void PreCommit_on_commit_actions ( void  )

Definition at line 19310 of file tablecmds.c.

19311{
19312 ListCell *l;
19313 List *oids_to_truncate = NIL;
19314 List *oids_to_drop = NIL;
19315
19316 foreach(l, on_commits)
19317 {
19318 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19319
19320 /* Ignore entry if already dropped in this xact */
19322 continue;
19323
19324 switch (oc->oncommit)
19325 {
19326 case ONCOMMIT_NOOP:
19328 /* Do nothing (there shouldn't be such entries, actually) */
19329 break;
19331
19332 /*
19333 * If this transaction hasn't accessed any temporary
19334 * relations, we can skip truncating ON COMMIT DELETE ROWS
19335 * tables, as they must still be empty.
19336 */
19338 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19339 break;
19340 case ONCOMMIT_DROP:
19341 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19342 break;
19343 }
19344 }
19345
19346 /*
19347 * Truncate relations before dropping so that all dependencies between
19348 * relations are removed after they are worked on. Doing it like this
19349 * might be a waste as it is possible that a relation being truncated will
19350 * be dropped anyway due to its parent being dropped, but this makes the
19351 * code more robust because of not having to re-check that the relation
19352 * exists at truncation time.
19353 */
19354 if (oids_to_truncate != NIL)
19355 heap_truncate(oids_to_truncate);
19356
19357 if (oids_to_drop != NIL)
19358 {
19359 ObjectAddresses *targetObjects = new_object_addresses();
19360
19361 foreach(l, oids_to_drop)
19362 {
19363 ObjectAddress object;
19364
19365 object.classId = RelationRelationId;
19366 object.objectId = lfirst_oid(l);
19367 object.objectSubId = 0;
19368
19369 Assert(!object_address_present(&object, targetObjects));
19370
19371 add_exact_object_address(&object, targetObjects);
19372 }
19373
19374 /*
19375 * Object deletion might involve toast table access (to clean up
19376 * toasted catalog entries), so ensure we have a valid snapshot.
19377 */
19379
19380 /*
19381 * Since this is an automatic drop, rather than one directly initiated
19382 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19383 */
19386
19388
19389#ifdef USE_ASSERT_CHECKING
19390
19391 /*
19392 * Note that table deletion will call remove_on_commit_action, so the
19393 * entry should get marked as deleted.
19394 */
19395 foreach(l, on_commits)
19396 {
19397 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19398
19399 if (oc->oncommit != ONCOMMIT_DROP)
19400 continue;
19401
19403 }
19404#endif
19405 }
19406}
#define PERFORM_DELETION_QUIETLY
Definition: dependency.h:94
void heap_truncate(List *relids)
Definition: heap.c:3587
@ ONCOMMIT_DELETE_ROWS
Definition: primnodes.h:60
@ ONCOMMIT_PRESERVE_ROWS
Definition: primnodes.h:59
@ ONCOMMIT_DROP
Definition: primnodes.h:61
OnCommitAction oncommit
Definition: tablecmds.c:119
int MyXactFlags
Definition: xact.c:137
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:103

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

13112{
13114 AlteredTableInfo *tab;
13115 HeapTuple copyTuple;
13116 Form_pg_constraint copy_con;
13117
13118 List *children = NIL;
13119 ListCell *child;
13120 NewConstraint *newcon;
13121 Datum val;
13122 char *conbin;
13123
13124 con = (Form_pg_constraint) GETSTRUCT(contuple);
13125 Assert(con->contype == CONSTRAINT_CHECK);
13126
13127 /*
13128 * If we're recursing, the parent has already done this, so skip it. Also,
13129 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13130 * for it in the children.
13131 */
13132 if (!recursing && !con->connoinherit)
13133 children = find_all_inheritors(RelationGetRelid(rel),
13134 lockmode, NULL);
13135
13136 /*
13137 * For CHECK constraints, we must ensure that we only mark the constraint
13138 * as validated on the parent if it's already validated on the children.
13139 *
13140 * We recurse before validating on the parent, to reduce risk of
13141 * deadlocks.
13142 */
13143 foreach(child, children)
13144 {
13145 Oid childoid = lfirst_oid(child);
13146 Relation childrel;
13147
13148 if (childoid == RelationGetRelid(rel))
13149 continue;
13150
13151 /*
13152 * If we are told not to recurse, there had better not be any child
13153 * tables, because we can't mark the constraint on the parent valid
13154 * unless it is valid for all child tables.
13155 */
13156 if (!recurse)
13157 ereport(ERROR,
13158 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13159 errmsg("constraint must be validated on child tables too")));
13160
13161 /* find_all_inheritors already got lock */
13162 childrel = table_open(childoid, NoLock);
13163
13164 ATExecValidateConstraint(wqueue, childrel, constrName, false,
13165 true, lockmode);
13166 table_close(childrel, NoLock);
13167 }
13168
13169 /* Queue validation for phase 3 */
13170 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13171 newcon->name = constrName;
13172 newcon->contype = CONSTR_CHECK;
13173 newcon->refrelid = InvalidOid;
13174 newcon->refindid = InvalidOid;
13175 newcon->conid = con->oid;
13176
13177 val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13178 Anum_pg_constraint_conbin);
13179 conbin = TextDatumGetCString(val);
13180 newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13181
13182 /* Find or create work queue entry for this table */
13183 tab = ATGetQueueEntry(wqueue, rel);
13184 tab->constraints = lappend(tab->constraints, newcon);
13185
13186 /*
13187 * Invalidate relcache so that others see the new validated constraint.
13188 */
13190
13191 /*
13192 * Now update the catalog, while we have the door open.
13193 */
13194 copyTuple = heap_copytuple(contuple);
13195 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13196 copy_con->convalidated = true;
13197 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13198
13199 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13200
13201 heap_freetuple(copyTuple);
13202}
long val
Definition: informix.c:689
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:625

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  fkrel,
Oid  pkrelid,
HeapTuple  contuple,
LOCKMODE  lockmode 
)
static

Definition at line 12990 of file tablecmds.c.

12992{
12994 AlteredTableInfo *tab;
12995 HeapTuple copyTuple;
12996 Form_pg_constraint copy_con;
12997
12998 con = (Form_pg_constraint) GETSTRUCT(contuple);
12999 Assert(con->contype == CONSTRAINT_FOREIGN);
13000 Assert(!con->convalidated);
13001
13002 /*
13003 * Add the validation to phase 3's queue; not needed for partitioned
13004 * tables themselves, only for their partitions.
13005 *
13006 * When the referenced table (pkrelid) is partitioned, the referencing
13007 * table (fkrel) has one pg_constraint row pointing to each partition
13008 * thereof. These rows are there only to support action triggers and no
13009 * table scan is needed, therefore skip this for them as well.
13010 */
13011 if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13012 con->confrelid == pkrelid)
13013 {
13014 NewConstraint *newcon;
13015 Constraint *fkconstraint;
13016
13017 /* Queue validation for phase 3 */
13018 fkconstraint = makeNode(Constraint);
13019 /* for now this is all we need */
13020 fkconstraint->conname = pstrdup(NameStr(con->conname));
13021
13022 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13023 newcon->name = fkconstraint->conname;
13024 newcon->contype = CONSTR_FOREIGN;
13025 newcon->refrelid = con->confrelid;
13026 newcon->refindid = con->conindid;
13027 newcon->conid = con->oid;
13028 newcon->qual = (Node *) fkconstraint;
13029
13030 /* Find or create work queue entry for this table */
13031 tab = ATGetQueueEntry(wqueue, fkrel);
13032 tab->constraints = lappend(tab->constraints, newcon);
13033 }
13034
13035 /*
13036 * If the table at either end of the constraint is partitioned, we need to
13037 * recurse and handle every unvalidate constraint that is a child of this
13038 * constraint.
13039 */
13040 if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13041 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13042 {
13043 ScanKeyData pkey;
13044 SysScanDesc pscan;
13045 HeapTuple childtup;
13046
13047 ScanKeyInit(&pkey,
13048 Anum_pg_constraint_conparentid,
13049 BTEqualStrategyNumber, F_OIDEQ,
13050 ObjectIdGetDatum(con->oid));
13051
13052 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13053 true, NULL, 1, &pkey);
13054
13055 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13056 {
13057 Form_pg_constraint childcon;
13058 Relation childrel;
13059
13060 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13061
13062 /*
13063 * If the child constraint has already been validated, no further
13064 * action is required for it or its descendants, as they are all
13065 * valid.
13066 */
13067 if (childcon->convalidated)
13068 continue;
13069
13070 childrel = table_open(childcon->conrelid, lockmode);
13071
13072 /*
13073 * NB: Note that pkrelid should be passed as-is during recursion,
13074 * as it is required to identify the root referenced table.
13075 */
13076 QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13077 childtup, lockmode);
13078 table_close(childrel, NoLock);
13079 }
13080
13081 systable_endscan(pscan);
13082 }
13083
13084 /*
13085 * Now mark the pg_constraint row as validated (even if we didn't check,
13086 * notably the ones for partitions on the referenced side).
13087 *
13088 * We rely on transaction abort to roll back this change if phase 3
13089 * ultimately finds violating rows. This is a bit ugly.
13090 */
13091 copyTuple = heap_copytuple(contuple);
13092 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13093 copy_con->convalidated = true;
13094 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13095
13096 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13097
13098 heap_freetuple(copyTuple);
13099}

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

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

◆ QueueNNConstraintValidation()

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

Definition at line 13212 of file tablecmds.c.

13215{
13217 AlteredTableInfo *tab;
13218 HeapTuple copyTuple;
13219 Form_pg_constraint copy_con;
13220 List *children = NIL;
13222 char *colname;
13223
13224 con = (Form_pg_constraint) GETSTRUCT(contuple);
13225 Assert(con->contype == CONSTRAINT_NOTNULL);
13226
13227 attnum = extractNotNullColumn(contuple);
13228
13229 /*
13230 * If we're recursing, we've already done this for parent, so skip it.
13231 * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13232 * look for it in the children.
13233 *
13234 * We recurse before validating on the parent, to reduce risk of
13235 * deadlocks.
13236 */
13237 if (!recursing && !con->connoinherit)
13238 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13239
13240 colname = get_attname(RelationGetRelid(rel), attnum, false);
13241 foreach_oid(childoid, children)
13242 {
13243 Relation childrel;
13244 HeapTuple contup;
13245 Form_pg_constraint childcon;
13246 char *conname;
13247
13248 if (childoid == RelationGetRelid(rel))
13249 continue;
13250
13251 /*
13252 * If we are told not to recurse, there had better not be any child
13253 * tables, because we can't mark the constraint on the parent valid
13254 * unless it is valid for all child tables.
13255 */
13256 if (!recurse)
13257 ereport(ERROR,
13258 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13259 errmsg("constraint must be validated on child tables too"));
13260
13261 /*
13262 * The column on child might have a different attnum, so search by
13263 * column name.
13264 */
13265 contup = findNotNullConstraint(childoid, colname);
13266 if (!contup)
13267 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13268 colname, get_rel_name(childoid));
13269 childcon = (Form_pg_constraint) GETSTRUCT(contup);
13270 if (childcon->convalidated)
13271 continue;
13272
13273 /* find_all_inheritors already got lock */
13274 childrel = table_open(childoid, NoLock);
13275 conname = pstrdup(NameStr(childcon->conname));
13276
13277 /* XXX improve ATExecValidateConstraint API to avoid double search */
13278 ATExecValidateConstraint(wqueue, childrel, conname,
13279 false, true, lockmode);
13280 table_close(childrel, NoLock);
13281 }
13282
13283 /* Set attnotnull appropriately without queueing another validation */
13284 set_attnotnull(NULL, rel, attnum, true, false);
13285
13286 tab = ATGetQueueEntry(wqueue, rel);
13287 tab->verify_new_notnull = true;
13288
13289 /*
13290 * Invalidate relcache so that others see the new validated constraint.
13291 */
13293
13294 /*
13295 * Now update the catalogs, while we have the door open.
13296 */
13297 copyTuple = heap_copytuple(contuple);
13298 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13299 copy_con->convalidated = true;
13300 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13301
13302 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13303
13304 heap_freetuple(copyTuple);
13305}

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

Referenced by ATExecValidateConstraint().

◆ QueuePartitionConstraintValidation()

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

Definition at line 20167 of file tablecmds.c.

20170{
20171 /*
20172 * Based on the table's existing constraints, determine whether or not we
20173 * may skip scanning the table.
20174 */
20175 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20176 {
20177 if (!validate_default)
20179 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20180 RelationGetRelationName(scanrel))));
20181 else
20183 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20184 RelationGetRelationName(scanrel))));
20185 return;
20186 }
20187
20188 /*
20189 * Constraints proved insufficient. For plain relations, queue a
20190 * validation item now; for partitioned tables, recurse to process each
20191 * partition.
20192 */
20193 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20194 {
20195 AlteredTableInfo *tab;
20196
20197 /* Grab a work queue entry. */
20198 tab = ATGetQueueEntry(wqueue, scanrel);
20199 Assert(tab->partition_constraint == NULL);
20200 tab->partition_constraint = (Expr *) linitial(partConstraint);
20201 tab->validate_default = validate_default;
20202 }
20203 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20204 {
20205 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20206 int i;
20207
20208 for (i = 0; i < partdesc->nparts; i++)
20209 {
20210 Relation part_rel;
20211 List *thisPartConstraint;
20212
20213 /*
20214 * This is the minimum lock we need to prevent deadlocks.
20215 */
20216 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20217
20218 /*
20219 * Adjust the constraint for scanrel so that it matches this
20220 * partition's attribute numbers.
20221 */
20222 thisPartConstraint =
20223 map_partition_varattnos(partConstraint, 1,
20224 part_rel, scanrel);
20225
20226 QueuePartitionConstraintValidation(wqueue, part_rel,
20227 thisPartConstraint,
20228 validate_default);
20229 table_close(part_rel, NoLock); /* keep lock till commit */
20230 }
20231 }
20232}
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:20049

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

19578{
19579 Node *stmt = (Node *) arg;
19580 ObjectType reltype;
19581 HeapTuple tuple;
19582 Form_pg_class classform;
19583 AclResult aclresult;
19584 char relkind;
19585
19586 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19587 if (!HeapTupleIsValid(tuple))
19588 return; /* concurrently dropped */
19589 classform = (Form_pg_class) GETSTRUCT(tuple);
19590 relkind = classform->relkind;
19591
19592 /* Must own relation. */
19593 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19595
19596 /* No system table modifications unless explicitly allowed. */
19597 if (!allowSystemTableMods && IsSystemClass(relid, classform))
19598 ereport(ERROR,
19599 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19600 errmsg("permission denied: \"%s\" is a system catalog",
19601 rv->relname)));
19602
19603 /*
19604 * Extract the specified relation type from the statement parse tree.
19605 *
19606 * Also, for ALTER .. RENAME, check permissions: the user must (still)
19607 * have CREATE rights on the containing namespace.
19608 */
19609 if (IsA(stmt, RenameStmt))
19610 {
19611 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19613 if (aclresult != ACLCHECK_OK)
19614 aclcheck_error(aclresult, OBJECT_SCHEMA,
19615 get_namespace_name(classform->relnamespace));
19616 reltype = ((RenameStmt *) stmt)->renameType;
19617 }
19618 else if (IsA(stmt, AlterObjectSchemaStmt))
19619 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19620
19621 else if (IsA(stmt, AlterTableStmt))
19622 reltype = ((AlterTableStmt *) stmt)->objtype;
19623 else
19624 {
19625 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19626 reltype = OBJECT_TABLE; /* placate compiler */
19627 }
19628
19629 /*
19630 * For compatibility with prior releases, we allow ALTER TABLE to be used
19631 * with most other types of relations (but not composite types). We allow
19632 * similar flexibility for ALTER INDEX in the case of RENAME, but not
19633 * otherwise. Otherwise, the user must select the correct form of the
19634 * command for the relation at issue.
19635 */
19636 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19637 ereport(ERROR,
19638 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19639 errmsg("\"%s\" is not a sequence", rv->relname)));
19640
19641 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19642 ereport(ERROR,
19643 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19644 errmsg("\"%s\" is not a view", rv->relname)));
19645
19646 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19647 ereport(ERROR,
19648 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19649 errmsg("\"%s\" is not a materialized view", rv->relname)));
19650
19651 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19652 ereport(ERROR,
19653 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19654 errmsg("\"%s\" is not a foreign table", rv->relname)));
19655
19656 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19657 ereport(ERROR,
19658 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19659 errmsg("\"%s\" is not a composite type", rv->relname)));
19660
19661 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19662 relkind != RELKIND_PARTITIONED_INDEX
19663 && !IsA(stmt, RenameStmt))
19664 ereport(ERROR,
19665 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19666 errmsg("\"%s\" is not an index", rv->relname)));
19667
19668 /*
19669 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19670 * TYPE for that.
19671 */
19672 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19673 ereport(ERROR,
19674 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19675 errmsg("\"%s\" is a composite type", rv->relname),
19676 /* translator: %s is an SQL ALTER command */
19677 errhint("Use %s instead.",
19678 "ALTER TYPE")));
19679
19680 /*
19681 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19682 * to a different schema, such as indexes and TOAST tables.
19683 */
19685 {
19686 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19687 ereport(ERROR,
19688 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19689 errmsg("cannot change schema of index \"%s\"",
19690 rv->relname),
19691 errhint("Change the schema of the table instead.")));
19692 else if (relkind == RELKIND_COMPOSITE_TYPE)
19693 ereport(ERROR,
19694 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19695 errmsg("cannot change schema of composite type \"%s\"",
19696 rv->relname),
19697 /* translator: %s is an SQL ALTER command */
19698 errhint("Use %s instead.",
19699 "ALTER TYPE")));
19700 else if (relkind == RELKIND_TOASTVALUE)
19701 ereport(ERROR,
19702 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19703 errmsg("cannot change schema of TOAST table \"%s\"",
19704 rv->relname),
19705 errhint("Change the schema of the table instead.")));
19706 }
19707
19708 ReleaseSysCache(tuple);
19709}
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:86
ObjectType
Definition: parsenodes.h:2324
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2343
@ OBJECT_VIEW
Definition: parsenodes.h:2376
@ OBJECT_TYPE
Definition: parsenodes.h:2374

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

21516{
21518 Form_pg_class classform;
21519 HeapTuple tuple;
21520
21521 state = (struct AttachIndexCallbackState *) arg;
21522
21523 if (!state->lockedParentTbl)
21524 {
21525 LockRelationOid(state->parentTblOid, AccessShareLock);
21526 state->lockedParentTbl = true;
21527 }
21528
21529 /*
21530 * If we previously locked some other heap, and the name we're looking up
21531 * no longer refers to an index on that relation, release the now-useless
21532 * lock. XXX maybe we should do *after* we verify whether the index does
21533 * not actually belong to the same relation ...
21534 */
21535 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21536 {
21537 UnlockRelationOid(state->partitionOid, AccessShareLock);
21538 state->partitionOid = InvalidOid;
21539 }
21540
21541 /* Didn't find a relation, so no need for locking or permission checks. */
21542 if (!OidIsValid(relOid))
21543 return;
21544
21545 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21546 if (!HeapTupleIsValid(tuple))
21547 return; /* concurrently dropped, so nothing to do */
21548 classform = (Form_pg_class) GETSTRUCT(tuple);
21549 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21550 classform->relkind != RELKIND_INDEX)
21551 ereport(ERROR,
21552 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21553 errmsg("\"%s\" is not an index", rv->relname)));
21554 ReleaseSysCache(tuple);
21555
21556 /*
21557 * Since we need only examine the heap's tupledesc, an access share lock
21558 * on it (preventing any DDL) is sufficient.
21559 */
21560 state->partitionOid = IndexGetRelation(relOid, false);
21561 LockRelationOid(state->partitionOid, AccessShareLock);
21562}
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:229

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

Referenced by ATExecAttachPartitionIdx().

◆ RangeVarCallbackForDropRelation()

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

Definition at line 1692 of file tablecmds.c.

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

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

Referenced by RemoveRelations().

◆ RangeVarCallbackForRenameAttribute()

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

Definition at line 3981 of file tablecmds.c.

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

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

19522{
19523 HeapTuple tuple;
19524
19525 /* Nothing to do if the relation was not found. */
19526 if (!OidIsValid(relId))
19527 return;
19528
19529 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19530 if (!HeapTupleIsValid(tuple)) /* should not happen */
19531 elog(ERROR, "cache lookup failed for relation %u", relId);
19532
19535
19536 ReleaseSysCache(tuple);
19537}

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

19486{
19487 char relkind;
19488 AclResult aclresult;
19489
19490 /* Nothing to do if the relation was not found. */
19491 if (!OidIsValid(relId))
19492 return;
19493
19494 /*
19495 * If the relation does exist, check whether it's an index. But note that
19496 * the relation might have been dropped between the time we did the name
19497 * lookup and now. In that case, there's nothing to do.
19498 */
19499 relkind = get_rel_relkind(relId);
19500 if (!relkind)
19501 return;
19502 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19503 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19504 ereport(ERROR,
19505 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19506 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19507
19508 /* Check permissions */
19509 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19510 if (aclresult != ACLCHECK_OK)
19511 aclcheck_error(aclresult,
19513 relation->relname);
19514}
#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 19544 of file tablecmds.c.

19546{
19547 HeapTuple tuple;
19548
19549 /* Nothing to do if the relation was not found. */
19550 if (!OidIsValid(relId))
19551 return;
19552
19553 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19554 if (!HeapTupleIsValid(tuple)) /* should not happen */
19555 elog(ERROR, "cache lookup failed for relation %u", relId);
19556
19557 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19559 relation->relname);
19560
19561 if (!allowSystemTableMods &&
19562 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19563 ereport(ERROR,
19564 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19565 errmsg("permission denied: \"%s\" is a system catalog",
19566 relation->relname)));
19567
19568 ReleaseSysCache(tuple);
19569}

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

15837{
15838 CommentStmt *cmd;
15839 char *comment_str;
15840 AlterTableCmd *newcmd;
15841
15842 /* Look for comment for object wanted, and leave if none */
15843 comment_str = GetComment(objid, ConstraintRelationId, 0);
15844 if (comment_str == NULL)
15845 return;
15846
15847 /* Build CommentStmt node, copying all input data for safety */
15848 cmd = makeNode(CommentStmt);
15849 if (rel)
15850 {
15852 cmd->object = (Node *)
15855 makeString(pstrdup(conname)));
15856 }
15857 else
15858 {
15860 cmd->object = (Node *)
15862 makeString(pstrdup(conname)));
15863 }
15864 cmd->comment = comment_str;
15865
15866 /* Append it to list of commands */
15867 newcmd = makeNode(AlterTableCmd);
15868 newcmd->subtype = AT_ReAddComment;
15869 newcmd->def = (Node *) cmd;
15870 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15871}
TypeName * makeTypeNameFromNameList(List *names)
Definition: makefuncs.c:531
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2365
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2338
#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:3362
ObjectType objtype
Definition: parsenodes.h:3360
Node * object
Definition: parsenodes.h:3361

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

21731{
21732 Oid existingIdx;
21733
21734 existingIdx = index_get_partition(partitionTbl,
21735 RelationGetRelid(parentIdx));
21736 if (OidIsValid(existingIdx))
21737 ereport(ERROR,
21738 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21739 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21740 RelationGetRelationName(partIdx),
21741 RelationGetRelationName(parentIdx)),
21742 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
21743 get_rel_name(existingIdx),
21744 RelationGetRelationName(partitionTbl))));
21745}

References ereport, errcode(), errdetail(), errmsg(), ERROR, get_rel_name(), 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 19251 of file tablecmds.c.

19252{
19253 OnCommitItem *oc;
19254 MemoryContext oldcxt;
19255
19256 /*
19257 * We needn't bother registering the relation unless there is an ON COMMIT
19258 * action we need to take.
19259 */
19261 return;
19262
19264
19265 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
19266 oc->relid = relid;
19267 oc->oncommit = action;
19270
19271 /*
19272 * We use lcons() here so that ON COMMIT actions are processed in reverse
19273 * order of registration. That might not be essential but it seems
19274 * reasonable.
19275 */
19277
19278 MemoryContextSwitchTo(oldcxt);
19279}
List * lcons(void *datum, List *list)
Definition: list.c:495
MemoryContext CacheMemoryContext
Definition: mcxt.c:169

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

18394{
18395 Relation pg_index;
18396 Relation pg_class;
18397 HeapTuple pg_class_tuple;
18398 HeapTuple pg_index_tuple;
18399 Form_pg_class pg_class_form;
18400 Form_pg_index pg_index_form;
18401 ListCell *index;
18402
18403 /*
18404 * Check whether relreplident has changed, and update it if so.
18405 */
18406 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18407 pg_class_tuple = SearchSysCacheCopy1(RELOID,
18409 if (!HeapTupleIsValid(pg_class_tuple))
18410 elog(ERROR, "cache lookup failed for relation \"%s\"",
18412 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18413 if (pg_class_form->relreplident != ri_type)
18414 {
18415 pg_class_form->relreplident = ri_type;
18416 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18417 }
18418 table_close(pg_class, RowExclusiveLock);
18419 heap_freetuple(pg_class_tuple);
18420
18421 /*
18422 * Update the per-index indisreplident flags correctly.
18423 */
18424 pg_index = table_open(IndexRelationId, RowExclusiveLock);
18425 foreach(index, RelationGetIndexList(rel))
18426 {
18427 Oid thisIndexOid = lfirst_oid(index);
18428 bool dirty = false;
18429
18430 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18431 ObjectIdGetDatum(thisIndexOid));
18432 if (!HeapTupleIsValid(pg_index_tuple))
18433 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18434 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18435
18436 if (thisIndexOid == indexOid)
18437 {
18438 /* Set the bit if not already set. */
18439 if (!pg_index_form->indisreplident)
18440 {
18441 dirty = true;
18442 pg_index_form->indisreplident = true;
18443 }
18444 }
18445 else
18446 {
18447 /* Unset the bit if set. */
18448 if (pg_index_form->indisreplident)
18449 {
18450 dirty = true;
18451 pg_index_form->indisreplident = false;
18452 }
18453 }
18454
18455 if (dirty)
18456 {
18457 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18458 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18459 InvalidOid, is_internal);
18460
18461 /*
18462 * Invalidate the relcache for the table, so that after we commit
18463 * all sessions will refresh the table's replica identity index
18464 * before attempting any UPDATE or DELETE on the table. (If we
18465 * changed the table's pg_class row above, then a relcache inval
18466 * is already queued due to that; but we might not have.)
18467 */
18469 }
18470 heap_freetuple(pg_index_tuple);
18471 }
18472
18473 table_close(pg_index, RowExclusiveLock);
18474}
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 15035 of file tablecmds.c.

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

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

15277{
15278 if (!get_index_isclustered(indoid))
15279 return;
15280
15281 if (tab->clusterOnIndex)
15282 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15283
15284 tab->clusterOnIndex = get_rel_name(indoid);
15285}
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3768

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

15293{
15294 /*
15295 * This de-duplication check is critical for two independent reasons: we
15296 * mustn't try to recreate the same constraint twice, and if a constraint
15297 * depends on more than one column whose type is to be altered, we must
15298 * capture its definition string before applying any of the column type
15299 * changes. ruleutils.c will get confused if we ask again later.
15300 */
15301 if (!list_member_oid(tab->changedConstraintOids, conoid))
15302 {
15303 /* OK, capture the constraint's existing definition string */
15304 char *defstring = pg_get_constraintdef_command(conoid);
15305 Oid indoid;
15306
15307 /*
15308 * It is critical to create not-null constraints ahead of primary key
15309 * indexes; otherwise, the not-null constraint would be created by the
15310 * primary key, and the constraint name would be wrong.
15311 */
15312 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15313 {
15314 tab->changedConstraintOids = lcons_oid(conoid,
15316 tab->changedConstraintDefs = lcons(defstring,
15318 }
15319 else
15320 {
15321
15323 conoid);
15325 defstring);
15326 }
15327
15328 /*
15329 * For the index of a constraint, if any, remember if it is used for
15330 * the table's replica identity or if it is a clustered index, so that
15331 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15332 * those properties.
15333 */
15334 indoid = get_constraint_index(conoid);
15335 if (OidIsValid(indoid))
15336 {
15338 RememberClusterOnForRebuilding(indoid, tab);
15339 }
15340 }
15341}
List * lcons_oid(Oid datum, List *list)
Definition: list.c:531
char * pg_get_constraintdef_command(Oid constraintId)
Definition: ruleutils.c:2183
static void RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15261
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15276

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

15349{
15350 /*
15351 * This de-duplication check is critical for two independent reasons: we
15352 * mustn't try to recreate the same index twice, and if an index depends
15353 * on more than one column whose type is to be altered, we must capture
15354 * its definition string before applying any of the column type changes.
15355 * ruleutils.c will get confused if we ask again later.
15356 */
15357 if (!list_member_oid(tab->changedIndexOids, indoid))
15358 {
15359 /*
15360 * Before adding it as an index-to-rebuild, we'd better see if it
15361 * belongs to a constraint, and if so rebuild the constraint instead.
15362 * Typically this check fails, because constraint indexes normally
15363 * have only dependencies on their constraint. But it's possible for
15364 * such an index to also have direct dependencies on table columns,
15365 * for example with a partial exclusion constraint.
15366 */
15367 Oid conoid = get_index_constraint(indoid);
15368
15369 if (OidIsValid(conoid))
15370 {
15372 }
15373 else
15374 {
15375 /* OK, capture the index's existing definition string */
15376 char *defstring = pg_get_indexdef_string(indoid);
15377
15379 indoid);
15381 defstring);
15382
15383 /*
15384 * Remember if this index is used for the table's replica identity
15385 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15386 * can queue up commands necessary to restore those properties.
15387 */
15389 RememberClusterOnForRebuilding(indoid, tab);
15390 }
15391 }
15392}
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 15261 of file tablecmds.c.

15262{
15263 if (!get_index_isreplident(indoid))
15264 return;
15265
15266 if (tab->replicaIdentityIndex)
15267 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15268
15269 tab->replicaIdentityIndex = get_rel_name(indoid);
15270}
bool get_index_isreplident(Oid index_oid)
Definition: lsyscache.c:3722

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

15400{
15401 /*
15402 * This de-duplication check is critical for two independent reasons: we
15403 * mustn't try to recreate the same statistics object twice, and if the
15404 * statistics object depends on more than one column whose type is to be
15405 * altered, we must capture its definition string before applying any of
15406 * the type changes. ruleutils.c will get confused if we ask again later.
15407 */
15408 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15409 {
15410 /* OK, capture the statistics object's existing definition string */
15411 char *defstring = pg_get_statisticsobjdef_string(stxoid);
15412
15414 stxoid);
15416 defstring);
15417 }
15418}
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition: ruleutils.c:1626

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

19288{
19289 ListCell *l;
19290
19291 foreach(l, on_commits)
19292 {
19293 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19294
19295 if (oc->relid == relid)
19296 {
19298 break;
19299 }
19300 }
19301}

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

17941{
17942 Relation catalogRelation;
17943 SysScanDesc scan;
17944 ScanKeyData key[3];
17945 HeapTuple attributeTuple,
17946 constraintTuple;
17947 AttrMap *attmap;
17948 List *connames;
17949 List *nncolumns;
17950 bool found;
17951 bool is_partitioning;
17952
17953 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17954
17955 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17956 RelationGetRelid(parent_rel),
17957 expect_detached,
17958 RelationGetRelationName(child_rel));
17959 if (!found)
17960 {
17961 if (is_partitioning)
17962 ereport(ERROR,
17964 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17965 RelationGetRelationName(child_rel),
17966 RelationGetRelationName(parent_rel))));
17967 else
17968 ereport(ERROR,
17970 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17971 RelationGetRelationName(parent_rel),
17972 RelationGetRelationName(child_rel))));
17973 }
17974
17975 /*
17976 * Search through child columns looking for ones matching parent rel
17977 */
17978 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17979 ScanKeyInit(&key[0],
17980 Anum_pg_attribute_attrelid,
17981 BTEqualStrategyNumber, F_OIDEQ,
17983 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17984 true, NULL, 1, key);
17985 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17986 {
17987 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17988
17989 /* Ignore if dropped or not inherited */
17990 if (att->attisdropped)
17991 continue;
17992 if (att->attinhcount <= 0)
17993 continue;
17994
17996 NameStr(att->attname)))
17997 {
17998 /* Decrement inhcount and possibly set islocal to true */
17999 HeapTuple copyTuple = heap_copytuple(attributeTuple);
18000 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
18001
18002 copy_att->attinhcount--;
18003 if (copy_att->attinhcount == 0)
18004 copy_att->attislocal = true;
18005
18006 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
18007 heap_freetuple(copyTuple);
18008 }
18009 }
18010 systable_endscan(scan);
18011 table_close(catalogRelation, RowExclusiveLock);
18012
18013 /*
18014 * Likewise, find inherited check and not-null constraints and disinherit
18015 * them. To do this, we first need a list of the names of the parent's
18016 * check constraints. (We cheat a bit by only checking for name matches,
18017 * assuming that the expressions will match.)
18018 *
18019 * For NOT NULL columns, we store column numbers to match, mapping them in
18020 * to the child rel's attribute numbers.
18021 */
18022 attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
18023 RelationGetDescr(parent_rel),
18024 false);
18025
18026 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
18027 ScanKeyInit(&key[0],
18028 Anum_pg_constraint_conrelid,
18029 BTEqualStrategyNumber, F_OIDEQ,
18030 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
18031 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18032 true, NULL, 1, key);
18033
18034 connames = NIL;
18035 nncolumns = NIL;
18036
18037 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18038 {
18039 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18040
18041 if (con->connoinherit)
18042 continue;
18043
18044 if (con->contype == CONSTRAINT_CHECK)
18045 connames = lappend(connames, pstrdup(NameStr(con->conname)));
18046 if (con->contype == CONSTRAINT_NOTNULL)
18047 {
18048 AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
18049
18050 nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
18051 }
18052 }
18053
18054 systable_endscan(scan);
18055
18056 /* Now scan the child's constraints to find matches */
18057 ScanKeyInit(&key[0],
18058 Anum_pg_constraint_conrelid,
18059 BTEqualStrategyNumber, F_OIDEQ,
18061 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
18062 true, NULL, 1, key);
18063
18064 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
18065 {
18066 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
18067 bool match = false;
18068
18069 /*
18070 * Match CHECK constraints by name, not-null constraints by column
18071 * number, and ignore all others.
18072 */
18073 if (con->contype == CONSTRAINT_CHECK)
18074 {
18075 foreach_ptr(char, chkname, connames)
18076 {
18077 if (con->contype == CONSTRAINT_CHECK &&
18078 strcmp(NameStr(con->conname), chkname) == 0)
18079 {
18080 match = true;
18081 connames = foreach_delete_current(connames, chkname);
18082 break;
18083 }
18084 }
18085 }
18086 else if (con->contype == CONSTRAINT_NOTNULL)
18087 {
18088 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18089
18090 foreach_int(prevattno, nncolumns)
18091 {
18092 if (prevattno == child_attno)
18093 {
18094 match = true;
18095 nncolumns = foreach_delete_current(nncolumns, prevattno);
18096 break;
18097 }
18098 }
18099 }
18100 else
18101 continue;
18102
18103 if (match)
18104 {
18105 /* Decrement inhcount and possibly set islocal to true */
18106 HeapTuple copyTuple = heap_copytuple(constraintTuple);
18107 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18108
18109 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18110 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18111 RelationGetRelid(child_rel), NameStr(copy_con->conname));
18112
18113 copy_con->coninhcount--;
18114 if (copy_con->coninhcount == 0)
18115 copy_con->conislocal = true;
18116
18117 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
18118 heap_freetuple(copyTuple);
18119 }
18120 }
18121
18122 /* We should have matched all constraints */
18123 if (connames != NIL || nncolumns != NIL)
18124 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18125 list_length(connames) + list_length(nncolumns),
18126 RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18127
18128 systable_endscan(scan);
18129 table_close(catalogRelation, RowExclusiveLock);
18130
18132 RelationRelationId,
18133 RelationGetRelid(parent_rel),
18134 child_dependency_type(is_partitioning));
18135
18136 /*
18137 * Post alter hook of this inherits. Since object_access_hook doesn't take
18138 * multiple object identifiers, we relay oid of parent relation using
18139 * auxiliary_id argument.
18140 */
18141 InvokeObjectPostAlterHookArg(InheritsRelationId,
18142 RelationGetRelid(child_rel), 0,
18143 RelationGetRelid(parent_rel), false);
18144}
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:517
#define child_dependency_type(child_is_partition)
Definition: tablecmds.c:366

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

11915{
11916 ObjectAddresses *objs;
11917 HeapTuple consttup;
11919 SysScanDesc scan;
11920 HeapTuple trigtup;
11921
11923 Anum_pg_constraint_conrelid,
11924 BTEqualStrategyNumber, F_OIDEQ,
11925 ObjectIdGetDatum(conrelid));
11926
11927 scan = systable_beginscan(conrel,
11928 ConstraintRelidTypidNameIndexId,
11929 true, NULL, 1, &key);
11930 objs = new_object_addresses();
11931 while ((consttup = systable_getnext(scan)) != NULL)
11932 {
11933 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11934
11935 if (conform->conparentid != conoid)
11936 continue;
11937 else
11938 {
11939 ObjectAddress addr;
11940 SysScanDesc scan2;
11941 ScanKeyData key2;
11943
11944 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11945 add_exact_object_address(&addr, objs);
11946
11947 /*
11948 * First we must delete the dependency record that binds the
11949 * constraint records together.
11950 */
11951 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11952 conform->oid,
11954 ConstraintRelationId,
11955 conoid);
11956 Assert(n == 1); /* actually only one is expected */
11957
11958 /*
11959 * Now search for the triggers for this constraint and set them up
11960 * for deletion too
11961 */
11962 ScanKeyInit(&key2,
11963 Anum_pg_trigger_tgconstraint,
11964 BTEqualStrategyNumber, F_OIDEQ,
11965 ObjectIdGetDatum(conform->oid));
11966 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11967 true, NULL, 1, &key2);
11968 while ((trigtup = systable_getnext(scan2)) != NULL)
11969 {
11970 ObjectAddressSet(addr, TriggerRelationId,
11971 ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11972 add_exact_object_address(&addr, objs);
11973 }
11974 systable_endscan(scan2);
11975 }
11976 }
11977 /* make the dependency deletions visible */
11981 systable_endscan(scan);
11982}
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:228
long deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype, Oid refclassId, Oid refobjectId)
Definition: pg_depend.c:398

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

Referenced by AttachPartitionForeignKey().

◆ RemoveRelations()

void RemoveRelations ( DropStmt drop)

Definition at line 1528 of file tablecmds.c.

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

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

Referenced by ExecDropStmt().

◆ rename_constraint_internal()

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

Definition at line 4039 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Referenced by finish_heap_swap().

◆ set_attnotnull()

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

Definition at line 7832 of file tablecmds.c.

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

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

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

◆ SetIndexStorageProperties()

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

Definition at line 9122 of file tablecmds.c.

9127{
9128 ListCell *lc;
9129
9130 foreach(lc, RelationGetIndexList(rel))
9131 {
9132 Oid indexoid = lfirst_oid(lc);
9133 Relation indrel;
9134 AttrNumber indattnum = 0;
9135 HeapTuple tuple;
9136
9137 indrel = index_open(indexoid, lockmode);
9138
9139 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9140 {
9141 if (indrel->rd_index->indkey.values[i] == attnum)
9142 {
9143 indattnum = i + 1;
9144 break;
9145 }
9146 }
9147
9148 if (indattnum == 0)
9149 {
9150 index_close(indrel, lockmode);
9151 continue;
9152 }
9153
9154 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9155
9156 if (HeapTupleIsValid(tuple))
9157 {
9158 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9159
9160 if (setstorage)
9161 attrtuple->attstorage = newstorage;
9162
9163 if (setcompression)
9164 attrtuple->attcompression = newcompression;
9165
9166 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9167
9168 InvokeObjectPostAlterHook(RelationRelationId,
9169 RelationGetRelid(rel),
9170 attrtuple->attnum);
9171
9172 heap_freetuple(tuple);
9173 }
9174
9175 index_close(indrel, lockmode);
9176 }
9177}

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

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

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

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

◆ SetRelationTableSpace()

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

Definition at line 3742 of file tablecmds.c.

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

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

Referenced by MergeChildAttribute(), and MergeInheritedAttribute().

◆ StoreCatalogInheritance()

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

Definition at line 3513 of file tablecmds.c.

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

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

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

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

13321{
13322 ListCell *l;
13323 int attnum;
13324
13325 attnum = 0;
13326 foreach(l, colList)
13327 {
13328 char *attname = strVal(lfirst(l));
13329 HeapTuple atttuple;
13330 Form_pg_attribute attform;
13331
13332 atttuple = SearchSysCacheAttName(relId, attname);
13333 if (!HeapTupleIsValid(atttuple))
13334 ereport(ERROR,
13335 (errcode(ERRCODE_UNDEFINED_COLUMN),
13336 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13337 attname)));
13338 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13339 if (attform->attnum < 0)
13340 ereport(ERROR,
13341 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13342 errmsg("system columns cannot be used in foreign keys")));
13343 if (attnum >= INDEX_MAX_KEYS)
13344 ereport(ERROR,
13345 (errcode(ERRCODE_TOO_MANY_COLUMNS),
13346 errmsg("cannot have more than %d keys in a foreign key",
13347 INDEX_MAX_KEYS)));
13348 attnums[attnum] = attform->attnum;
13349 if (atttypids != NULL)
13350 atttypids[attnum] = attform->atttypid;
13351 if (attcollids != NULL)
13352 attcollids[attnum] = attform->attcollation;
13353 ReleaseSysCache(atttuple);
13354 attnum++;
13355 }
13356
13357 return attnum;
13358}

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

13481{
13482 Oid indexoid = InvalidOid;
13483 bool found = false;
13484 bool found_deferrable = false;
13485 List *indexoidlist;
13486 ListCell *indexoidscan;
13487 int i,
13488 j;
13489
13490 /*
13491 * Reject duplicate appearances of columns in the referenced-columns list.
13492 * Such a case is forbidden by the SQL standard, and even if we thought it
13493 * useful to allow it, there would be ambiguity about how to match the
13494 * list to unique indexes (in particular, it'd be unclear which index
13495 * opclass goes with which FK column).
13496 */
13497 for (i = 0; i < numattrs; i++)
13498 {
13499 for (j = i + 1; j < numattrs; j++)
13500 {
13501 if (attnums[i] == attnums[j])
13502 ereport(ERROR,
13503 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13504 errmsg("foreign key referenced-columns list must not contain duplicates")));
13505 }
13506 }
13507
13508 /*
13509 * Get the list of index OIDs for the table from the relcache, and look up
13510 * each one in the pg_index syscache, and match unique indexes to the list
13511 * of attnums we are given.
13512 */
13513 indexoidlist = RelationGetIndexList(pkrel);
13514
13515 foreach(indexoidscan, indexoidlist)
13516 {
13517 HeapTuple indexTuple;
13518 Form_pg_index indexStruct;
13519
13520 indexoid = lfirst_oid(indexoidscan);
13521 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13522 if (!HeapTupleIsValid(indexTuple))
13523 elog(ERROR, "cache lookup failed for index %u", indexoid);
13524 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13525
13526 /*
13527 * Must have the right number of columns; must be unique (or if
13528 * temporal then exclusion instead) and not a partial index; forget it
13529 * if there are any expressions, too. Invalid indexes are out as well.
13530 */
13531 if (indexStruct->indnkeyatts == numattrs &&
13532 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13533 indexStruct->indisvalid &&
13534 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13535 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13536 {
13537 Datum indclassDatum;
13538 oidvector *indclass;
13539
13540 /* Must get indclass the hard way */
13541 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13542 Anum_pg_index_indclass);
13543 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13544
13545 /*
13546 * The given attnum list may match the index columns in any order.
13547 * Check for a match, and extract the appropriate opclasses while
13548 * we're at it.
13549 *
13550 * We know that attnums[] is duplicate-free per the test at the
13551 * start of this function, and we checked above that the number of
13552 * index columns agrees, so if we find a match for each attnums[]
13553 * entry then we must have a one-to-one match in some order.
13554 */
13555 for (i = 0; i < numattrs; i++)
13556 {
13557 found = false;
13558 for (j = 0; j < numattrs; j++)
13559 {
13560 if (attnums[i] == indexStruct->indkey.values[j])
13561 {
13562 opclasses[i] = indclass->values[j];
13563 found = true;
13564 break;
13565 }
13566 }
13567 if (!found)
13568 break;
13569 }
13570 /* The last attribute in the index must be the PERIOD FK part */
13571 if (found && with_period)
13572 {
13573 int16 periodattnum = attnums[numattrs - 1];
13574
13575 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13576 }
13577
13578 /*
13579 * Refuse to use a deferrable unique/primary key. This is per SQL
13580 * spec, and there would be a lot of interesting semantic problems
13581 * if we tried to allow it.
13582 */
13583 if (found && !indexStruct->indimmediate)
13584 {
13585 /*
13586 * Remember that we found an otherwise matching index, so that
13587 * we can generate a more appropriate error message.
13588 */
13589 found_deferrable = true;
13590 found = false;
13591 }
13592
13593 /* We need to know whether the index has WITHOUT OVERLAPS */
13594 if (found)
13595 *pk_has_without_overlaps = indexStruct->indisexclusion;
13596 }
13597 ReleaseSysCache(indexTuple);
13598 if (found)
13599 break;
13600 }
13601
13602 if (!found)
13603 {
13604 if (found_deferrable)
13605 ereport(ERROR,
13606 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13607 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13608 RelationGetRelationName(pkrel))));
13609 else
13610 ereport(ERROR,
13611 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13612 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13613 RelationGetRelationName(pkrel))));
13614 }
13615
13616 list_free(indexoidlist);
13617
13618 return indexoid;
13619}
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:456
Definition: c.h:734
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:741

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

13378{
13379 List *indexoidlist;
13380 ListCell *indexoidscan;
13381 HeapTuple indexTuple = NULL;
13382 Form_pg_index indexStruct = NULL;
13383 Datum indclassDatum;
13384 oidvector *indclass;
13385 int i;
13386
13387 /*
13388 * Get the list of index OIDs for the table from the relcache, and look up
13389 * each one in the pg_index syscache until we find one marked primary key
13390 * (hopefully there isn't more than one such). Insist it's valid, too.
13391 */
13392 *indexOid = InvalidOid;
13393
13394 indexoidlist = RelationGetIndexList(pkrel);
13395
13396 foreach(indexoidscan, indexoidlist)
13397 {
13398 Oid indexoid = lfirst_oid(indexoidscan);
13399
13400 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13401 if (!HeapTupleIsValid(indexTuple))
13402 elog(ERROR, "cache lookup failed for index %u", indexoid);
13403 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13404 if (indexStruct->indisprimary && indexStruct->indisvalid)
13405 {
13406 /*
13407 * Refuse to use a deferrable primary key. This is per SQL spec,
13408 * and there would be a lot of interesting semantic problems if we
13409 * tried to allow it.
13410 */
13411 if (!indexStruct->indimmediate)
13412 ereport(ERROR,
13413 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13414 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13415 RelationGetRelationName(pkrel))));
13416
13417 *indexOid = indexoid;
13418 break;
13419 }
13420 ReleaseSysCache(indexTuple);
13421 }
13422
13423 list_free(indexoidlist);
13424
13425 /*
13426 * Check that we found it
13427 */
13428 if (!OidIsValid(*indexOid))
13429 ereport(ERROR,
13430 (errcode(ERRCODE_UNDEFINED_OBJECT),
13431 errmsg("there is no primary key for referenced table \"%s\"",
13432 RelationGetRelationName(pkrel))));
13433
13434 /* Must get indclass the hard way */
13435 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13436 Anum_pg_index_indclass);
13437 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13438
13439 /*
13440 * Now build the list of PK attributes from the indkey definition (we
13441 * assume a primary key cannot have expressional elements)
13442 */
13443 *attnamelist = NIL;
13444 for (i = 0; i < indexStruct->indnkeyatts; i++)
13445 {
13446 int pkattno = indexStruct->indkey.values[i];
13447
13448 attnums[i] = pkattno;
13449 atttypids[i] = attnumTypeId(pkrel, pkattno);
13450 attcollids[i] = attnumCollationId(pkrel, pkattno);
13451 opclasses[i] = indclass->values[i];
13452 *attnamelist = lappend(*attnamelist,
13453 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13454 }
13455
13456 *pk_has_without_overlaps = indexStruct->indisexclusion;
13457
13458 ReleaseSysCache(indexTuple);
13459
13460 return i;
13461}
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 19717 of file tablecmds.c.

19718{
19719 PartitionSpec *newspec;
19720 ParseState *pstate;
19721 ParseNamespaceItem *nsitem;
19722 ListCell *l;
19723
19724 newspec = makeNode(PartitionSpec);
19725
19726 newspec->strategy = partspec->strategy;
19727 newspec->partParams = NIL;
19728 newspec->location = partspec->location;
19729
19730 /* Check valid number of columns for strategy */
19731 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19732 list_length(partspec->partParams) != 1)
19733 ereport(ERROR,
19734 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19735 errmsg("cannot use \"list\" partition strategy with more than one column")));
19736
19737 /*
19738 * Create a dummy ParseState and insert the target relation as its sole
19739 * rangetable entry. We need a ParseState for transformExpr.
19740 */
19741 pstate = make_parsestate(NULL);
19742 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19743 NULL, false, true);
19744 addNSItemToQuery(pstate, nsitem, true, true, true);
19745
19746 /* take care of any partition expressions */
19747 foreach(l, partspec->partParams)
19748 {
19750
19751 if (pelem->expr)
19752 {
19753 /* Copy, to avoid scribbling on the input */
19754 pelem = copyObject(pelem);
19755
19756 /* Now do parse transformation of the expression */
19757 pelem->expr = transformExpr(pstate, pelem->expr,
19759
19760 /* we have to fix its collations too */
19761 assign_expr_collations(pstate, pelem->expr);
19762 }
19763
19764 newspec->partParams = lappend(newspec->partParams, pelem);
19765 }
19766
19767 return newspec;
19768}
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:119
@ EXPR_KIND_PARTITION_EXPRESSION
Definition: parse_node.h:80
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:900
List * partParams
Definition: parsenodes.h:914
ParseLoc location
Definition: parsenodes.h:915
PartitionStrategy strategy
Definition: parsenodes.h:913

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

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

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

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

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

Referenced by ExecuteTruncateGuts(), and RangeVarCallbackForTruncate().

◆ truncate_check_rel()

static void truncate_check_rel ( Oid  relid,
Form_pg_class  reltuple 
)
static

Definition at line 2362 of file tablecmds.c.

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

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

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

◆ tryAttachPartitionForeignKey()

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

Definition at line 11686 of file tablecmds.c.

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

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

Referenced by addFkRecurseReferencing(), and CloneFkReferencing().

◆ TryReuseForeignKey()

static void TryReuseForeignKey ( Oid  oldId,
Constraint con 
)
static

Definition at line 15907 of file tablecmds.c.

15908{
15909 HeapTuple tup;
15910 Datum adatum;
15911 ArrayType *arr;
15912 Oid *rawarr;
15913 int numkeys;
15914 int i;
15915
15916 Assert(con->contype == CONSTR_FOREIGN);
15917 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15918
15919 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15920 if (!HeapTupleIsValid(tup)) /* should not happen */
15921 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15922
15923 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15924 Anum_pg_constraint_conpfeqop);
15925 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15926 numkeys = ARR_DIMS(arr)[0];
15927 /* test follows the one in ri_FetchConstraintInfo() */
15928 if (ARR_NDIM(arr) != 1 ||
15929 ARR_HASNULL(arr) ||
15930 ARR_ELEMTYPE(arr) != OIDOID)
15931 elog(ERROR, "conpfeqop is not a 1-D Oid array");
15932 rawarr = (Oid *) ARR_DATA_PTR(arr);
15933
15934 /* stash a List of the operator Oids in our Constraint node */
15935 for (i = 0; i < numkeys; i++)
15936 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15937
15938 ReleaseSysCache(tup);
15939}
#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 15878 of file tablecmds.c.

15879{
15880 if (CheckIndexCompatible(oldId,
15881 stmt->accessMethod,
15882 stmt->indexParams,
15883 stmt->excludeOpNames,
15884 stmt->iswithoutoverlaps))
15885 {
15886 Relation irel = index_open(oldId, NoLock);
15887
15888 /* If it's a partitioned index, there is no storage to share. */
15889 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15890 {
15891 stmt->oldNumber = irel->rd_locator.relNumber;
15892 stmt->oldCreateSubid = irel->rd_createSubid;
15893 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15894 }
15895 index_close(irel, NoLock);
15896 }
15897}
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
Definition: indexcmds.c:177

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

Referenced by ATPostAlterTypeParse().

◆ validateFkOnDeleteSetColumns()

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

Definition at line 10638 of file tablecmds.c.

10641{
10642 int numcolsout = 0;
10643
10644 for (int i = 0; i < numfksetcols; i++)
10645 {
10646 int16 setcol_attnum = fksetcolsattnums[i];
10647 bool seen = false;
10648
10649 /* Make sure it's in fkattnums[] */
10650 for (int j = 0; j < numfks; j++)
10651 {
10652 if (fkattnums[j] == setcol_attnum)
10653 {
10654 seen = true;
10655 break;
10656 }
10657 }
10658
10659 if (!seen)
10660 {
10661 char *col = strVal(list_nth(fksetcols, i));
10662
10663 ereport(ERROR,
10664 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10665 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10666 }
10667
10668 /* Now check for dups */
10669 seen = false;
10670 for (int j = 0; j < numcolsout; j++)
10671 {
10672 if (fksetcolsattnums[j] == setcol_attnum)
10673 {
10674 seen = true;
10675 break;
10676 }
10677 }
10678 if (!seen)
10679 fksetcolsattnums[numcolsout++] = setcol_attnum;
10680 }
10681 return numcolsout;
10682}

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

13692{
13693 TupleTableSlot *slot;
13694 TableScanDesc scan;
13695 Trigger trig = {0};
13696 Snapshot snapshot;
13697 MemoryContext oldcxt;
13698 MemoryContext perTupCxt;
13699
13701 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13702
13703 /*
13704 * Build a trigger call structure; we'll need it either way.
13705 */
13706 trig.tgoid = InvalidOid;
13707 trig.tgname = conname;
13709 trig.tgisinternal = true;
13710 trig.tgconstrrelid = RelationGetRelid(pkrel);
13711 trig.tgconstrindid = pkindOid;
13712 trig.tgconstraint = constraintOid;
13713 trig.tgdeferrable = false;
13714 trig.tginitdeferred = false;
13715 /* we needn't fill in remaining fields */
13716
13717 /*
13718 * See if we can do it with a single LEFT JOIN query. A false result
13719 * indicates we must proceed with the fire-the-trigger method. We can't do
13720 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13721 * left joins.
13722 */
13723 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13724 return;
13725
13726 /*
13727 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13728 * if that tuple had just been inserted. If any of those fail, it should
13729 * ereport(ERROR) and that's that.
13730 */
13731 snapshot = RegisterSnapshot(GetLatestSnapshot());
13732 slot = table_slot_create(rel, NULL);
13733 scan = table_beginscan(rel, snapshot, 0, NULL);
13734
13736 "validateForeignKeyConstraint",
13738 oldcxt = MemoryContextSwitchTo(perTupCxt);
13739
13740 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13741 {
13742 LOCAL_FCINFO(fcinfo, 0);
13743 TriggerData trigdata = {0};
13744
13746
13747 /*
13748 * Make a call to the trigger function
13749 *
13750 * No parameters are passed, but we do set a context
13751 */
13752 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13753
13754 /*
13755 * We assume RI_FKey_check_ins won't look at flinfo...
13756 */
13757 trigdata.type = T_TriggerData;
13759 trigdata.tg_relation = rel;
13760 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13761 trigdata.tg_trigslot = slot;
13762 trigdata.tg_trigger = &trig;
13763
13764 fcinfo->context = (Node *) &trigdata;
13765
13766 RI_FKey_check_ins(fcinfo);
13767
13768 MemoryContextReset(perTupCxt);
13769 }
13770
13771 MemoryContextSwitchTo(oldcxt);
13772 MemoryContextDelete(perTupCxt);
13773 table_endscan(scan);
13774 UnregisterSnapshot(snapshot);
13776}
#define MemSet(start, val, len)
Definition: c.h:1022
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1833
#define SizeForFunctionCallInfo(nargs)
Definition: fmgr.h:102
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
Datum RI_FKey_check_ins(PG_FUNCTION_ARGS)
Definition: ri_triggers.c:472
bool RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1517
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 21754 of file tablecmds.c.

21755{
21756 Relation inheritsRel;
21757 SysScanDesc scan;
21759 int tuples = 0;
21760 HeapTuple inhTup;
21761 bool updated = false;
21762
21763 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21764
21765 /*
21766 * Scan pg_inherits for this parent index. Count each valid index we find
21767 * (verifying the pg_index entry for each), and if we reach the total
21768 * amount we expect, we can mark this parent index as valid.
21769 */
21770 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21771 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21772 BTEqualStrategyNumber, F_OIDEQ,
21774 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21775 NULL, 1, &key);
21776 while ((inhTup = systable_getnext(scan)) != NULL)
21777 {
21778 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21779 HeapTuple indTup;
21780 Form_pg_index indexForm;
21781
21782 indTup = SearchSysCache1(INDEXRELID,
21783 ObjectIdGetDatum(inhForm->inhrelid));
21784 if (!HeapTupleIsValid(indTup))
21785 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21786 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21787 if (indexForm->indisvalid)
21788 tuples += 1;
21789 ReleaseSysCache(indTup);
21790 }
21791
21792 /* Done with pg_inherits */
21793 systable_endscan(scan);
21794 table_close(inheritsRel, AccessShareLock);
21795
21796 /*
21797 * If we found as many inherited indexes as the partitioned table has
21798 * partitions, we're good; update pg_index to set indisvalid.
21799 */
21800 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21801 {
21802 Relation idxRel;
21803 HeapTuple indTup;
21804 Form_pg_index indexForm;
21805
21806 idxRel = table_open(IndexRelationId, RowExclusiveLock);
21807 indTup = SearchSysCacheCopy1(INDEXRELID,
21809 if (!HeapTupleIsValid(indTup))
21810 elog(ERROR, "cache lookup failed for index %u",
21811 RelationGetRelid(partedIdx));
21812 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21813
21814 indexForm->indisvalid = true;
21815 updated = true;
21816
21817 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21818
21820 heap_freetuple(indTup);
21821 }
21822
21823 /*
21824 * If this index is in turn a partition of a larger index, validating it
21825 * might cause the parent to become valid also. Try that.
21826 */
21827 if (updated && partedIdx->rd_rel->relispartition)
21828 {
21829 Oid parentIdxId,
21830 parentTblId;
21831 Relation parentIdx,
21832 parentTbl;
21833
21834 /* make sure we see the validation we just did */
21836
21837 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21838 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21839 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21840 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21841 Assert(!parentIdx->rd_index->indisvalid);
21842
21843 validatePartitionedIndex(parentIdx, parentTbl);
21844
21847 }
21848}

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

Referenced by ATExecAttachPartitionIdx(), and validatePartitionedIndex().

◆ verifyNotNullPKCompatible()

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

Definition at line 9570 of file tablecmds.c.

9571{
9573
9574 if (conForm->contype != CONSTRAINT_NOTNULL)
9575 elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9576
9577 /* a NO INHERIT constraint is no good */
9578 if (conForm->connoinherit)
9579 ereport(ERROR,
9580 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9581 errmsg("cannot create primary key on column \"%s\"", colname),
9582 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9583 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9584 NameStr(conForm->conname), colname,
9585 get_rel_name(conForm->conrelid), "NO INHERIT"),
9586 errhint("You might need to make the existing constraint inheritable using %s.",
9587 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9588
9589 /* an unvalidated constraint is no good */
9590 if (!conForm->convalidated)
9591 ereport(ERROR,
9592 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9593 errmsg("cannot create primary key on column \"%s\"", colname),
9594 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9595 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9596 NameStr(conForm->conname), colname,
9597 get_rel_name(conForm->conrelid), "NOT VALID"),
9598 errhint("You might need to validate it using %s.",
9599 "ALTER TABLE ... VALIDATE CONSTRAINT"));
9600}

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

Referenced by ATPrepAddPrimaryKey().

◆ verifyPartitionIndexNotNull()

static void verifyPartitionIndexNotNull ( IndexInfo iinfo,
Relation  partition 
)
static

Definition at line 21856 of file tablecmds.c.

21857{
21858 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21859 {
21861 iinfo->ii_IndexAttrNumbers[i] - 1);
21862
21863 if (!att->attnotnull)
21864 ereport(ERROR,
21865 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21866 errmsg("invalid primary key definition"),
21867 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21868 NameStr(att->attname),
21869 RelationGetRelationName(partition)));
21870 }
21871}
int ii_NumIndexKeyAttrs
Definition: execnodes.h:169
AttrNumber ii_IndexAttrNumbers[INDEX_MAX_KEYS]
Definition: execnodes.h:175

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

Referenced by DropErrorMsgNonExistent(), and DropErrorMsgWrongType().

◆ on_commits