Thanks to visit codestin.com
Credit goes to doxygen.postgresql.org

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 DetachAddConstraintIfNeeded (List **wqueue, Relation partRel)
 
static void DropClonedTriggersFromPartition (Oid partitionId)
 
static ObjectAddress ATExecDetachPartition (List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
 
static void DetachPartitionFinalize (Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
 
static ObjectAddress ATExecDetachPartitionFinalize (Relation rel, RangeVar *name)
 
static ObjectAddress ATExecAttachPartitionIdx (List **wqueue, Relation parentIdx, RangeVar *name)
 
static void validatePartitionedIndex (Relation partedIdx, Relation partedTbl)
 
static void refuseDupeIndexAttach (Relation parentIdx, Relation partIdx, Relation partitionTbl)
 
static void verifyPartitionIndexNotNull (IndexInfo *iinfo, Relation partition)
 
static ListGetParentedForeignKeyRefs (Relation partition)
 
static void ATDetachCheckNoForeignKeyRefs (Relation partition)
 
static char GetAttributeCompression (Oid atttypid, const char *compression)
 
static char GetAttributeStorage (Oid atttypid, const char *storagemode)
 
ObjectAddress DefineRelation (CreateStmt *stmt, char relkind, Oid ownerId, ObjectAddress *typaddress, const char *queryString)
 
TupleDesc BuildDescForRelation (const List *columns)
 
static void DropErrorMsgNonExistent (RangeVar *rel, char rightkind, bool missing_ok)
 
static void DropErrorMsgWrongType (const char *relname, char wrongkind, char rightkind)
 
void RemoveRelations (DropStmt *drop)
 
void ExecuteTruncate (TruncateStmt *stmt)
 
void ExecuteTruncateGuts (List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
 
void SetRelationHasSubclass (Oid relationId, bool relhassubclass)
 
bool CheckRelationTableSpaceMove (Relation rel, Oid newTableSpaceId)
 
void SetRelationTableSpace (Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
 
static void renameatt_check (Oid myrelid, Form_pg_class classform, bool recursing)
 
static AttrNumber renameatt_internal (Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
 
static void RangeVarCallbackForRenameAttribute (const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
 
ObjectAddress renameatt (RenameStmt *stmt)
 
static ObjectAddress rename_constraint_internal (Oid myrelid, Oid mytypid, const char *oldconname, const char *newconname, bool recurse, bool recursing, int expected_parents)
 
ObjectAddress RenameConstraint (RenameStmt *stmt)
 
ObjectAddress RenameRelation (RenameStmt *stmt)
 
void RenameRelationInternal (Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
 
void ResetRelRewrite (Oid myrelid)
 
void CheckTableNotInUse (Relation rel, const char *stmt)
 
Oid AlterTableLookupRelation (AlterTableStmt *stmt, LOCKMODE lockmode)
 
void AlterTable (AlterTableStmt *stmt, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
void AlterTableInternal (Oid relid, List *cmds, bool recurse)
 
LOCKMODE AlterTableGetLockLevel (List *cmds)
 
static const char * alter_table_type_to_string (AlterTableType cmdtype)
 
void find_composite_type_dependencies (Oid typeOid, Relation origRelation, const char *origTypeName)
 
void check_of_type (HeapTuple typetuple)
 
static void SetIndexStorageProperties (Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
 
static Oid CreateFKCheckTrigger (Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
 
static void RememberReplicaIdentityForRebuilding (Oid indoid, AlteredTableInfo *tab)
 
static void RememberClusterOnForRebuilding (Oid indoid, AlteredTableInfo *tab)
 
void ATExecChangeOwner (Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
 
Oid AlterTableMoveAll (AlterTableMoveAllStmt *stmt)
 
static char * decompile_conbin (HeapTuple contup, TupleDesc tupdesc)
 
static bool constraints_equivalent (HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
 
static void MarkInheritDetached (Relation child_rel, Relation parent_rel)
 
static void relation_mark_replica_identity (Relation rel, char ri_type, Oid indexOid, bool is_internal)
 
ObjectAddress AlterTableNamespace (AlterObjectSchemaStmt *stmt, Oid *oldschema)
 
void AlterTableNamespaceInternal (Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
 
void AlterRelationNamespaceInternal (Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
 
void register_on_commit_action (Oid relid, OnCommitAction action)
 
void remove_on_commit_action (Oid relid)
 
void PreCommit_on_commit_actions (void)
 
void AtEOXact_on_commit_actions (bool isCommit)
 
void AtEOSubXact_on_commit_actions (bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid)
 
void RangeVarCallbackMaintainsTable (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
void RangeVarCallbackOwnsRelation (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
bool PartConstraintImpliedByRelConstraint (Relation scanrel, List *partConstraint)
 
static void RangeVarCallbackForAttachIndex (const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
 

Variables

static Liston_commits = NIL
 
static const struct dropmsgstrings dropmsgstringarray []
 

Macro Definition Documentation

◆ AT_NUM_PASSES

#define AT_NUM_PASSES   (AT_PASS_MISC + 1)

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

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

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

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

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

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

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

◆ addFkRecurseReferenced()

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

Definition at line 10868 of file tablecmds.c.

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

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

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

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

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

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

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

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

12739{
12740 Form_pg_constraint currcon;
12741 Oid conoid;
12742 ScanKeyData pkey;
12743 SysScanDesc pscan;
12744 HeapTuple childtup;
12745
12746 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12747 conoid = currcon->oid;
12748
12749 ScanKeyInit(&pkey,
12750 Anum_pg_constraint_conparentid,
12751 BTEqualStrategyNumber, F_OIDEQ,
12752 ObjectIdGetDatum(conoid));
12753
12754 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12755 true, NULL, 1, &pkey);
12756
12757 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12758 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12759 pkrelid, childtup, lockmode,
12760 ReferencedParentDelTrigger,
12761 ReferencedParentUpdTrigger,
12762 ReferencingParentInsTrigger,
12763 ReferencingParentUpdTrigger);
12764
12765 systable_endscan(pscan);
12766}
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:12380

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

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

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

Referenced by ATExecAlterConstrDeferrability().

◆ AlterConstrUpdateConstraintEntry()

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

Definition at line 12822 of file tablecmds.c.

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

19099{
19100 List *indexList;
19101 ListCell *l;
19102
19103 indexList = RelationGetIndexList(rel);
19104
19105 foreach(l, indexList)
19106 {
19107 Oid indexOid = lfirst_oid(l);
19108 ObjectAddress thisobj;
19109
19110 thisobj.classId = RelationRelationId;
19111 thisobj.objectId = indexOid;
19112 thisobj.objectSubId = 0;
19113
19114 /*
19115 * Note: currently, the index will not have its own dependency on the
19116 * namespace, so we don't need to do changeDependencyFor(). There's no
19117 * row type in pg_type, either.
19118 *
19119 * XXX this objsMoved test may be pointless -- surely we have a single
19120 * dependency link from a relation to each index?
19121 */
19122 if (!object_address_present(&thisobj, objsMoved))
19123 {
19124 AlterRelationNamespaceInternal(classRel, indexOid,
19125 oldNspOid, newNspOid,
19126 false, objsMoved);
19127 add_exact_object_address(&thisobj, objsMoved);
19128 }
19129 }
19130
19131 list_free(indexList);
19132}
bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs)
Definition: dependency.c:2619
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2559
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:19020

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

19024{
19025 HeapTuple classTup;
19026 Form_pg_class classForm;
19027 ObjectAddress thisobj;
19028 bool already_done = false;
19029
19030 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19031 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19032 if (!HeapTupleIsValid(classTup))
19033 elog(ERROR, "cache lookup failed for relation %u", relOid);
19034 classForm = (Form_pg_class) GETSTRUCT(classTup);
19035
19036 Assert(classForm->relnamespace == oldNspOid);
19037
19038 thisobj.classId = RelationRelationId;
19039 thisobj.objectId = relOid;
19040 thisobj.objectSubId = 0;
19041
19042 /*
19043 * If the object has already been moved, don't move it again. If it's
19044 * already in the right place, don't move it, but still fire the object
19045 * access hook.
19046 */
19047 already_done = object_address_present(&thisobj, objsMoved);
19048 if (!already_done && oldNspOid != newNspOid)
19049 {
19050 ItemPointerData otid = classTup->t_self;
19051
19052 /* check for duplicate name (more friendly than unique-index failure) */
19053 if (get_relname_relid(NameStr(classForm->relname),
19054 newNspOid) != InvalidOid)
19055 ereport(ERROR,
19056 (errcode(ERRCODE_DUPLICATE_TABLE),
19057 errmsg("relation \"%s\" already exists in schema \"%s\"",
19058 NameStr(classForm->relname),
19059 get_namespace_name(newNspOid))));
19060
19061 /* classTup is a copy, so OK to scribble on */
19062 classForm->relnamespace = newNspOid;
19063
19064 CatalogTupleUpdate(classRel, &otid, classTup);
19065 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19066
19067
19068 /* Update dependency on schema if caller said so */
19069 if (hasDependEntry &&
19070 changeDependencyFor(RelationRelationId,
19071 relOid,
19072 NamespaceRelationId,
19073 oldNspOid,
19074 newNspOid) != 1)
19075 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19076 NameStr(classForm->relname));
19077 }
19078 else
19079 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19080 if (!already_done)
19081 {
19082 add_exact_object_address(&thisobj, objsMoved);
19083
19084 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19085 }
19086
19087 heap_freetuple(classTup);
19088}
#define NameStr(name)
Definition: c.h:752
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 19142 of file tablecmds.c.

19145{
19146 Relation depRel;
19147 SysScanDesc scan;
19148 ScanKeyData key[2];
19149 HeapTuple tup;
19150
19151 /*
19152 * SERIAL sequences are those having an auto dependency on one of the
19153 * table's columns (we don't care *which* column, exactly).
19154 */
19155 depRel = table_open(DependRelationId, AccessShareLock);
19156
19157 ScanKeyInit(&key[0],
19158 Anum_pg_depend_refclassid,
19159 BTEqualStrategyNumber, F_OIDEQ,
19160 ObjectIdGetDatum(RelationRelationId));
19161 ScanKeyInit(&key[1],
19162 Anum_pg_depend_refobjid,
19163 BTEqualStrategyNumber, F_OIDEQ,
19165 /* we leave refobjsubid unspecified */
19166
19167 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19168 NULL, 2, key);
19169
19170 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19171 {
19172 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19173 Relation seqRel;
19174
19175 /* skip dependencies other than auto dependencies on columns */
19176 if (depForm->refobjsubid == 0 ||
19177 depForm->classid != RelationRelationId ||
19178 depForm->objsubid != 0 ||
19179 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19180 continue;
19181
19182 /* Use relation_open just in case it's an index */
19183 seqRel = relation_open(depForm->objid, lockmode);
19184
19185 /* skip non-sequence relations */
19186 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19187 {
19188 /* No need to keep the lock */
19189 relation_close(seqRel, lockmode);
19190 continue;
19191 }
19192
19193 /* Fix the pg_class and pg_depend entries */
19194 AlterRelationNamespaceInternal(classRel, depForm->objid,
19195 oldNspOid, newNspOid,
19196 true, objsMoved);
19197
19198 /*
19199 * Sequences used to have entries in pg_type, but no longer do. If we
19200 * ever re-instate that, we'll need to move the pg_type entry to the
19201 * new namespace, too (using AlterTypeNamespaceInternal).
19202 */
19203 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19204
19205 /* Now we can close it. Keep the lock till end of transaction. */
19206 relation_close(seqRel, NoLock);
19207 }
19208
19209 systable_endscan(scan);
19210
19212}
@ 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:508
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 4527 of file tablecmds.c.

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

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

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4601 of file tablecmds.c.

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

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

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

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4468 of file tablecmds.c.

4469{
4470 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4471 stmt->missing_ok ? RVR_MISSING_OK : 0,
4473 stmt);
4474}
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:19552

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

Referenced by ProcessUtilitySlow().

◆ AlterTableMoveAll()

Oid AlterTableMoveAll ( AlterTableMoveAllStmt stmt)

Definition at line 16953 of file tablecmds.c.

16954{
16955 List *relations = NIL;
16956 ListCell *l;
16957 ScanKeyData key[1];
16958 Relation rel;
16959 TableScanDesc scan;
16960 HeapTuple tuple;
16961 Oid orig_tablespaceoid;
16962 Oid new_tablespaceoid;
16963 List *role_oids = roleSpecsToIds(stmt->roles);
16964
16965 /* Ensure we were not asked to move something we can't */
16966 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16967 stmt->objtype != OBJECT_MATVIEW)
16968 ereport(ERROR,
16969 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16970 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16971
16972 /* Get the orig and new tablespace OIDs */
16973 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16974 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16975
16976 /* Can't move shared relations in to or out of pg_global */
16977 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16978 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16979 new_tablespaceoid == GLOBALTABLESPACE_OID)
16980 ereport(ERROR,
16981 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16982 errmsg("cannot move relations in to or out of pg_global tablespace")));
16983
16984 /*
16985 * Must have CREATE rights on the new tablespace, unless it is the
16986 * database default tablespace (which all users implicitly have CREATE
16987 * rights on).
16988 */
16989 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16990 {
16991 AclResult aclresult;
16992
16993 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16994 ACL_CREATE);
16995 if (aclresult != ACLCHECK_OK)
16997 get_tablespace_name(new_tablespaceoid));
16998 }
16999
17000 /*
17001 * Now that the checks are done, check if we should set either to
17002 * InvalidOid because it is our database's default tablespace.
17003 */
17004 if (orig_tablespaceoid == MyDatabaseTableSpace)
17005 orig_tablespaceoid = InvalidOid;
17006
17007 if (new_tablespaceoid == MyDatabaseTableSpace)
17008 new_tablespaceoid = InvalidOid;
17009
17010 /* no-op */
17011 if (orig_tablespaceoid == new_tablespaceoid)
17012 return new_tablespaceoid;
17013
17014 /*
17015 * Walk the list of objects in the tablespace and move them. This will
17016 * only find objects in our database, of course.
17017 */
17018 ScanKeyInit(&key[0],
17019 Anum_pg_class_reltablespace,
17020 BTEqualStrategyNumber, F_OIDEQ,
17021 ObjectIdGetDatum(orig_tablespaceoid));
17022
17023 rel = table_open(RelationRelationId, AccessShareLock);
17024 scan = table_beginscan_catalog(rel, 1, key);
17025 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17026 {
17027 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17028 Oid relOid = relForm->oid;
17029
17030 /*
17031 * Do not move objects in pg_catalog as part of this, if an admin
17032 * really wishes to do so, they can issue the individual ALTER
17033 * commands directly.
17034 *
17035 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17036 * (TOAST will be moved with the main table).
17037 */
17038 if (IsCatalogNamespace(relForm->relnamespace) ||
17039 relForm->relisshared ||
17040 isAnyTempNamespace(relForm->relnamespace) ||
17041 IsToastNamespace(relForm->relnamespace))
17042 continue;
17043
17044 /* Only move the object type requested */
17045 if ((stmt->objtype == OBJECT_TABLE &&
17046 relForm->relkind != RELKIND_RELATION &&
17047 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17048 (stmt->objtype == OBJECT_INDEX &&
17049 relForm->relkind != RELKIND_INDEX &&
17050 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17051 (stmt->objtype == OBJECT_MATVIEW &&
17052 relForm->relkind != RELKIND_MATVIEW))
17053 continue;
17054
17055 /* Check if we are only moving objects owned by certain roles */
17056 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17057 continue;
17058
17059 /*
17060 * Handle permissions-checking here since we are locking the tables
17061 * and also to avoid doing a bunch of work only to fail part-way. Note
17062 * that permissions will also be checked by AlterTableInternal().
17063 *
17064 * Caller must be considered an owner on the table to move it.
17065 */
17066 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17068 NameStr(relForm->relname));
17069
17070 if (stmt->nowait &&
17072 ereport(ERROR,
17073 (errcode(ERRCODE_OBJECT_IN_USE),
17074 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17075 get_namespace_name(relForm->relnamespace),
17076 NameStr(relForm->relname))));
17077 else
17079
17080 /* Add to our list of objects to move */
17081 relations = lappend_oid(relations, relOid);
17082 }
17083
17084 table_endscan(scan);
17086
17087 if (relations == NIL)
17089 (errcode(ERRCODE_NO_DATA_FOUND),
17090 errmsg("no matching relations in tablespace \"%s\" found",
17091 orig_tablespaceoid == InvalidOid ? "(database default)" :
17092 get_tablespace_name(orig_tablespaceoid))));
17093
17094 /* Everything is locked, loop through and move all of the relations. */
17095 foreach(l, relations)
17096 {
17097 List *cmds = NIL;
17099
17101 cmd->name = stmt->new_tablespacename;
17102
17103 cmds = lappend(cmds, cmd);
17104
17106 /* OID is set by AlterTableInternal */
17107 AlterTableInternal(lfirst_oid(l), cmds, false);
17109 }
17110
17111 return new_tablespaceoid;
17112}
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:1346
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:2347
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2366
@ OBJECT_INDEX
Definition: parsenodes.h:2344
@ OBJECT_TABLE
Definition: parsenodes.h:2365
#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:4556
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 18912 of file tablecmds.c.

18913{
18914 Relation rel;
18915 Oid relid;
18916 Oid oldNspOid;
18917 Oid nspOid;
18918 RangeVar *newrv;
18919 ObjectAddresses *objsMoved;
18920 ObjectAddress myself;
18921
18923 stmt->missing_ok ? RVR_MISSING_OK : 0,
18925 stmt);
18926
18927 if (!OidIsValid(relid))
18928 {
18930 (errmsg("relation \"%s\" does not exist, skipping",
18931 stmt->relation->relname)));
18932 return InvalidObjectAddress;
18933 }
18934
18935 rel = relation_open(relid, NoLock);
18936
18937 oldNspOid = RelationGetNamespace(rel);
18938
18939 /* If it's an owned sequence, disallow moving it by itself. */
18940 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18941 {
18942 Oid tableId;
18943 int32 colId;
18944
18945 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18946 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18947 ereport(ERROR,
18948 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18949 errmsg("cannot move an owned sequence into another schema"),
18950 errdetail("Sequence \"%s\" is linked to table \"%s\".",
18952 get_rel_name(tableId))));
18953 }
18954
18955 /* Get and lock schema OID and check its permissions. */
18956 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18957 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18958
18959 /* common checks on switching namespaces */
18960 CheckSetNamespace(oldNspOid, nspOid);
18961
18962 objsMoved = new_object_addresses();
18963 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18964 free_object_addresses(objsMoved);
18965
18966 ObjectAddressSet(myself, RelationRelationId, relid);
18967
18968 if (oldschema)
18969 *oldschema = oldNspOid;
18970
18971 /* close rel, but keep lock until commit */
18972 relation_close(rel, NoLock);
18973
18974 return myself;
18975}
int32_t int32
Definition: c.h:535
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2513
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2799
int errdetail(const char *fmt,...)
Definition: elog.c:1207
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:18983

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

18985{
18986 Relation classRel;
18987
18988 Assert(objsMoved != NULL);
18989
18990 /* OK, modify the pg_class row and pg_depend entry */
18991 classRel = table_open(RelationRelationId, RowExclusiveLock);
18992
18993 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18994 nspOid, true, objsMoved);
18995
18996 /* Fix the table's row type too, if it has one */
18997 if (OidIsValid(rel->rd_rel->reltype))
18998 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18999 false, /* isImplicitArray */
19000 false, /* ignoreDependent */
19001 false, /* errorOnTableType */
19002 objsMoved);
19003
19004 /* Fix other dependent stuff */
19005 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
19006 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
19007 objsMoved, AccessExclusiveLock);
19008 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
19009 false, objsMoved);
19010
19011 table_close(classRel, RowExclusiveLock);
19012}
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:19142
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19097
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition: typecmds.c:4165

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

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

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

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

◆ ATAddForeignKeyConstraint()

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

Definition at line 10035 of file tablecmds.c.

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

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

14648{
14649 Assert(expr != NULL);
14650
14651 for (;;)
14652 {
14653 /* only one varno, so no need to check that */
14654 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14655 return false;
14656 else if (IsA(expr, RelabelType))
14657 expr = (Node *) ((RelabelType *) expr)->arg;
14658 else if (IsA(expr, CoerceToDomain))
14659 {
14660 CoerceToDomain *d = (CoerceToDomain *) expr;
14661
14663 return true;
14664 expr = (Node *) d->arg;
14665 }
14666 else if (IsA(expr, FuncExpr))
14667 {
14668 FuncExpr *f = (FuncExpr *) expr;
14669
14670 switch (f->funcid)
14671 {
14672 case F_TIMESTAMPTZ_TIMESTAMP:
14673 case F_TIMESTAMP_TIMESTAMPTZ:
14675 return true;
14676 else
14677 expr = linitial(f->args);
14678 break;
14679 default:
14680 return true;
14681 }
14682 }
14683 else
14684 return true;
14685 }
14686}
bool TimestampTimestampTzRequiresRewrite(void)
Definition: timestamp.c:6414
void * arg
#define linitial(l)
Definition: pg_list.h:178
Oid funcid
Definition: primnodes.h:769
List * args
Definition: primnodes.h:787
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 4863 of file tablecmds.c.

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

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

21947{
21948 List *constraints;
21949 ListCell *cell;
21950
21951 constraints = GetParentedForeignKeyRefs(partition);
21952
21953 foreach(cell, constraints)
21954 {
21955 Oid constrOid = lfirst_oid(cell);
21956 HeapTuple tuple;
21957 Form_pg_constraint constrForm;
21958 Relation rel;
21959 Trigger trig = {0};
21960
21961 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21962 if (!HeapTupleIsValid(tuple))
21963 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21964 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21965
21966 Assert(OidIsValid(constrForm->conparentid));
21967 Assert(constrForm->confrelid == RelationGetRelid(partition));
21968
21969 /* prevent data changes into the referencing table until commit */
21970 rel = table_open(constrForm->conrelid, ShareLock);
21971
21972 trig.tgoid = InvalidOid;
21973 trig.tgname = NameStr(constrForm->conname);
21975 trig.tgisinternal = true;
21976 trig.tgconstrrelid = RelationGetRelid(partition);
21977 trig.tgconstrindid = constrForm->conindid;
21978 trig.tgconstraint = constrForm->oid;
21979 trig.tgdeferrable = false;
21980 trig.tginitdeferred = false;
21981 /* we needn't fill in remaining fields */
21982
21983 RI_PartitionRemove_Check(&trig, rel, partition);
21984
21985 ReleaseSysCache(tuple);
21986
21987 table_close(rel, NoLock);
21988 }
21989}
#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:21893
#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 19425 of file tablecmds.c.

19427{
19428 ListCell *cur_item;
19429
19430 foreach(cur_item, on_commits)
19431 {
19432 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19433
19434 if (!isCommit && oc->creating_subid == mySubid)
19435 {
19436 /* cur_item must be removed */
19438 pfree(oc);
19439 }
19440 else
19441 {
19442 /* cur_item must be preserved */
19443 if (oc->creating_subid == mySubid)
19444 oc->creating_subid = parentSubid;
19445 if (oc->deleting_subid == mySubid)
19446 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19447 }
19448 }
19449}
#define InvalidSubTransactionId
Definition: c.h:664
#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 19393 of file tablecmds.c.

19394{
19395 ListCell *cur_item;
19396
19397 foreach(cur_item, on_commits)
19398 {
19399 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19400
19401 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19403 {
19404 /* cur_item must be removed */
19406 pfree(oc);
19407 }
19408 else
19409 {
19410 /* cur_item must be preserved */
19413 }
19414 }
19415}

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

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

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

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

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

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

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

Referenced by ATExecAddIdentity(), and ATExecCmd().

◆ ATExecAddIndex()

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

Definition at line 9588 of file tablecmds.c.

9590{
9591 bool check_rights;
9592 bool skip_build;
9593 bool quiet;
9594 ObjectAddress address;
9595
9597 Assert(!stmt->concurrent);
9598
9599 /* The IndexStmt has already been through transformIndexStmt */
9600 Assert(stmt->transformed);
9601
9602 /* suppress schema rights check when rebuilding existing index */
9603 check_rights = !is_rebuild;
9604 /* skip index build if phase 3 will do it or we're reusing an old one */
9605 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9606 /* suppress notices when rebuilding existing index */
9607 quiet = is_rebuild;
9608
9609 address = DefineIndex(RelationGetRelid(rel),
9610 stmt,
9611 InvalidOid, /* no predefined OID */
9612 InvalidOid, /* no parent index */
9613 InvalidOid, /* no parent constraint */
9614 -1, /* total_parts unknown */
9615 true, /* is_alter_table */
9616 check_rights,
9617 false, /* check_not_in_use - we did it already */
9618 skip_build,
9619 quiet);
9620
9621 /*
9622 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9623 * new index instead of building from scratch. Restore associated fields.
9624 * This may store InvalidSubTransactionId in both fields, in which case
9625 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9626 * this after the CCI that made catalog rows visible to any rebuild. The
9627 * DROP of the old edition of this index will have scheduled the storage
9628 * for deletion at commit, so cancel that pending deletion.
9629 */
9630 if (RelFileNumberIsValid(stmt->oldNumber))
9631 {
9632 Relation irel = index_open(address.objectId, NoLock);
9633
9634 irel->rd_createSubid = stmt->oldCreateSubid;
9635 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9637 index_close(irel, NoLock);
9638 }
9639
9640 return address;
9641}
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 9672 of file tablecmds.c.

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

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

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

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

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

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

Referenced by ATExecCmd().

◆ ATExecAlterColumnGenericOptions()

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

Definition at line 15923 of file tablecmds.c.

15927{
15928 Relation ftrel;
15929 Relation attrel;
15930 ForeignServer *server;
15931 ForeignDataWrapper *fdw;
15932 HeapTuple tuple;
15933 HeapTuple newtuple;
15934 bool isnull;
15935 Datum repl_val[Natts_pg_attribute];
15936 bool repl_null[Natts_pg_attribute];
15937 bool repl_repl[Natts_pg_attribute];
15938 Datum datum;
15939 Form_pg_foreign_table fttableform;
15940 Form_pg_attribute atttableform;
15942 ObjectAddress address;
15943
15944 if (options == NIL)
15945 return InvalidObjectAddress;
15946
15947 /* First, determine FDW validator associated to the foreign table. */
15948 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15949 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15950 if (!HeapTupleIsValid(tuple))
15951 ereport(ERROR,
15952 (errcode(ERRCODE_UNDEFINED_OBJECT),
15953 errmsg("foreign table \"%s\" does not exist",
15955 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15956 server = GetForeignServer(fttableform->ftserver);
15957 fdw = GetForeignDataWrapper(server->fdwid);
15958
15960 ReleaseSysCache(tuple);
15961
15962 attrel = table_open(AttributeRelationId, RowExclusiveLock);
15963 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15964 if (!HeapTupleIsValid(tuple))
15965 ereport(ERROR,
15966 (errcode(ERRCODE_UNDEFINED_COLUMN),
15967 errmsg("column \"%s\" of relation \"%s\" does not exist",
15968 colName, RelationGetRelationName(rel))));
15969
15970 /* Prevent them from altering a system attribute */
15971 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15972 attnum = atttableform->attnum;
15973 if (attnum <= 0)
15974 ereport(ERROR,
15975 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15976 errmsg("cannot alter system column \"%s\"", colName)));
15977
15978
15979 /* Initialize buffers for new tuple values */
15980 memset(repl_val, 0, sizeof(repl_val));
15981 memset(repl_null, false, sizeof(repl_null));
15982 memset(repl_repl, false, sizeof(repl_repl));
15983
15984 /* Extract the current options */
15985 datum = SysCacheGetAttr(ATTNAME,
15986 tuple,
15987 Anum_pg_attribute_attfdwoptions,
15988 &isnull);
15989 if (isnull)
15990 datum = PointerGetDatum(NULL);
15991
15992 /* Transform the options */
15993 datum = transformGenericOptions(AttributeRelationId,
15994 datum,
15995 options,
15996 fdw->fdwvalidator);
15997
15998 if (DatumGetPointer(datum) != NULL)
15999 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
16000 else
16001 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
16002
16003 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
16004
16005 /* Everything looks good - update the tuple */
16006
16007 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16008 repl_val, repl_null, repl_repl);
16009
16010 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16011
16012 InvokeObjectPostAlterHook(RelationRelationId,
16013 RelationGetRelid(rel),
16014 atttableform->attnum);
16015 ObjectAddressSubSet(address, RelationRelationId,
16016 RelationGetRelid(rel), attnum);
16017
16018 ReleaseSysCache(tuple);
16019
16021
16022 heap_freetuple(newtuple);
16023
16024 return address;
16025}
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 14694 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

16416{
16417 Oid indexOid;
16418 ObjectAddress address;
16419
16420 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16421
16422 if (!OidIsValid(indexOid))
16423 ereport(ERROR,
16424 (errcode(ERRCODE_UNDEFINED_OBJECT),
16425 errmsg("index \"%s\" for table \"%s\" does not exist",
16426 indexName, RelationGetRelationName(rel))));
16427
16428 /* Check index is valid to cluster on */
16429 check_index_is_clusterable(rel, indexOid, lockmode);
16430
16431 /* And do the work */
16432 mark_index_clustered(rel, indexOid, false);
16433
16434 ObjectAddressSet(address,
16435 RelationRelationId, indexOid);
16436
16437 return address;
16438}
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 5369 of file tablecmds.c.

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

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

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

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

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

20868{
20869 Relation partRel;
20870 ObjectAddress address;
20871 Oid defaultPartOid;
20872
20873 /*
20874 * We must lock the default partition, because detaching this partition
20875 * will change its partition constraint.
20876 */
20877 defaultPartOid =
20879 if (OidIsValid(defaultPartOid))
20880 {
20881 /*
20882 * Concurrent detaching when a default partition exists is not
20883 * supported. The main problem is that the default partition
20884 * constraint would change. And there's a definitional problem: what
20885 * should happen to the tuples that are being inserted that belong to
20886 * the partition being detached? Putting them on the partition being
20887 * detached would be wrong, since they'd become "lost" after the
20888 * detaching completes but we cannot put them in the default partition
20889 * either until we alter its partition constraint.
20890 *
20891 * I think we could solve this problem if we effected the constraint
20892 * change before committing the first transaction. But the lock would
20893 * have to remain AEL and it would cause concurrent query planning to
20894 * be blocked, so changing it that way would be even worse.
20895 */
20896 if (concurrent)
20897 ereport(ERROR,
20898 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20899 errmsg("cannot detach partitions concurrently when a default partition exists")));
20900 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20901 }
20902
20903 /*
20904 * In concurrent mode, the partition is locked with share-update-exclusive
20905 * in the first transaction. This allows concurrent transactions to be
20906 * doing DML to the partition.
20907 */
20908 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20910
20911 /*
20912 * Check inheritance conditions and either delete the pg_inherits row (in
20913 * non-concurrent mode) or just set the inhdetachpending flag.
20914 */
20915 if (!concurrent)
20916 RemoveInheritance(partRel, rel, false);
20917 else
20918 MarkInheritDetached(partRel, rel);
20919
20920 /*
20921 * Ensure that foreign keys still hold after this detach. This keeps
20922 * locks on the referencing tables, which prevents concurrent transactions
20923 * from adding rows that we wouldn't see. For this to work in concurrent
20924 * mode, it is critical that the partition appears as no longer attached
20925 * for the RI queries as soon as the first transaction commits.
20926 */
20928
20929 /*
20930 * Concurrent mode has to work harder; first we add a new constraint to
20931 * the partition that matches the partition constraint. Then we close our
20932 * existing transaction, and in a new one wait for all processes to catch
20933 * up on the catalog updates we've done so far; at that point we can
20934 * complete the operation.
20935 */
20936 if (concurrent)
20937 {
20938 Oid partrelid,
20939 parentrelid;
20940 LOCKTAG tag;
20941 char *parentrelname;
20942 char *partrelname;
20943
20944 /*
20945 * Add a new constraint to the partition being detached, which
20946 * supplants the partition constraint (unless there is one already).
20947 */
20948 DetachAddConstraintIfNeeded(wqueue, partRel);
20949
20950 /*
20951 * We're almost done now; the only traces that remain are the
20952 * pg_inherits tuple and the partition's relpartbounds. Before we can
20953 * remove those, we need to wait until all transactions that know that
20954 * this is a partition are gone.
20955 */
20956
20957 /*
20958 * Remember relation OIDs to re-acquire them later; and relation names
20959 * too, for error messages if something is dropped in between.
20960 */
20961 partrelid = RelationGetRelid(partRel);
20962 parentrelid = RelationGetRelid(rel);
20963 parentrelname = MemoryContextStrdup(PortalContext,
20965 partrelname = MemoryContextStrdup(PortalContext,
20966 RelationGetRelationName(partRel));
20967
20968 /* Invalidate relcache entries for the parent -- must be before close */
20970
20971 table_close(partRel, NoLock);
20972 table_close(rel, NoLock);
20973 tab->rel = NULL;
20974
20975 /* Make updated catalog entry visible */
20978
20980
20981 /*
20982 * Now wait. This ensures that all queries that were planned
20983 * including the partition are finished before we remove the rest of
20984 * catalog entries. We don't need or indeed want to acquire this
20985 * lock, though -- that would block later queries.
20986 *
20987 * We don't need to concern ourselves with waiting for a lock on the
20988 * partition itself, since we will acquire AccessExclusiveLock below.
20989 */
20990 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20992
20993 /*
20994 * Now acquire locks in both relations again. Note they may have been
20995 * removed in the meantime, so care is required.
20996 */
20997 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20998 partRel = try_relation_open(partrelid, AccessExclusiveLock);
20999
21000 /* If the relations aren't there, something bad happened; bail out */
21001 if (rel == NULL)
21002 {
21003 if (partRel != NULL) /* shouldn't happen */
21004 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21005 partrelname);
21006 ereport(ERROR,
21007 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21008 errmsg("partitioned table \"%s\" was removed concurrently",
21009 parentrelname)));
21010 }
21011 if (partRel == NULL)
21012 ereport(ERROR,
21013 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21014 errmsg("partition \"%s\" was removed concurrently", partrelname)));
21015
21016 tab->rel = rel;
21017 }
21018
21019 /*
21020 * Detaching the partition might involve TOAST table access, so ensure we
21021 * have a valid snapshot.
21022 */
21024
21025 /* Do the final part of detaching */
21026 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21027
21029
21030 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21031
21032 /* keep our lock until commit */
21033 table_close(partRel, NoLock);
21034
21035 return address;
21036}
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:271
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:680
void PopActiveSnapshot(void)
Definition: snapmgr.c:773
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:17833
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:17916
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:21045
static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
Definition: tablecmds.c:21414
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21946
void StartTransactionCommand(void)
Definition: xact.c:3071
void CommitTransactionCommand(void)
Definition: xact.c:3169

References AccessExclusiveLock, ATDetachCheckNoForeignKeyRefs(), CacheInvalidateRelcache(), CommitTransactionCommand(), DetachAddConstraintIfNeeded(), 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 21379 of file tablecmds.c.

21380{
21381 Relation partRel;
21382 ObjectAddress address;
21383 Snapshot snap = GetActiveSnapshot();
21384
21386
21387 /*
21388 * Wait until existing snapshots are gone. This is important if the
21389 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21390 * user could immediately run DETACH FINALIZE without actually waiting for
21391 * existing transactions. We must not complete the detach action until
21392 * all such queries are complete (otherwise we would present them with an
21393 * inconsistent view of catalogs).
21394 */
21395 WaitForOlderSnapshots(snap->xmin, false);
21396
21397 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21398
21399 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21400
21401 table_close(partRel, NoLock);
21402
21403 return address;
21404}
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:434
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:798
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 16447 of file tablecmds.c.

16448{
16449 mark_index_clustered(rel, InvalidOid, false);
16450}

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

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

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

Referenced by ATExecCmd(), and ATExecDropColumn().

◆ ATExecDropConstraint()

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

Definition at line 13980 of file tablecmds.c.

13983{
13984 Relation conrel;
13985 SysScanDesc scan;
13986 ScanKeyData skey[3];
13987 HeapTuple tuple;
13988 bool found = false;
13989
13990 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13991
13992 /*
13993 * Find and drop the target constraint
13994 */
13995 ScanKeyInit(&skey[0],
13996 Anum_pg_constraint_conrelid,
13997 BTEqualStrategyNumber, F_OIDEQ,
13999 ScanKeyInit(&skey[1],
14000 Anum_pg_constraint_contypid,
14001 BTEqualStrategyNumber, F_OIDEQ,
14003 ScanKeyInit(&skey[2],
14004 Anum_pg_constraint_conname,
14005 BTEqualStrategyNumber, F_NAMEEQ,
14006 CStringGetDatum(constrName));
14007 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14008 true, NULL, 3, skey);
14009
14010 /* There can be at most one matching row */
14011 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14012 {
14013 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14014 missing_ok, lockmode);
14015 found = true;
14016 }
14017
14018 systable_endscan(scan);
14019
14020 if (!found)
14021 {
14022 if (!missing_ok)
14023 ereport(ERROR,
14024 errcode(ERRCODE_UNDEFINED_OBJECT),
14025 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14026 constrName, RelationGetRelationName(rel)));
14027 else
14029 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14030 constrName, RelationGetRelationName(rel)));
14031 }
14032
14034}
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14045

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

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

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

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

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

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

◆ ATExecDropInherit()

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

Definition at line 17791 of file tablecmds.c.

17792{
17793 ObjectAddress address;
17794 Relation parent_rel;
17795
17796 if (rel->rd_rel->relispartition)
17797 ereport(ERROR,
17798 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17799 errmsg("cannot change inheritance of a partition")));
17800
17801 /*
17802 * AccessShareLock on the parent is probably enough, seeing that DROP
17803 * TABLE doesn't lock parent tables at all. We need some lock since we'll
17804 * be inspecting the parent's schema.
17805 */
17806 parent_rel = table_openrv(parent, AccessShareLock);
17807
17808 /*
17809 * We don't bother to check ownership of the parent table --- ownership of
17810 * the child is presumed enough rights.
17811 */
17812
17813 /* Off to RemoveInheritance() where most of the work happens */
17814 RemoveInheritance(rel, parent_rel, false);
17815
17816 ObjectAddressSet(address, RelationRelationId,
17817 RelationGetRelid(parent_rel));
17818
17819 /* keep our lock on the parent relation until commit */
17820 table_close(parent_rel, NoLock);
17821
17822 return address;
17823}

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

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

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

Referenced by ATExecCmd().

◆ ATExecDropOf()

static void ATExecDropOf ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 18324 of file tablecmds.c.

18325{
18326 Oid relid = RelationGetRelid(rel);
18327 Relation relationRelation;
18328 HeapTuple tuple;
18329
18330 if (!OidIsValid(rel->rd_rel->reloftype))
18331 ereport(ERROR,
18332 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18333 errmsg("\"%s\" is not a typed table",
18335
18336 /*
18337 * We don't bother to check ownership of the type --- ownership of the
18338 * table is presumed enough rights. No lock required on the type, either.
18339 */
18340
18341 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18343
18344 /* Clear pg_class.reloftype */
18345 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18346 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18347 if (!HeapTupleIsValid(tuple))
18348 elog(ERROR, "cache lookup failed for relation %u", relid);
18349 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18350 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18351
18352 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18353
18354 heap_freetuple(tuple);
18355 table_close(relationRelation, RowExclusiveLock);
18356}

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

17192{
17193 EnableDisableRule(rel, rulename, fires_when);
17194
17195 InvokeObjectPostAlterHook(RelationRelationId,
17196 RelationGetRelid(rel), 0);
17197}
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 17172 of file tablecmds.c.

17175{
17176 EnableDisableTrigger(rel, trigname, InvalidOid,
17177 fires_when, skip_system, recurse,
17178 lockmode);
17179
17180 InvokeObjectPostAlterHook(RelationRelationId,
17181 RelationGetRelid(rel), 0);
17182}
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 18600 of file tablecmds.c.

18601{
18602 Relation pg_class;
18603 Oid relid;
18604 HeapTuple tuple;
18605
18606 relid = RelationGetRelid(rel);
18607
18608 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18609
18610 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18611
18612 if (!HeapTupleIsValid(tuple))
18613 elog(ERROR, "cache lookup failed for relation %u", relid);
18614
18615 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18616 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18617
18618 InvokeObjectPostAlterHook(RelationRelationId,
18619 RelationGetRelid(rel), 0);
18620
18621 table_close(pg_class, RowExclusiveLock);
18622 heap_freetuple(tuple);
18623}

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

18630{
18631 Relation ftrel;
18632 ForeignServer *server;
18633 ForeignDataWrapper *fdw;
18634 HeapTuple tuple;
18635 bool isnull;
18636 Datum repl_val[Natts_pg_foreign_table];
18637 bool repl_null[Natts_pg_foreign_table];
18638 bool repl_repl[Natts_pg_foreign_table];
18639 Datum datum;
18640 Form_pg_foreign_table tableform;
18641
18642 if (options == NIL)
18643 return;
18644
18645 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18646
18647 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18648 ObjectIdGetDatum(rel->rd_id));
18649 if (!HeapTupleIsValid(tuple))
18650 ereport(ERROR,
18651 (errcode(ERRCODE_UNDEFINED_OBJECT),
18652 errmsg("foreign table \"%s\" does not exist",
18654 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18655 server = GetForeignServer(tableform->ftserver);
18656 fdw = GetForeignDataWrapper(server->fdwid);
18657
18658 memset(repl_val, 0, sizeof(repl_val));
18659 memset(repl_null, false, sizeof(repl_null));
18660 memset(repl_repl, false, sizeof(repl_repl));
18661
18662 /* Extract the current options */
18663 datum = SysCacheGetAttr(FOREIGNTABLEREL,
18664 tuple,
18665 Anum_pg_foreign_table_ftoptions,
18666 &isnull);
18667 if (isnull)
18668 datum = PointerGetDatum(NULL);
18669
18670 /* Transform the options */
18671 datum = transformGenericOptions(ForeignTableRelationId,
18672 datum,
18673 options,
18674 fdw->fdwvalidator);
18675
18676 if (DatumGetPointer(datum) != NULL)
18677 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18678 else
18679 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18680
18681 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18682
18683 /* Everything looks good - update the tuple */
18684
18685 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18686 repl_val, repl_null, repl_repl);
18687
18688 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18689
18690 /*
18691 * Invalidate relcache so that all sessions will refresh any cached plans
18692 * that might depend on the old options.
18693 */
18695
18696 InvokeObjectPostAlterHook(ForeignTableRelationId,
18697 RelationGetRelid(rel), 0);
18698
18700
18701 heap_freetuple(tuple);
18702}

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

18457{
18458 Oid indexOid;
18459 Relation indexRel;
18460 int key;
18461
18462 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18463 {
18464 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18465 return;
18466 }
18467 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18468 {
18469 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18470 return;
18471 }
18472 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18473 {
18474 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18475 return;
18476 }
18477 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18478 {
18479 /* fallthrough */ ;
18480 }
18481 else
18482 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18483
18484 /* Check that the index exists */
18485 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18486 if (!OidIsValid(indexOid))
18487 ereport(ERROR,
18488 (errcode(ERRCODE_UNDEFINED_OBJECT),
18489 errmsg("index \"%s\" for table \"%s\" does not exist",
18490 stmt->name, RelationGetRelationName(rel))));
18491
18492 indexRel = index_open(indexOid, ShareLock);
18493
18494 /* Check that the index is on the relation we're altering. */
18495 if (indexRel->rd_index == NULL ||
18496 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18497 ereport(ERROR,
18498 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18499 errmsg("\"%s\" is not an index for table \"%s\"",
18500 RelationGetRelationName(indexRel),
18502
18503 /*
18504 * The AM must support uniqueness, and the index must in fact be unique.
18505 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18506 * exclusion), we can use that too.
18507 */
18508 if ((!indexRel->rd_indam->amcanunique ||
18509 !indexRel->rd_index->indisunique) &&
18510 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18511 ereport(ERROR,
18512 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18513 errmsg("cannot use non-unique index \"%s\" as replica identity",
18514 RelationGetRelationName(indexRel))));
18515 /* Deferred indexes are not guaranteed to be always unique. */
18516 if (!indexRel->rd_index->indimmediate)
18517 ereport(ERROR,
18518 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18519 errmsg("cannot use non-immediate index \"%s\" as replica identity",
18520 RelationGetRelationName(indexRel))));
18521 /* Expression indexes aren't supported. */
18522 if (RelationGetIndexExpressions(indexRel) != NIL)
18523 ereport(ERROR,
18524 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18525 errmsg("cannot use expression index \"%s\" as replica identity",
18526 RelationGetRelationName(indexRel))));
18527 /* Predicate indexes aren't supported. */
18528 if (RelationGetIndexPredicate(indexRel) != NIL)
18529 ereport(ERROR,
18530 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18531 errmsg("cannot use partial index \"%s\" as replica identity",
18532 RelationGetRelationName(indexRel))));
18533
18534 /* Check index for nullable columns. */
18535 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18536 {
18537 int16 attno = indexRel->rd_index->indkey.values[key];
18538 Form_pg_attribute attr;
18539
18540 /*
18541 * Reject any other system columns. (Going forward, we'll disallow
18542 * indexes containing such columns in the first place, but they might
18543 * exist in older branches.)
18544 */
18545 if (attno <= 0)
18546 ereport(ERROR,
18547 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18548 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18549 RelationGetRelationName(indexRel), attno)));
18550
18551 attr = TupleDescAttr(rel->rd_att, attno - 1);
18552 if (!attr->attnotnull)
18553 ereport(ERROR,
18554 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18555 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18556 RelationGetRelationName(indexRel),
18557 NameStr(attr->attname))));
18558 }
18559
18560 /* This index is suitable for use as a replica identity. Mark it. */
18561 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18562
18563 index_close(indexRel, NoLock);
18564}
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:533
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5210
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5097
bool amcanunique
Definition: amapi.h:256
struct IndexAmRoutine * rd_indam
Definition: rel.h:206
TupleDesc rd_att
Definition: rel.h:112
static void relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, bool is_internal)
Definition: tablecmds.c:18368

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

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

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

18714{
18715 Relation attrel;
18716 HeapTuple tuple;
18717 Form_pg_attribute atttableform;
18719 char *compression;
18720 char cmethod;
18721 ObjectAddress address;
18722
18723 compression = strVal(newValue);
18724
18725 attrel = table_open(AttributeRelationId, RowExclusiveLock);
18726
18727 /* copy the cache entry so we can scribble on it below */
18728 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18729 if (!HeapTupleIsValid(tuple))
18730 ereport(ERROR,
18731 (errcode(ERRCODE_UNDEFINED_COLUMN),
18732 errmsg("column \"%s\" of relation \"%s\" does not exist",
18733 column, RelationGetRelationName(rel))));
18734
18735 /* prevent them from altering a system attribute */
18736 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18737 attnum = atttableform->attnum;
18738 if (attnum <= 0)
18739 ereport(ERROR,
18740 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18741 errmsg("cannot alter system column \"%s\"", column)));
18742
18743 /*
18744 * Check that column type is compressible, then get the attribute
18745 * compression method code
18746 */
18747 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18748
18749 /* update pg_attribute entry */
18750 atttableform->attcompression = cmethod;
18751 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18752
18753 InvokeObjectPostAlterHook(RelationRelationId,
18754 RelationGetRelid(rel),
18755 attnum);
18756
18757 /*
18758 * Apply the change to indexes as well (only for simple index columns,
18759 * matching behavior of index.c ConstructTupleDescriptor()).
18760 */
18761 SetIndexStorageProperties(rel, attrel, attnum,
18762 false, 0,
18763 true, cmethod,
18764 lockmode);
18765
18766 heap_freetuple(tuple);
18767
18769
18770 /* make changes visible */
18772
18773 ObjectAddressSubSet(address, RelationRelationId,
18774 RelationGetRelid(rel), attnum);
18775 return address;
18776}
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:21995
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:9098

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

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

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

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

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

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

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

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

18571{
18572 Relation pg_class;
18573 Oid relid;
18574 HeapTuple tuple;
18575
18576 relid = RelationGetRelid(rel);
18577
18578 /* Pull the record for this relation and update it */
18579 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18580
18581 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18582
18583 if (!HeapTupleIsValid(tuple))
18584 elog(ERROR, "cache lookup failed for relation %u", relid);
18585
18586 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18587 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18588
18589 InvokeObjectPostAlterHook(RelationRelationId,
18590 RelationGetRelid(rel), 0);
18591
18592 table_close(pg_class, RowExclusiveLock);
18593 heap_freetuple(tuple);
18594}

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

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

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

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

16822{
16823 Relation rel;
16824 Oid reltoastrelid;
16825 RelFileNumber newrelfilenumber;
16826 RelFileLocator newrlocator;
16827 List *reltoastidxids = NIL;
16828 ListCell *lc;
16829
16830 /*
16831 * Need lock here in case we are recursing to toast table or index
16832 */
16833 rel = relation_open(tableOid, lockmode);
16834
16835 /* Check first if relation can be moved to new tablespace */
16836 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16837 {
16838 InvokeObjectPostAlterHook(RelationRelationId,
16839 RelationGetRelid(rel), 0);
16840 relation_close(rel, NoLock);
16841 return;
16842 }
16843
16844 reltoastrelid = rel->rd_rel->reltoastrelid;
16845 /* Fetch the list of indexes on toast relation if necessary */
16846 if (OidIsValid(reltoastrelid))
16847 {
16848 Relation toastRel = relation_open(reltoastrelid, lockmode);
16849
16850 reltoastidxids = RelationGetIndexList(toastRel);
16851 relation_close(toastRel, lockmode);
16852 }
16853
16854 /*
16855 * Relfilenumbers are not unique in databases across tablespaces, so we
16856 * need to allocate a new one in the new tablespace.
16857 */
16858 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16859 rel->rd_rel->relpersistence);
16860
16861 /* Open old and new relation */
16862 newrlocator = rel->rd_locator;
16863 newrlocator.relNumber = newrelfilenumber;
16864 newrlocator.spcOid = newTableSpace;
16865
16866 /* hand off to AM to actually create new rel storage and copy the data */
16867 if (rel->rd_rel->relkind == RELKIND_INDEX)
16868 {
16869 index_copy_data(rel, newrlocator);
16870 }
16871 else
16872 {
16873 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16874 table_relation_copy_data(rel, &newrlocator);
16875 }
16876
16877 /*
16878 * Update the pg_class row.
16879 *
16880 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16881 * executed on pg_class or its indexes (the above copy wouldn't contain
16882 * the updated pg_class entry), but that's forbidden with
16883 * CheckRelationTableSpaceMove().
16884 */
16885 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16886
16887 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16888
16890
16891 relation_close(rel, NoLock);
16892
16893 /* Make sure the reltablespace change is visible */
16895
16896 /* Move associated toast relation and/or indexes, too */
16897 if (OidIsValid(reltoastrelid))
16898 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16899 foreach(lc, reltoastidxids)
16900 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16901
16902 /* Clean up */
16903 list_free(reltoastidxids);
16904}
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:1620
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:16821
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3686
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3743
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:17115

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

16915{
16916 /*
16917 * Shouldn't be called on relations having storage; these are processed in
16918 * phase 3.
16919 */
16920 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16921
16922 /* check if relation can be moved to its new tablespace */
16923 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16924 {
16925 InvokeObjectPostAlterHook(RelationRelationId,
16926 RelationGetRelid(rel),
16927 0);
16928 return;
16929 }
16930
16931 /* Update can be done, so change reltablespace */
16932 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16933
16934 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16935
16936 /* Make sure the reltablespace change is visible */
16938}

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

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

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

6556{
6557 Oid relid = RelationGetRelid(rel);
6558 AlteredTableInfo *tab;
6559 ListCell *ltab;
6560
6561 foreach(ltab, *wqueue)
6562 {
6563 tab = (AlteredTableInfo *) lfirst(ltab);
6564 if (tab->relid == relid)
6565 return tab;
6566 }
6567
6568 /*
6569 * Not there, so add it. Note that we make a copy of the relation's
6570 * existing descriptor before anything interesting can happen to it.
6571 */
6572 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6573 tab->relid = relid;
6574 tab->rel = NULL; /* set later */
6575 tab->relkind = rel->rd_rel->relkind;
6578 tab->chgAccessMethod = false;
6580 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6581 tab->chgPersistence = false;
6582
6583 *wqueue = lappend(*wqueue, tab);
6584
6585 return tab;
6586}
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(), DetachAddConstraintIfNeeded(), QueueCheckConstraintValidation(), QueueFKConstraintValidation(), QueueNNConstraintValidation(), QueuePartitionConstraintValidation(), and set_attnotnull().

◆ ATParseTransformCmd()

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

Definition at line 5704 of file tablecmds.c.

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

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

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

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

15598{
15599 List *raw_parsetree_list;
15600 List *querytree_list;
15601 ListCell *list_item;
15602 Relation rel;
15603
15604 /*
15605 * We expect that we will get only ALTER TABLE and CREATE INDEX
15606 * statements. Hence, there is no need to pass them through
15607 * parse_analyze_*() or the rewriter, but instead we need to pass them
15608 * through parse_utilcmd.c to make them ready for execution.
15609 */
15610 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15611 querytree_list = NIL;
15612 foreach(list_item, raw_parsetree_list)
15613 {
15614 RawStmt *rs = lfirst_node(RawStmt, list_item);
15615 Node *stmt = rs->stmt;
15616
15617 if (IsA(stmt, IndexStmt))
15618 querytree_list = lappend(querytree_list,
15619 transformIndexStmt(oldRelId,
15620 (IndexStmt *) stmt,
15621 cmd));
15622 else if (IsA(stmt, AlterTableStmt))
15623 {
15624 List *beforeStmts;
15625 List *afterStmts;
15626
15627 stmt = (Node *) transformAlterTableStmt(oldRelId,
15628 (AlterTableStmt *) stmt,
15629 cmd,
15630 &beforeStmts,
15631 &afterStmts);
15632 querytree_list = list_concat(querytree_list, beforeStmts);
15633 querytree_list = lappend(querytree_list, stmt);
15634 querytree_list = list_concat(querytree_list, afterStmts);
15635 }
15636 else if (IsA(stmt, CreateStatsStmt))
15637 querytree_list = lappend(querytree_list,
15638 transformStatsStmt(oldRelId,
15640 cmd));
15641 else
15642 querytree_list = lappend(querytree_list, stmt);
15643 }
15644
15645 /* Caller should already have acquired whatever lock we need. */
15646 rel = relation_open(oldRelId, NoLock);
15647
15648 /*
15649 * Attach each generated command to the proper place in the work queue.
15650 * Note this could result in creation of entirely new work-queue entries.
15651 *
15652 * Also note that we have to tweak the command subtypes, because it turns
15653 * out that re-creation of indexes and constraints has to act a bit
15654 * differently from initial creation.
15655 */
15656 foreach(list_item, querytree_list)
15657 {
15658 Node *stm = (Node *) lfirst(list_item);
15659 AlteredTableInfo *tab;
15660
15661 tab = ATGetQueueEntry(wqueue, rel);
15662
15663 if (IsA(stm, IndexStmt))
15664 {
15665 IndexStmt *stmt = (IndexStmt *) stm;
15666 AlterTableCmd *newcmd;
15667
15668 if (!rewrite)
15669 TryReuseIndex(oldId, stmt);
15670 stmt->reset_default_tblspc = true;
15671 /* keep the index's comment */
15672 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15673
15674 newcmd = makeNode(AlterTableCmd);
15675 newcmd->subtype = AT_ReAddIndex;
15676 newcmd->def = (Node *) stmt;
15678 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15679 }
15680 else if (IsA(stm, AlterTableStmt))
15681 {
15683 ListCell *lcmd;
15684
15685 foreach(lcmd, stmt->cmds)
15686 {
15688
15689 if (cmd->subtype == AT_AddIndex)
15690 {
15691 IndexStmt *indstmt;
15692 Oid indoid;
15693
15694 indstmt = castNode(IndexStmt, cmd->def);
15695 indoid = get_constraint_index(oldId);
15696
15697 if (!rewrite)
15698 TryReuseIndex(indoid, indstmt);
15699 /* keep any comment on the index */
15700 indstmt->idxcomment = GetComment(indoid,
15701 RelationRelationId, 0);
15702 indstmt->reset_default_tblspc = true;
15703
15704 cmd->subtype = AT_ReAddIndex;
15706 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15707
15708 /* recreate any comment on the constraint */
15711 oldId,
15712 rel,
15713 NIL,
15714 indstmt->idxname);
15715 }
15716 else if (cmd->subtype == AT_AddConstraint)
15717 {
15718 Constraint *con = castNode(Constraint, cmd->def);
15719
15720 con->old_pktable_oid = refRelId;
15721 /* rewriting neither side of a FK */
15722 if (con->contype == CONSTR_FOREIGN &&
15723 !rewrite && tab->rewrite == 0)
15724 TryReuseForeignKey(oldId, con);
15725 con->reset_default_tblspc = true;
15729
15730 /*
15731 * Recreate any comment on the constraint. If we have
15732 * recreated a primary key, then transformTableConstraint
15733 * has added an unnamed not-null constraint here; skip
15734 * this in that case.
15735 */
15736 if (con->conname)
15739 oldId,
15740 rel,
15741 NIL,
15742 con->conname);
15743 else
15744 Assert(con->contype == CONSTR_NOTNULL);
15745 }
15746 else
15747 elog(ERROR, "unexpected statement subtype: %d",
15748 (int) cmd->subtype);
15749 }
15750 }
15751 else if (IsA(stm, AlterDomainStmt))
15752 {
15754
15755 if (stmt->subtype == AD_AddConstraint)
15756 {
15757 Constraint *con = castNode(Constraint, stmt->def);
15759
15761 cmd->def = (Node *) stmt;
15764
15765 /* recreate any comment on the constraint */
15768 oldId,
15769 NULL,
15770 stmt->typeName,
15771 con->conname);
15772 }
15773 else
15774 elog(ERROR, "unexpected statement subtype: %d",
15775 (int) stmt->subtype);
15776 }
15777 else if (IsA(stm, CreateStatsStmt))
15778 {
15780 AlterTableCmd *newcmd;
15781
15782 /* keep the statistics object's comment */
15783 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15784
15785 newcmd = makeNode(AlterTableCmd);
15786 newcmd->subtype = AT_ReAddStatistics;
15787 newcmd->def = (Node *) stmt;
15788 tab->subcmds[AT_PASS_MISC] =
15789 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15790 }
15791 else
15792 elog(ERROR, "unexpected statement type: %d",
15793 (int) nodeTag(stm));
15794 }
15795
15796 relation_close(rel, NoLock);
15797}
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:2546
@ RAW_PARSE_DEFAULT
Definition: parser.h:39
bool reset_default_tblspc
Definition: parsenodes.h:2857
bool reset_default_tblspc
Definition: parsenodes.h:3510
char * idxname
Definition: parsenodes.h:3484
char * idxcomment
Definition: parsenodes.h:3494
Node * stmt
Definition: parsenodes.h:2087
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:15854
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:15883
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:15810

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

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

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

17208{
17209 if (child_rel->rd_rel->reloftype)
17210 ereport(ERROR,
17211 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17212 errmsg("cannot change inheritance of typed table")));
17213
17214 if (child_rel->rd_rel->relispartition)
17215 ereport(ERROR,
17216 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17217 errmsg("cannot change inheritance of a partition")));
17218
17219 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17220 ereport(ERROR,
17221 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17222 errmsg("cannot change inheritance of partitioned table")));
17223}

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

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

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

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

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

18788{
18789 Relation pg_constraint;
18790 HeapTuple tuple;
18791 SysScanDesc scan;
18792 ScanKeyData skey[1];
18793
18794 /*
18795 * Disallow changing status for a temp table. Also verify whether we can
18796 * get away with doing nothing; in such cases we don't need to run the
18797 * checks below, either.
18798 */
18799 switch (rel->rd_rel->relpersistence)
18800 {
18801 case RELPERSISTENCE_TEMP:
18802 ereport(ERROR,
18803 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18804 errmsg("cannot change logged status of table \"%s\" because it is temporary",
18806 errtable(rel)));
18807 break;
18808 case RELPERSISTENCE_PERMANENT:
18809 if (toLogged)
18810 /* nothing to do */
18811 return;
18812 break;
18813 case RELPERSISTENCE_UNLOGGED:
18814 if (!toLogged)
18815 /* nothing to do */
18816 return;
18817 break;
18818 }
18819
18820 /*
18821 * Check that the table is not part of any publication when changing to
18822 * UNLOGGED, as UNLOGGED tables can't be published.
18823 */
18824 if (!toLogged &&
18826 ereport(ERROR,
18827 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18828 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18830 errdetail("Unlogged relations cannot be replicated.")));
18831
18832 /*
18833 * Check existing foreign key constraints to preserve the invariant that
18834 * permanent tables cannot reference unlogged ones. Self-referencing
18835 * foreign keys can safely be ignored.
18836 */
18837 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18838
18839 /*
18840 * Scan conrelid if changing to permanent, else confrelid. This also
18841 * determines whether a useful index exists.
18842 */
18843 ScanKeyInit(&skey[0],
18844 toLogged ? Anum_pg_constraint_conrelid :
18845 Anum_pg_constraint_confrelid,
18846 BTEqualStrategyNumber, F_OIDEQ,
18848 scan = systable_beginscan(pg_constraint,
18849 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18850 true, NULL, 1, skey);
18851
18852 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18853 {
18855
18856 if (con->contype == CONSTRAINT_FOREIGN)
18857 {
18858 Oid foreignrelid;
18859 Relation foreignrel;
18860
18861 /* the opposite end of what we used as scankey */
18862 foreignrelid = toLogged ? con->confrelid : con->conrelid;
18863
18864 /* ignore if self-referencing */
18865 if (RelationGetRelid(rel) == foreignrelid)
18866 continue;
18867
18868 foreignrel = relation_open(foreignrelid, AccessShareLock);
18869
18870 if (toLogged)
18871 {
18872 if (!RelationIsPermanent(foreignrel))
18873 ereport(ERROR,
18874 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18875 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18877 RelationGetRelationName(foreignrel)),
18878 errtableconstraint(rel, NameStr(con->conname))));
18879 }
18880 else
18881 {
18882 if (RelationIsPermanent(foreignrel))
18883 ereport(ERROR,
18884 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18885 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18887 RelationGetRelationName(foreignrel)),
18888 errtableconstraint(rel, NameStr(con->conname))));
18889 }
18890
18891 relation_close(foreignrel, AccessShareLock);
18892 }
18893 }
18894
18895 systable_endscan(scan);
18896
18897 table_close(pg_constraint, AccessShareLock);
18898
18899 /* force rewrite if necessary; see comment in ATRewriteTables */
18901 if (toLogged)
18902 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18903 else
18904 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18905 tab->chgPersistence = true;
18906}
#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 4898 of file tablecmds.c.

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

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

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

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

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

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

16460{
16461 Oid amoid;
16462
16463 /*
16464 * Look up the access method name and check that it differs from the
16465 * table's current AM. If DEFAULT was specified for a partitioned table
16466 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16467 */
16468 if (amname != NULL)
16469 amoid = get_table_am_oid(amname, false);
16470 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16471 amoid = InvalidOid;
16472 else
16474
16475 /* if it's a match, phase 3 doesn't need to do anything */
16476 if (rel->rd_rel->relam == amoid)
16477 return;
16478
16479 /* Save info for Phase 3 to do the real work */
16481 tab->newAccessMethod = amoid;
16482 tab->chgAccessMethod = true;
16483}
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 16583 of file tablecmds.c.

16584{
16585 Oid tablespaceId;
16586
16587 /* Check that the tablespace exists */
16588 tablespaceId = get_tablespace_oid(tablespacename, false);
16589
16590 /* Check permissions except when moving to database's default */
16591 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16592 {
16593 AclResult aclresult;
16594
16595 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16596 if (aclresult != ACLCHECK_OK)
16597 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16598 }
16599
16600 /* Save info for Phase 3 to do the real work */
16601 if (OidIsValid(tab->newTableSpace))
16602 ereport(ERROR,
16603 (errcode(ERRCODE_SYNTAX_ERROR),
16604 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16605
16606 tab->newTableSpace = tablespaceId;
16607}

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

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

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

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

Referenced by ATRewriteTables().

◆ ATRewriteTables()

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

Definition at line 5831 of file tablecmds.c.

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

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

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

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

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

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

20528{
20529 List *idxes;
20530 List *attachRelIdxs;
20531 Relation *attachrelIdxRels;
20532 IndexInfo **attachInfos;
20533 ListCell *cell;
20534 MemoryContext cxt;
20535 MemoryContext oldcxt;
20536
20538 "AttachPartitionEnsureIndexes",
20540 oldcxt = MemoryContextSwitchTo(cxt);
20541
20542 idxes = RelationGetIndexList(rel);
20543 attachRelIdxs = RelationGetIndexList(attachrel);
20544 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
20545 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
20546
20547 /* Build arrays of all existing indexes and their IndexInfos */
20548 foreach_oid(cldIdxId, attachRelIdxs)
20549 {
20550 int i = foreach_current_index(cldIdxId);
20551
20552 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20553 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20554 }
20555
20556 /*
20557 * If we're attaching a foreign table, we must fail if any of the indexes
20558 * is a constraint index; otherwise, there's nothing to do here. Do this
20559 * before starting work, to avoid wasting the effort of building a few
20560 * non-unique indexes before coming across a unique one.
20561 */
20562 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20563 {
20564 foreach(cell, idxes)
20565 {
20566 Oid idx = lfirst_oid(cell);
20568
20569 if (idxRel->rd_index->indisunique ||
20570 idxRel->rd_index->indisprimary)
20571 ereport(ERROR,
20572 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20573 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20574 RelationGetRelationName(attachrel),
20576 errdetail("Partitioned table \"%s\" contains unique indexes.",
20579 }
20580
20581 goto out;
20582 }
20583
20584 /*
20585 * For each index on the partitioned table, find a matching one in the
20586 * partition-to-be; if one is not found, create one.
20587 */
20588 foreach(cell, idxes)
20589 {
20590 Oid idx = lfirst_oid(cell);
20592 IndexInfo *info;
20593 AttrMap *attmap;
20594 bool found = false;
20595 Oid constraintOid;
20596
20597 /*
20598 * Ignore indexes in the partitioned table other than partitioned
20599 * indexes.
20600 */
20601 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20602 {
20604 continue;
20605 }
20606
20607 /* construct an indexinfo to compare existing indexes against */
20608 info = BuildIndexInfo(idxRel);
20609 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20610 RelationGetDescr(rel),
20611 false);
20613
20614 /*
20615 * Scan the list of existing indexes in the partition-to-be, and mark
20616 * the first matching, valid, unattached one we find, if any, as
20617 * partition of the parent index. If we find one, we're done.
20618 */
20619 for (int i = 0; i < list_length(attachRelIdxs); i++)
20620 {
20621 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20622 Oid cldConstrOid = InvalidOid;
20623
20624 /* does this index have a parent? if so, can't use it */
20625 if (attachrelIdxRels[i]->rd_rel->relispartition)
20626 continue;
20627
20628 /* If this index is invalid, can't use it */
20629 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20630 continue;
20631
20632 if (CompareIndexInfo(attachInfos[i], info,
20633 attachrelIdxRels[i]->rd_indcollation,
20634 idxRel->rd_indcollation,
20635 attachrelIdxRels[i]->rd_opfamily,
20636 idxRel->rd_opfamily,
20637 attmap))
20638 {
20639 /*
20640 * If this index is being created in the parent because of a
20641 * constraint, then the child needs to have a constraint also,
20642 * so look for one. If there is no such constraint, this
20643 * index is no good, so keep looking.
20644 */
20645 if (OidIsValid(constraintOid))
20646 {
20647 cldConstrOid =
20649 cldIdxId);
20650 /* no dice */
20651 if (!OidIsValid(cldConstrOid))
20652 continue;
20653
20654 /* Ensure they're both the same type of constraint */
20655 if (get_constraint_type(constraintOid) !=
20656 get_constraint_type(cldConstrOid))
20657 continue;
20658 }
20659
20660 /* bingo. */
20661 IndexSetParentIndex(attachrelIdxRels[i], idx);
20662 if (OidIsValid(constraintOid))
20663 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20664 RelationGetRelid(attachrel));
20665 found = true;
20666
20668 break;
20669 }
20670 }
20671
20672 /*
20673 * If no suitable index was found in the partition-to-be, create one
20674 * now. Note that if this is a PK, not-null constraints must already
20675 * exist.
20676 */
20677 if (!found)
20678 {
20679 IndexStmt *stmt;
20680 Oid conOid;
20681
20683 idxRel, attmap,
20684 &conOid);
20686 RelationGetRelid(idxRel),
20687 conOid,
20688 -1,
20689 true, false, false, false, false);
20690 }
20691
20693 }
20694
20695out:
20696 /* Clean up. */
20697 for (int i = 0; i < list_length(attachRelIdxs); i++)
20698 index_close(attachrelIdxRels[i], AccessShareLock);
20699 MemoryContextSwitchTo(oldcxt);
20701}
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 11758 of file tablecmds.c.

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

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

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

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

16282{
16283 Relation attRelation;
16284 SysScanDesc scan;
16285 ScanKeyData key[1];
16286 HeapTuple attributeTuple;
16287
16288 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16289 ScanKeyInit(&key[0],
16290 Anum_pg_attribute_attrelid,
16291 BTEqualStrategyNumber, F_OIDEQ,
16292 ObjectIdGetDatum(relationOid));
16293 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16294 true, NULL, 1, key);
16295 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16296 {
16297 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16298 Datum repl_val[Natts_pg_attribute];
16299 bool repl_null[Natts_pg_attribute];
16300 bool repl_repl[Natts_pg_attribute];
16301 Acl *newAcl;
16302 Datum aclDatum;
16303 bool isNull;
16304 HeapTuple newtuple;
16305
16306 /* Ignore dropped columns */
16307 if (att->attisdropped)
16308 continue;
16309
16310 aclDatum = heap_getattr(attributeTuple,
16311 Anum_pg_attribute_attacl,
16312 RelationGetDescr(attRelation),
16313 &isNull);
16314 /* Null ACLs do not require changes */
16315 if (isNull)
16316 continue;
16317
16318 memset(repl_null, false, sizeof(repl_null));
16319 memset(repl_repl, false, sizeof(repl_repl));
16320
16321 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16322 oldOwnerId, newOwnerId);
16323 repl_repl[Anum_pg_attribute_attacl - 1] = true;
16324 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16325
16326 newtuple = heap_modify_tuple(attributeTuple,
16327 RelationGetDescr(attRelation),
16328 repl_val, repl_null, repl_repl);
16329
16330 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16331
16332 heap_freetuple(newtuple);
16333 }
16334 systable_endscan(scan);
16335 table_close(attRelation, RowExclusiveLock);
16336}

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

16347{
16348 Relation depRel;
16349 SysScanDesc scan;
16350 ScanKeyData key[2];
16351 HeapTuple tup;
16352
16353 /*
16354 * SERIAL sequences are those having an auto dependency on one of the
16355 * table's columns (we don't care *which* column, exactly).
16356 */
16357 depRel = table_open(DependRelationId, AccessShareLock);
16358
16359 ScanKeyInit(&key[0],
16360 Anum_pg_depend_refclassid,
16361 BTEqualStrategyNumber, F_OIDEQ,
16362 ObjectIdGetDatum(RelationRelationId));
16363 ScanKeyInit(&key[1],
16364 Anum_pg_depend_refobjid,
16365 BTEqualStrategyNumber, F_OIDEQ,
16366 ObjectIdGetDatum(relationOid));
16367 /* we leave refobjsubid unspecified */
16368
16369 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16370 NULL, 2, key);
16371
16372 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16373 {
16374 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16375 Relation seqRel;
16376
16377 /* skip dependencies other than auto dependencies on columns */
16378 if (depForm->refobjsubid == 0 ||
16379 depForm->classid != RelationRelationId ||
16380 depForm->objsubid != 0 ||
16381 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16382 continue;
16383
16384 /* Use relation_open just in case it's an index */
16385 seqRel = relation_open(depForm->objid, lockmode);
16386
16387 /* skip non-sequence relations */
16388 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16389 {
16390 /* No need to keep the lock */
16391 relation_close(seqRel, lockmode);
16392 continue;
16393 }
16394
16395 /* We don't need to close the sequence while we alter it. */
16396 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16397
16398 /* Now we can close it. Keep the lock till end of transaction. */
16399 relation_close(seqRel, NoLock);
16400 }
16401
16402 systable_endscan(scan);
16403
16405}

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

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

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

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

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

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

13634{
13635 Oid roleid = GetUserId();
13636 AclResult aclresult;
13637 int i;
13638
13639 /* Okay if we have relation-level REFERENCES permission */
13640 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13642 if (aclresult == ACLCHECK_OK)
13643 return;
13644 /* Else we must have REFERENCES on each column */
13645 for (i = 0; i < natts; i++)
13646 {
13647 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13648 roleid, ACL_REFERENCES);
13649 if (aclresult != ACLCHECK_OK)
13650 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13652 }
13653}
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 3686 of file tablecmds.c.

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

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

4410{
4411 int expected_refcnt;
4412
4413 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4414 if (rel->rd_refcnt != expected_refcnt)
4415 ereport(ERROR,
4416 (errcode(ERRCODE_OBJECT_IN_USE),
4417 /* translator: first %s is a SQL command, eg ALTER TABLE */
4418 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4420
4421 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4422 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4424 ereport(ERROR,
4425 (errcode(ERRCODE_OBJECT_IN_USE),
4426 /* translator: first %s is a SQL command, eg ALTER TABLE */
4427 errmsg("cannot %s \"%s\" because it has pending trigger events",
4429}
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 9838 of file tablecmds.c.

9839{
9840 char buf[NAMEDATALEN * 2];
9841 int buflen = 0;
9842 ListCell *lc;
9843
9844 buf[0] = '\0';
9845 foreach(lc, colnames)
9846 {
9847 const char *name = strVal(lfirst(lc));
9848
9849 if (buflen > 0)
9850 buf[buflen++] = '_'; /* insert _ between names */
9851
9852 /*
9853 * At this point we have buflen <= NAMEDATALEN. name should be less
9854 * than NAMEDATALEN already, but use strlcpy for paranoia.
9855 */
9856 strlcpy(buf + buflen, name, NAMEDATALEN);
9857 buflen += strlen(buf + buflen);
9858 if (buflen >= NAMEDATALEN)
9859 break;
9860 }
9861 return pstrdup(buf);
9862}
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 11214 of file tablecmds.c.

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

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

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

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

11187{
11188 /* This only works for declarative partitioning */
11189 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11190
11191 /*
11192 * First, clone constraints where the parent is on the referencing side.
11193 */
11194 CloneFkReferencing(wqueue, parentRel, partitionRel);
11195
11196 /*
11197 * Clone constraints for which the parent is on the referenced side.
11198 */
11199 CloneFkReferenced(parentRel, partitionRel);
11200}
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11415
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11214

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

20710{
20711 Relation pg_trigger;
20713 SysScanDesc scan;
20714 HeapTuple tuple;
20715 MemoryContext perTupCxt;
20716
20717 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20718 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20719 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20720 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20721 true, NULL, 1, &key);
20722
20724 "clone trig", ALLOCSET_SMALL_SIZES);
20725
20726 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20727 {
20728 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20729 CreateTrigStmt *trigStmt;
20730 Node *qual = NULL;
20731 Datum value;
20732 bool isnull;
20733 List *cols = NIL;
20734 List *trigargs = NIL;
20735 MemoryContext oldcxt;
20736
20737 /*
20738 * Ignore statement-level triggers; those are not cloned.
20739 */
20740 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20741 continue;
20742
20743 /*
20744 * Don't clone internal triggers, because the constraint cloning code
20745 * will.
20746 */
20747 if (trigForm->tgisinternal)
20748 continue;
20749
20750 /*
20751 * Complain if we find an unexpected trigger type.
20752 */
20753 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20754 !TRIGGER_FOR_AFTER(trigForm->tgtype))
20755 elog(ERROR, "unexpected trigger \"%s\" found",
20756 NameStr(trigForm->tgname));
20757
20758 /* Use short-lived context for CREATE TRIGGER */
20759 oldcxt = MemoryContextSwitchTo(perTupCxt);
20760
20761 /*
20762 * If there is a WHEN clause, generate a 'cooked' version of it that's
20763 * appropriate for the partition.
20764 */
20765 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20766 RelationGetDescr(pg_trigger), &isnull);
20767 if (!isnull)
20768 {
20770 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20771 partition, parent);
20772 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20773 partition, parent);
20774 }
20775
20776 /*
20777 * If there is a column list, transform it to a list of column names.
20778 * Note we don't need to map this list in any way ...
20779 */
20780 if (trigForm->tgattr.dim1 > 0)
20781 {
20782 int i;
20783
20784 for (i = 0; i < trigForm->tgattr.dim1; i++)
20785 {
20787
20788 col = TupleDescAttr(parent->rd_att,
20789 trigForm->tgattr.values[i] - 1);
20790 cols = lappend(cols,
20791 makeString(pstrdup(NameStr(col->attname))));
20792 }
20793 }
20794
20795 /* Reconstruct trigger arguments list. */
20796 if (trigForm->tgnargs > 0)
20797 {
20798 char *p;
20799
20800 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20801 RelationGetDescr(pg_trigger), &isnull);
20802 if (isnull)
20803 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20804 NameStr(trigForm->tgname), RelationGetRelationName(partition));
20805
20806 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20807
20808 for (int i = 0; i < trigForm->tgnargs; i++)
20809 {
20810 trigargs = lappend(trigargs, makeString(pstrdup(p)));
20811 p += strlen(p) + 1;
20812 }
20813 }
20814
20815 trigStmt = makeNode(CreateTrigStmt);
20816 trigStmt->replace = false;
20817 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20818 trigStmt->trigname = NameStr(trigForm->tgname);
20819 trigStmt->relation = NULL;
20820 trigStmt->funcname = NULL; /* passed separately */
20821 trigStmt->args = trigargs;
20822 trigStmt->row = true;
20823 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20824 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20825 trigStmt->columns = cols;
20826 trigStmt->whenClause = NULL; /* passed separately */
20827 trigStmt->transitionRels = NIL; /* not supported at present */
20828 trigStmt->deferrable = trigForm->tgdeferrable;
20829 trigStmt->initdeferred = trigForm->tginitdeferred;
20830 trigStmt->constrrel = NULL; /* passed separately */
20831
20832 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20833 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20834 trigForm->tgfoid, trigForm->oid, qual,
20835 false, true, trigForm->tgenabled);
20836
20837 MemoryContextSwitchTo(oldcxt);
20838 MemoryContextReset(perTupCxt);
20839 }
20840
20841 MemoryContextDelete(perTupCxt);
20842
20843 systable_endscan(scan);
20844 table_close(pg_trigger, RowExclusiveLock);
20845}
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define DatumGetByteaPP(X)
Definition: fmgr.h:291
static struct @166 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:3120
List * transitionRels
Definition: parsenodes.h:3122
RangeVar * constrrel
Definition: parsenodes.h:3126
RangeVar * relation
Definition: parsenodes.h:3111
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 19751 of file tablecmds.c.

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

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

Referenced by DefineRelation().

◆ ConstraintImpliedByRelConstraint()

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

Definition at line 20070 of file tablecmds.c.

20071{
20072 List *existConstraint = list_copy(provenConstraint);
20073 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20074 int num_check,
20075 i;
20076
20077 num_check = (constr != NULL) ? constr->num_check : 0;
20078 for (i = 0; i < num_check; i++)
20079 {
20080 Node *cexpr;
20081
20082 /*
20083 * If this constraint hasn't been fully validated yet, we must ignore
20084 * it here.
20085 */
20086 if (!constr->check[i].ccvalid)
20087 continue;
20088
20089 /*
20090 * NOT ENFORCED constraints are always marked as invalid, which should
20091 * have been ignored.
20092 */
20093 Assert(constr->check[i].ccenforced);
20094
20095 cexpr = stringToNode(constr->check[i].ccbin);
20096
20097 /*
20098 * Run each expression through const-simplification and
20099 * canonicalization. It is necessary, because we will be comparing it
20100 * to similarly-processed partition constraint expressions, and may
20101 * fail to detect valid matches without this.
20102 */
20103 cexpr = eval_const_expressions(NULL, cexpr);
20104 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20105
20106 existConstraint = list_concat(existConstraint,
20107 make_ands_implicit((Expr *) cexpr));
20108 }
20109
20110 /*
20111 * Try to make the proof. Since we are comparing CHECK constraints, we
20112 * need to use weak implication, i.e., we assume existConstraint is
20113 * not-false and try to prove the same for testConstraint.
20114 *
20115 * Note that predicate_implied_by assumes its first argument is known
20116 * immutable. That should always be true for both NOT NULL and partition
20117 * constraints, so we don't test it here.
20118 */
20119 return predicate_implied_by(testConstraint, existConstraint, true);
20120}
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 17437 of file tablecmds.c.

17438{
17441
17442 if (acon->condeferrable != bcon->condeferrable ||
17443 acon->condeferred != bcon->condeferred ||
17444 strcmp(decompile_conbin(a, tupleDesc),
17445 decompile_conbin(b, tupleDesc)) != 0)
17446 return false;
17447 else
17448 return true;
17449}
int b
Definition: isn.c:74
int a
Definition: isn.c:73
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:17409

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

13765{
13766 ObjectAddress trigAddress;
13767 CreateTrigStmt *fk_trigger;
13768
13769 /*
13770 * Note: for a self-referential FK (referencing and referenced tables are
13771 * the same), it is important that the ON UPDATE action fires before the
13772 * CHECK action, since both triggers will fire on the same row during an
13773 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13774 * state of the row. Triggers fire in name order, so we ensure this by
13775 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13776 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13777 */
13778 fk_trigger = makeNode(CreateTrigStmt);
13779 fk_trigger->replace = false;
13780 fk_trigger->isconstraint = true;
13781 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13782 fk_trigger->relation = NULL;
13783
13784 /* Either ON INSERT or ON UPDATE */
13785 if (on_insert)
13786 {
13787 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13788 fk_trigger->events = TRIGGER_TYPE_INSERT;
13789 }
13790 else
13791 {
13792 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13793 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13794 }
13795
13796 fk_trigger->args = NIL;
13797 fk_trigger->row = true;
13798 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13799 fk_trigger->columns = NIL;
13800 fk_trigger->whenClause = NULL;
13801 fk_trigger->transitionRels = NIL;
13802 fk_trigger->deferrable = fkconstraint->deferrable;
13803 fk_trigger->initdeferred = fkconstraint->initdeferred;
13804 fk_trigger->constrrel = NULL;
13805
13806 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13807 constraintOid, indexOid, InvalidOid,
13808 parentTrigOid, NULL, true, false);
13809
13810 /* Make changes-so-far visible */
13812
13813 return trigAddress.objectId;
13814}
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 13825 of file tablecmds.c.

13829{
13830 CreateTrigStmt *fk_trigger;
13831 ObjectAddress trigAddress;
13832
13833 /*
13834 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13835 * DELETE action on the referenced table.
13836 */
13837 fk_trigger = makeNode(CreateTrigStmt);
13838 fk_trigger->replace = false;
13839 fk_trigger->isconstraint = true;
13840 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13841 fk_trigger->relation = NULL;
13842 fk_trigger->args = NIL;
13843 fk_trigger->row = true;
13844 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13845 fk_trigger->events = TRIGGER_TYPE_DELETE;
13846 fk_trigger->columns = NIL;
13847 fk_trigger->whenClause = NULL;
13848 fk_trigger->transitionRels = NIL;
13849 fk_trigger->constrrel = NULL;
13850
13851 switch (fkconstraint->fk_del_action)
13852 {
13854 fk_trigger->deferrable = fkconstraint->deferrable;
13855 fk_trigger->initdeferred = fkconstraint->initdeferred;
13856 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13857 break;
13859 fk_trigger->deferrable = false;
13860 fk_trigger->initdeferred = false;
13861 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13862 break;
13864 fk_trigger->deferrable = false;
13865 fk_trigger->initdeferred = false;
13866 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13867 break;
13869 fk_trigger->deferrable = false;
13870 fk_trigger->initdeferred = false;
13871 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13872 break;
13874 fk_trigger->deferrable = false;
13875 fk_trigger->initdeferred = false;
13876 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13877 break;
13878 default:
13879 elog(ERROR, "unrecognized FK action type: %d",
13880 (int) fkconstraint->fk_del_action);
13881 break;
13882 }
13883
13884 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13885 constraintOid, indexOid, InvalidOid,
13886 parentDelTrigger, NULL, true, false);
13887 if (deleteTrigOid)
13888 *deleteTrigOid = trigAddress.objectId;
13889
13890 /* Make changes-so-far visible */
13892
13893 /*
13894 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13895 * UPDATE action on the referenced table.
13896 */
13897 fk_trigger = makeNode(CreateTrigStmt);
13898 fk_trigger->replace = false;
13899 fk_trigger->isconstraint = true;
13900 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13901 fk_trigger->relation = NULL;
13902 fk_trigger->args = NIL;
13903 fk_trigger->row = true;
13904 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13905 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13906 fk_trigger->columns = NIL;
13907 fk_trigger->whenClause = NULL;
13908 fk_trigger->transitionRels = NIL;
13909 fk_trigger->constrrel = NULL;
13910
13911 switch (fkconstraint->fk_upd_action)
13912 {
13914 fk_trigger->deferrable = fkconstraint->deferrable;
13915 fk_trigger->initdeferred = fkconstraint->initdeferred;
13916 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13917 break;
13919 fk_trigger->deferrable = false;
13920 fk_trigger->initdeferred = false;
13921 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13922 break;
13924 fk_trigger->deferrable = false;
13925 fk_trigger->initdeferred = false;
13926 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13927 break;
13929 fk_trigger->deferrable = false;
13930 fk_trigger->initdeferred = false;
13931 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13932 break;
13934 fk_trigger->deferrable = false;
13935 fk_trigger->initdeferred = false;
13936 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13937 break;
13938 default:
13939 elog(ERROR, "unrecognized FK action type: %d",
13940 (int) fkconstraint->fk_upd_action);
13941 break;
13942 }
13943
13944 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13945 constraintOid, indexOid, InvalidOid,
13946 parentUpdTrigger, NULL, true, false);
13947 if (updateTrigOid)
13948 *updateTrigOid = trigAddress.objectId;
13949}
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2818

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

13965{
13966 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13967 constraintOid, indexOid,
13968 parentInsTrigger, true);
13969 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13970 constraintOid, indexOid,
13971 parentUpdTrigger, false);
13972}
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:13762

References CreateFKCheckTrigger().

Referenced by addFkRecurseReferencing(), and ATExecAlterConstrEnforceability().

◆ CreateInheritance()

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

Definition at line 17340 of file tablecmds.c.

17341{
17342 Relation catalogRelation;
17343 SysScanDesc scan;
17345 HeapTuple inheritsTuple;
17346 int32 inhseqno;
17347
17348 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17349 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17350
17351 /*
17352 * Check for duplicates in the list of parents, and determine the highest
17353 * inhseqno already present; we'll use the next one for the new parent.
17354 * Also, if proposed child is a partition, it cannot already be
17355 * inheriting.
17356 *
17357 * Note: we do not reject the case where the child already inherits from
17358 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17359 */
17361 Anum_pg_inherits_inhrelid,
17362 BTEqualStrategyNumber, F_OIDEQ,
17364 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17365 true, NULL, 1, &key);
17366
17367 /* inhseqno sequences start at 1 */
17368 inhseqno = 0;
17369 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17370 {
17371 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17372
17373 if (inh->inhparent == RelationGetRelid(parent_rel))
17374 ereport(ERROR,
17375 (errcode(ERRCODE_DUPLICATE_TABLE),
17376 errmsg("relation \"%s\" would be inherited from more than once",
17377 RelationGetRelationName(parent_rel))));
17378
17379 if (inh->inhseqno > inhseqno)
17380 inhseqno = inh->inhseqno;
17381 }
17382 systable_endscan(scan);
17383
17384 /* Match up the columns and bump attinhcount as needed */
17385 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17386
17387 /* Match up the constraints and bump coninhcount as needed */
17388 MergeConstraintsIntoExisting(child_rel, parent_rel);
17389
17390 /*
17391 * OK, it looks valid. Make the catalog entries that show inheritance.
17392 */
17394 RelationGetRelid(parent_rel),
17395 inhseqno + 1,
17396 catalogRelation,
17397 parent_rel->rd_rel->relkind ==
17398 RELKIND_PARTITIONED_TABLE);
17399
17400 /* Now we're done with pg_inherits */
17401 table_close(catalogRelation, RowExclusiveLock);
17402}
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:17466
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3558
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17604

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

17410{
17412 bool isnull;
17413 Datum attr;
17414 Datum expr;
17415
17416 con = (Form_pg_constraint) GETSTRUCT(contup);
17417 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17418 if (isnull)
17419 elog(ERROR, "null conbin for constraint %u", con->oid);
17420
17421 expr = DirectFunctionCall2(pg_get_expr, attr,
17422 ObjectIdGetDatum(con->conrelid));
17423 return TextDatumGetCString(expr);
17424}
#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 765 of file tablecmds.c.

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

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

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

◆ DetachAddConstraintIfNeeded()

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

Definition at line 21414 of file tablecmds.c.

21415{
21416 List *constraintExpr;
21417
21418 constraintExpr = RelationGetPartitionQual(partRel);
21419 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
21420
21421 /*
21422 * Avoid adding a new constraint if the needed constraint is implied by an
21423 * existing constraint
21424 */
21425 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
21426 {
21427 AlteredTableInfo *tab;
21428 Constraint *n;
21429
21430 tab = ATGetQueueEntry(wqueue, partRel);
21431
21432 /* Add constraint on partition, equivalent to the partition constraint */
21433 n = makeNode(Constraint);
21434 n->contype = CONSTR_CHECK;
21435 n->conname = NULL;
21436 n->location = -1;
21437 n->is_no_inherit = false;
21438 n->raw_expr = NULL;
21439 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
21440 n->is_enforced = true;
21441 n->initially_valid = true;
21442 n->skip_validation = true;
21443 /* It's a re-add, since it nominally already exists */
21444 ATAddCheckNNConstraint(wqueue, tab, partRel, n,
21445 true, false, true, ShareUpdateExclusiveLock);
21446 }
21447}
char * nodeToString(const void *obj)
Definition: outfuncs.c:805
char * cooked_expr
Definition: parsenodes.h:2842
Node * raw_expr
Definition: parsenodes.h:2840
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:20015

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

Referenced by ATExecDetachPartition().

◆ DetachPartitionFinalize()

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

Definition at line 21045 of file tablecmds.c.

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

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

18132{
18133 Relation catalogRelation;
18134 SysScanDesc scan;
18135 ScanKeyData key[3];
18136 HeapTuple depTuple;
18137
18138 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18139
18140 ScanKeyInit(&key[0],
18141 Anum_pg_depend_classid,
18142 BTEqualStrategyNumber, F_OIDEQ,
18143 ObjectIdGetDatum(RelationRelationId));
18144 ScanKeyInit(&key[1],
18145 Anum_pg_depend_objid,
18146 BTEqualStrategyNumber, F_OIDEQ,
18147 ObjectIdGetDatum(relid));
18148 ScanKeyInit(&key[2],
18149 Anum_pg_depend_objsubid,
18150 BTEqualStrategyNumber, F_INT4EQ,
18151 Int32GetDatum(0));
18152
18153 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18154 NULL, 3, key);
18155
18156 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18157 {
18158 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18159
18160 if (dep->refclassid == refclassid &&
18161 dep->refobjid == refobjid &&
18162 dep->refobjsubid == 0 &&
18163 dep->deptype == deptype)
18164 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18165 }
18166
18167 systable_endscan(scan);
18168 table_close(catalogRelation, RowExclusiveLock);
18169}

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

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

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

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

1455{
1456 const struct dropmsgstrings *rentry;
1457
1458 if (rel->schemaname != NULL &&
1460 {
1461 if (!missing_ok)
1462 {
1463 ereport(ERROR,
1464 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1465 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1466 }
1467 else
1468 {
1470 (errmsg("schema \"%s\" does not exist, skipping",
1471 rel->schemaname)));
1472 }
1473 return;
1474 }
1475
1476 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1477 {
1478 if (rentry->kind == rightkind)
1479 {
1480 if (!missing_ok)
1481 {
1482 ereport(ERROR,
1483 (errcode(rentry->nonexistent_code),
1484 errmsg(rentry->nonexistent_msg, rel->relname)));
1485 }
1486 else
1487 {
1488 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1489 break;
1490 }
1491 }
1492 }
1493
1494 Assert(rentry->kind != '\0'); /* Should be impossible */
1495}
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 1502 of file tablecmds.c.

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

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

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

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

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

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

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

Referenced by apply_handle_truncate(), and ExecuteTruncate().

◆ find_composite_type_dependencies()

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

Definition at line 6929 of file tablecmds.c.

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

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

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

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

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

References i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 13604 of file tablecmds.c.

13605{
13606 CoercionPathType ret;
13607
13608 if (targetTypeId == sourceTypeId)
13609 {
13611 *funcid = InvalidOid;
13612 }
13613 else
13614 {
13615 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13616 COERCION_IMPLICIT, funcid);
13617 if (ret == COERCION_PATH_NONE)
13618 /* A previously-relied-upon cast is now gone. */
13619 elog(ERROR, "could not find cast from %u to %u",
13620 sourceTypeId, targetTypeId);
13621 }
13622
13623 return ret;
13624}
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 21995 of file tablecmds.c.

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

22034{
22035 char cstorage = 0;
22036
22037 if (pg_strcasecmp(storagemode, "plain") == 0)
22038 cstorage = TYPSTORAGE_PLAIN;
22039 else if (pg_strcasecmp(storagemode, "external") == 0)
22040 cstorage = TYPSTORAGE_EXTERNAL;
22041 else if (pg_strcasecmp(storagemode, "extended") == 0)
22042 cstorage = TYPSTORAGE_EXTENDED;
22043 else if (pg_strcasecmp(storagemode, "main") == 0)
22044 cstorage = TYPSTORAGE_MAIN;
22045 else if (pg_strcasecmp(storagemode, "default") == 0)
22046 cstorage = get_typstorage(atttypid);
22047 else
22048 ereport(ERROR,
22049 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22050 errmsg("invalid storage type \"%s\"",
22051 storagemode)));
22052
22053 /*
22054 * safety check: do not allow toasted storage modes unless column datatype
22055 * is TOAST-aware.
22056 */
22057 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22058 ereport(ERROR,
22059 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22060 errmsg("column data type %s can only have storage PLAIN",
22061 format_type_be(atttypid))));
22062
22063 return cstorage;
22064}
char get_typstorage(Oid typid)
Definition: lsyscache.c:2586
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36

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

Referenced by ATExecSetStorage(), and BuildDescForRelation().

◆ GetForeignKeyActionTriggers()

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

Definition at line 12039 of file tablecmds.c.

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

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

21894{
21895 Relation pg_constraint;
21896 HeapTuple tuple;
21897 SysScanDesc scan;
21898 ScanKeyData key[2];
21899 List *constraints = NIL;
21900
21901 /*
21902 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21903 * scan.
21904 */
21905 if (RelationGetIndexList(partition) == NIL ||
21908 return NIL;
21909
21910 /* Search for constraints referencing this table */
21911 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21912 ScanKeyInit(&key[0],
21913 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21914 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21915 ScanKeyInit(&key[1],
21916 Anum_pg_constraint_contype, BTEqualStrategyNumber,
21917 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21918
21919 /* XXX This is a seqscan, as we don't have a usable index */
21920 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21921 while ((tuple = systable_getnext(scan)) != NULL)
21922 {
21923 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21924
21925 /*
21926 * We only need to process constraints that are part of larger ones.
21927 */
21928 if (!OidIsValid(constrForm->conparentid))
21929 continue;
21930
21931 constraints = lappend_oid(constraints, constrForm->oid);
21932 }
21933
21934 systable_endscan(scan);
21935 table_close(pg_constraint, AccessShareLock);
21936
21937 return constraints;
21938}
#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 17115 of file tablecmds.c.

17116{
17117 SMgrRelation dstrel;
17118
17119 /*
17120 * Since we copy the file directly without looking at the shared buffers,
17121 * we'd better first flush out any pages of the source relation that are
17122 * in shared buffers. We assume no new changes will be made while we are
17123 * holding exclusive lock on the rel.
17124 */
17126
17127 /*
17128 * Create and copy all forks of the relation, and schedule unlinking of
17129 * old physical files.
17130 *
17131 * NOTE: any conflict in relfilenumber value will be caught in
17132 * RelationCreateStorage().
17133 */
17134 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17135
17136 /* copy main fork */
17138 rel->rd_rel->relpersistence);
17139
17140 /* copy those extra forks that exist */
17141 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17142 forkNum <= MAX_FORKNUM; forkNum++)
17143 {
17144 if (smgrexists(RelationGetSmgr(rel), forkNum))
17145 {
17146 smgrcreate(dstrel, forkNum, false);
17147
17148 /*
17149 * WAL log creation if the relation is persistent, or this is the
17150 * init fork of an unlogged relation.
17151 */
17152 if (RelationIsPermanent(rel) ||
17153 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17154 forkNum == INIT_FORKNUM))
17155 log_smgrcreate(&newrlocator, forkNum);
17156 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17157 rel->rd_rel->relpersistence);
17158 }
17159 }
17160
17161 /* drop old relation, and close new one */
17163 smgrclose(dstrel);
17164}
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:4908
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:576
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 17833 of file tablecmds.c.

17834{
17835 Relation catalogRelation;
17836 SysScanDesc scan;
17838 HeapTuple inheritsTuple;
17839 bool found = false;
17840
17841 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17842
17843 /*
17844 * Find pg_inherits entries by inhparent. (We need to scan them all in
17845 * order to verify that no other partition is pending detach.)
17846 */
17847 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17849 Anum_pg_inherits_inhparent,
17850 BTEqualStrategyNumber, F_OIDEQ,
17851 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17852 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17853 true, NULL, 1, &key);
17854
17855 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17856 {
17857 Form_pg_inherits inhForm;
17858
17859 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17860 if (inhForm->inhdetachpending)
17861 ereport(ERROR,
17862 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17863 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17864 get_rel_name(inhForm->inhrelid),
17865 get_namespace_name(parent_rel->rd_rel->relnamespace),
17866 RelationGetRelationName(parent_rel)),
17867 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17868
17869 if (inhForm->inhrelid == RelationGetRelid(child_rel))
17870 {
17871 HeapTuple newtup;
17872
17873 newtup = heap_copytuple(inheritsTuple);
17874 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17875
17876 CatalogTupleUpdate(catalogRelation,
17877 &inheritsTuple->t_self,
17878 newtup);
17879 found = true;
17880 heap_freetuple(newtup);
17881 /* keep looking, to ensure we catch others pending detach */
17882 }
17883 }
17884
17885 /* Done */
17886 systable_endscan(scan);
17887 table_close(catalogRelation, RowExclusiveLock);
17888
17889 if (!found)
17890 ereport(ERROR,
17892 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17893 RelationGetRelationName(child_rel),
17894 RelationGetRelationName(parent_rel))));
17895}
#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 2540 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

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

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

20017{
20018 List *existConstraint = NIL;
20019 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20020 int i;
20021
20022 if (constr && constr->has_not_null)
20023 {
20024 int natts = scanrel->rd_att->natts;
20025
20026 for (i = 1; i <= natts; i++)
20027 {
20028 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20029
20030 /* invalid not-null constraint must be ignored here */
20031 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20032 {
20033 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20034 NullTest *ntest = makeNode(NullTest);
20035
20036 ntest->arg = (Expr *) makeVar(1,
20037 i,
20038 wholeatt->atttypid,
20039 wholeatt->atttypmod,
20040 wholeatt->attcollation,
20041 0);
20042 ntest->nulltesttype = IS_NOT_NULL;
20043
20044 /*
20045 * argisrow=false is correct even for a composite column,
20046 * because attnotnull does not represent a SQL-spec IS NOT
20047 * NULL test in such a case, just IS DISTINCT FROM NULL.
20048 */
20049 ntest->argisrow = false;
20050 ntest->location = -1;
20051 existConstraint = lappend(existConstraint, ntest);
20052 }
20053 }
20054 }
20055
20056 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20057}

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

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

◆ PreCommit_on_commit_actions()

void PreCommit_on_commit_actions ( void  )

Definition at line 19286 of file tablecmds.c.

19287{
19288 ListCell *l;
19289 List *oids_to_truncate = NIL;
19290 List *oids_to_drop = NIL;
19291
19292 foreach(l, on_commits)
19293 {
19294 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19295
19296 /* Ignore entry if already dropped in this xact */
19298 continue;
19299
19300 switch (oc->oncommit)
19301 {
19302 case ONCOMMIT_NOOP:
19304 /* Do nothing (there shouldn't be such entries, actually) */
19305 break;
19307
19308 /*
19309 * If this transaction hasn't accessed any temporary
19310 * relations, we can skip truncating ON COMMIT DELETE ROWS
19311 * tables, as they must still be empty.
19312 */
19314 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19315 break;
19316 case ONCOMMIT_DROP:
19317 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19318 break;
19319 }
19320 }
19321
19322 /*
19323 * Truncate relations before dropping so that all dependencies between
19324 * relations are removed after they are worked on. Doing it like this
19325 * might be a waste as it is possible that a relation being truncated will
19326 * be dropped anyway due to its parent being dropped, but this makes the
19327 * code more robust because of not having to re-check that the relation
19328 * exists at truncation time.
19329 */
19330 if (oids_to_truncate != NIL)
19331 heap_truncate(oids_to_truncate);
19332
19333 if (oids_to_drop != NIL)
19334 {
19335 ObjectAddresses *targetObjects = new_object_addresses();
19336
19337 foreach(l, oids_to_drop)
19338 {
19339 ObjectAddress object;
19340
19341 object.classId = RelationRelationId;
19342 object.objectId = lfirst_oid(l);
19343 object.objectSubId = 0;
19344
19345 Assert(!object_address_present(&object, targetObjects));
19346
19347 add_exact_object_address(&object, targetObjects);
19348 }
19349
19350 /*
19351 * Object deletion might involve toast table access (to clean up
19352 * toasted catalog entries), so ensure we have a valid snapshot.
19353 */
19355
19356 /*
19357 * Since this is an automatic drop, rather than one directly initiated
19358 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19359 */
19362
19364
19365#ifdef USE_ASSERT_CHECKING
19366
19367 /*
19368 * Note that table deletion will call remove_on_commit_action, so the
19369 * entry should get marked as deleted.
19370 */
19371 foreach(l, on_commits)
19372 {
19373 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19374
19375 if (oc->oncommit != ONCOMMIT_DROP)
19376 continue;
19377
19379 }
19380#endif
19381 }
19382}
#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:136
#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 13085 of file tablecmds.c.

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

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

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

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

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

20136{
20137 /*
20138 * Based on the table's existing constraints, determine whether or not we
20139 * may skip scanning the table.
20140 */
20141 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20142 {
20143 if (!validate_default)
20145 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20146 RelationGetRelationName(scanrel))));
20147 else
20149 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20150 RelationGetRelationName(scanrel))));
20151 return;
20152 }
20153
20154 /*
20155 * Constraints proved insufficient. For plain relations, queue a
20156 * validation item now; for partitioned tables, recurse to process each
20157 * partition.
20158 */
20159 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20160 {
20161 AlteredTableInfo *tab;
20162
20163 /* Grab a work queue entry. */
20164 tab = ATGetQueueEntry(wqueue, scanrel);
20165 Assert(tab->partition_constraint == NULL);
20166 tab->partition_constraint = (Expr *) linitial(partConstraint);
20167 tab->validate_default = validate_default;
20168 }
20169 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20170 {
20171 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20172 int i;
20173
20174 for (i = 0; i < partdesc->nparts; i++)
20175 {
20176 Relation part_rel;
20177 List *thisPartConstraint;
20178
20179 /*
20180 * This is the minimum lock we need to prevent deadlocks.
20181 */
20182 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20183
20184 /*
20185 * Adjust the constraint for scanrel so that it matches this
20186 * partition's attribute numbers.
20187 */
20188 thisPartConstraint =
20189 map_partition_varattnos(partConstraint, 1,
20190 part_rel, scanrel);
20191
20192 QueuePartitionConstraintValidation(wqueue, part_rel,
20193 thisPartConstraint,
20194 validate_default);
20195 table_close(part_rel, NoLock); /* keep lock till commit */
20196 }
20197 }
20198}

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

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

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

21531{
21533 Form_pg_class classform;
21534 HeapTuple tuple;
21535
21536 state = (struct AttachIndexCallbackState *) arg;
21537
21538 if (!state->lockedParentTbl)
21539 {
21540 LockRelationOid(state->parentTblOid, AccessShareLock);
21541 state->lockedParentTbl = true;
21542 }
21543
21544 /*
21545 * If we previously locked some other heap, and the name we're looking up
21546 * no longer refers to an index on that relation, release the now-useless
21547 * lock. XXX maybe we should do *after* we verify whether the index does
21548 * not actually belong to the same relation ...
21549 */
21550 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21551 {
21552 UnlockRelationOid(state->partitionOid, AccessShareLock);
21553 state->partitionOid = InvalidOid;
21554 }
21555
21556 /* Didn't find a relation, so no need for locking or permission checks. */
21557 if (!OidIsValid(relOid))
21558 return;
21559
21560 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21561 if (!HeapTupleIsValid(tuple))
21562 return; /* concurrently dropped, so nothing to do */
21563 classform = (Form_pg_class) GETSTRUCT(tuple);
21564 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21565 classform->relkind != RELKIND_INDEX)
21566 ereport(ERROR,
21567 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21568 errmsg("\"%s\" is not an index", rv->relname)));
21569 ReleaseSysCache(tuple);
21570
21571 /*
21572 * Since we need only examine the heap's tupledesc, an access share lock
21573 * on it (preventing any DDL) is sufficient.
21574 */
21575 state->partitionOid = IndexGetRelation(relOid, false);
21576 LockRelationOid(state->partitionOid, AccessShareLock);
21577}
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 1693 of file tablecmds.c.

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

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

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

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

19498{
19499 HeapTuple tuple;
19500
19501 /* Nothing to do if the relation was not found. */
19502 if (!OidIsValid(relId))
19503 return;
19504
19505 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19506 if (!HeapTupleIsValid(tuple)) /* should not happen */
19507 elog(ERROR, "cache lookup failed for relation %u", relId);
19508
19511
19512 ReleaseSysCache(tuple);
19513}

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

19462{
19463 char relkind;
19464 AclResult aclresult;
19465
19466 /* Nothing to do if the relation was not found. */
19467 if (!OidIsValid(relId))
19468 return;
19469
19470 /*
19471 * If the relation does exist, check whether it's an index. But note that
19472 * the relation might have been dropped between the time we did the name
19473 * lookup and now. In that case, there's nothing to do.
19474 */
19475 relkind = get_rel_relkind(relId);
19476 if (!relkind)
19477 return;
19478 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19479 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19480 ereport(ERROR,
19481 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19482 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19483
19484 /* Check permissions */
19485 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19486 if (aclresult != ACLCHECK_OK)
19487 aclcheck_error(aclresult,
19489 relation->relname);
19490}
#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 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
19533 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19535 relation->relname);
19536
19537 if (!allowSystemTableMods &&
19538 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19539 ereport(ERROR,
19540 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19541 errmsg("permission denied: \"%s\" is a system catalog",
19542 relation->relname)));
19543
19544 ReleaseSysCache(tuple);
19545}

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

15813{
15814 CommentStmt *cmd;
15815 char *comment_str;
15816 AlterTableCmd *newcmd;
15817
15818 /* Look for comment for object wanted, and leave if none */
15819 comment_str = GetComment(objid, ConstraintRelationId, 0);
15820 if (comment_str == NULL)
15821 return;
15822
15823 /* Build CommentStmt node, copying all input data for safety */
15824 cmd = makeNode(CommentStmt);
15825 if (rel)
15826 {
15828 cmd->object = (Node *)
15831 makeString(pstrdup(conname)));
15832 }
15833 else
15834 {
15836 cmd->object = (Node *)
15838 makeString(pstrdup(conname)));
15839 }
15840 cmd->comment = comment_str;
15841
15842 /* Append it to list of commands */
15843 newcmd = makeNode(AlterTableCmd);
15844 newcmd->subtype = AT_ReAddComment;
15845 newcmd->def = (Node *) cmd;
15846 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15847}
TypeName * makeTypeNameFromNameList(List *names)
Definition: makefuncs.c:531
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2364
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2337
#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:3361
ObjectType objtype
Definition: parsenodes.h:3359
Node * object
Definition: parsenodes.h:3360

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

21746{
21747 Oid existingIdx;
21748
21749 existingIdx = index_get_partition(partitionTbl,
21750 RelationGetRelid(parentIdx));
21751 if (OidIsValid(existingIdx))
21752 ereport(ERROR,
21753 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21754 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21755 RelationGetRelationName(partIdx),
21756 RelationGetRelationName(parentIdx)),
21757 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
21758 get_rel_name(existingIdx),
21759 RelationGetRelationName(partitionTbl))));
21760}

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

19228{
19229 OnCommitItem *oc;
19230 MemoryContext oldcxt;
19231
19232 /*
19233 * We needn't bother registering the relation unless there is an ON COMMIT
19234 * action we need to take.
19235 */
19237 return;
19238
19240
19241 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
19242 oc->relid = relid;
19243 oc->oncommit = action;
19246
19247 /*
19248 * We use lcons() here so that ON COMMIT actions are processed in reverse
19249 * order of registration. That might not be essential but it seems
19250 * reasonable.
19251 */
19253
19254 MemoryContextSwitchTo(oldcxt);
19255}
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 18368 of file tablecmds.c.

18370{
18371 Relation pg_index;
18372 Relation pg_class;
18373 HeapTuple pg_class_tuple;
18374 HeapTuple pg_index_tuple;
18375 Form_pg_class pg_class_form;
18376 Form_pg_index pg_index_form;
18377 ListCell *index;
18378
18379 /*
18380 * Check whether relreplident has changed, and update it if so.
18381 */
18382 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18383 pg_class_tuple = SearchSysCacheCopy1(RELOID,
18385 if (!HeapTupleIsValid(pg_class_tuple))
18386 elog(ERROR, "cache lookup failed for relation \"%s\"",
18388 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18389 if (pg_class_form->relreplident != ri_type)
18390 {
18391 pg_class_form->relreplident = ri_type;
18392 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18393 }
18394 table_close(pg_class, RowExclusiveLock);
18395 heap_freetuple(pg_class_tuple);
18396
18397 /*
18398 * Update the per-index indisreplident flags correctly.
18399 */
18400 pg_index = table_open(IndexRelationId, RowExclusiveLock);
18401 foreach(index, RelationGetIndexList(rel))
18402 {
18403 Oid thisIndexOid = lfirst_oid(index);
18404 bool dirty = false;
18405
18406 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18407 ObjectIdGetDatum(thisIndexOid));
18408 if (!HeapTupleIsValid(pg_index_tuple))
18409 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18410 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18411
18412 if (thisIndexOid == indexOid)
18413 {
18414 /* Set the bit if not already set. */
18415 if (!pg_index_form->indisreplident)
18416 {
18417 dirty = true;
18418 pg_index_form->indisreplident = true;
18419 }
18420 }
18421 else
18422 {
18423 /* Unset the bit if set. */
18424 if (pg_index_form->indisreplident)
18425 {
18426 dirty = true;
18427 pg_index_form->indisreplident = false;
18428 }
18429 }
18430
18431 if (dirty)
18432 {
18433 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18434 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18435 InvalidOid, is_internal);
18436
18437 /*
18438 * Invalidate the relcache for the table, so that after we commit
18439 * all sessions will refresh the table's replica identity index
18440 * before attempting any UPDATE or DELETE on the table. (If we
18441 * changed the table's pg_class row above, then a relcache inval
18442 * is already queued due to that; but we might not have.)
18443 */
18445 }
18446 heap_freetuple(pg_index_tuple);
18447 }
18448
18449 table_close(pg_index, RowExclusiveLock);
18450}
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 15011 of file tablecmds.c.

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

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

15253{
15254 if (!get_index_isclustered(indoid))
15255 return;
15256
15257 if (tab->clusterOnIndex)
15258 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15259
15260 tab->clusterOnIndex = get_rel_name(indoid);
15261}
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 15268 of file tablecmds.c.

15269{
15270 /*
15271 * This de-duplication check is critical for two independent reasons: we
15272 * mustn't try to recreate the same constraint twice, and if a constraint
15273 * depends on more than one column whose type is to be altered, we must
15274 * capture its definition string before applying any of the column type
15275 * changes. ruleutils.c will get confused if we ask again later.
15276 */
15277 if (!list_member_oid(tab->changedConstraintOids, conoid))
15278 {
15279 /* OK, capture the constraint's existing definition string */
15280 char *defstring = pg_get_constraintdef_command(conoid);
15281 Oid indoid;
15282
15283 /*
15284 * It is critical to create not-null constraints ahead of primary key
15285 * indexes; otherwise, the not-null constraint would be created by the
15286 * primary key, and the constraint name would be wrong.
15287 */
15288 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15289 {
15290 tab->changedConstraintOids = lcons_oid(conoid,
15292 tab->changedConstraintDefs = lcons(defstring,
15294 }
15295 else
15296 {
15297
15299 conoid);
15301 defstring);
15302 }
15303
15304 /*
15305 * For the index of a constraint, if any, remember if it is used for
15306 * the table's replica identity or if it is a clustered index, so that
15307 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15308 * those properties.
15309 */
15310 indoid = get_constraint_index(conoid);
15311 if (OidIsValid(indoid))
15312 {
15314 RememberClusterOnForRebuilding(indoid, tab);
15315 }
15316 }
15317}
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:15237
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15252

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

15325{
15326 /*
15327 * This de-duplication check is critical for two independent reasons: we
15328 * mustn't try to recreate the same index twice, and if an index depends
15329 * on more than one column whose type is to be altered, we must capture
15330 * its definition string before applying any of the column type changes.
15331 * ruleutils.c will get confused if we ask again later.
15332 */
15333 if (!list_member_oid(tab->changedIndexOids, indoid))
15334 {
15335 /*
15336 * Before adding it as an index-to-rebuild, we'd better see if it
15337 * belongs to a constraint, and if so rebuild the constraint instead.
15338 * Typically this check fails, because constraint indexes normally
15339 * have only dependencies on their constraint. But it's possible for
15340 * such an index to also have direct dependencies on table columns,
15341 * for example with a partial exclusion constraint.
15342 */
15343 Oid conoid = get_index_constraint(indoid);
15344
15345 if (OidIsValid(conoid))
15346 {
15348 }
15349 else
15350 {
15351 /* OK, capture the index's existing definition string */
15352 char *defstring = pg_get_indexdef_string(indoid);
15353
15355 indoid);
15357 defstring);
15358
15359 /*
15360 * Remember if this index is used for the table's replica identity
15361 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15362 * can queue up commands necessary to restore those properties.
15363 */
15365 RememberClusterOnForRebuilding(indoid, tab);
15366 }
15367 }
15368}
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 15237 of file tablecmds.c.

15238{
15239 if (!get_index_isreplident(indoid))
15240 return;
15241
15242 if (tab->replicaIdentityIndex)
15243 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15244
15245 tab->replicaIdentityIndex = get_rel_name(indoid);
15246}
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 15375 of file tablecmds.c.

15376{
15377 /*
15378 * This de-duplication check is critical for two independent reasons: we
15379 * mustn't try to recreate the same statistics object twice, and if the
15380 * statistics object depends on more than one column whose type is to be
15381 * altered, we must capture its definition string before applying any of
15382 * the type changes. ruleutils.c will get confused if we ask again later.
15383 */
15384 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15385 {
15386 /* OK, capture the statistics object's existing definition string */
15387 char *defstring = pg_get_statisticsobjdef_string(stxoid);
15388
15390 stxoid);
15392 defstring);
15393 }
15394}
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 19263 of file tablecmds.c.

19264{
19265 ListCell *l;
19266
19267 foreach(l, on_commits)
19268 {
19269 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19270
19271 if (oc->relid == relid)
19272 {
19274 break;
19275 }
19276 }
19277}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Referenced by MergeChildAttribute(), and MergeInheritedAttribute().

◆ StoreCatalogInheritance()

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

Definition at line 3514 of file tablecmds.c.

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

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

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

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

13297{
13298 ListCell *l;
13299 int attnum;
13300
13301 attnum = 0;
13302 foreach(l, colList)
13303 {
13304 char *attname = strVal(lfirst(l));
13305 HeapTuple atttuple;
13306 Form_pg_attribute attform;
13307
13308 atttuple = SearchSysCacheAttName(relId, attname);
13309 if (!HeapTupleIsValid(atttuple))
13310 ereport(ERROR,
13311 (errcode(ERRCODE_UNDEFINED_COLUMN),
13312 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13313 attname)));
13314 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13315 if (attform->attnum < 0)
13316 ereport(ERROR,
13317 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13318 errmsg("system columns cannot be used in foreign keys")));
13319 if (attnum >= INDEX_MAX_KEYS)
13320 ereport(ERROR,
13321 (errcode(ERRCODE_TOO_MANY_COLUMNS),
13322 errmsg("cannot have more than %d keys in a foreign key",
13323 INDEX_MAX_KEYS)));
13324 attnums[attnum] = attform->attnum;
13325 if (atttypids != NULL)
13326 atttypids[attnum] = attform->atttypid;
13327 if (attcollids != NULL)
13328 attcollids[attnum] = attform->attcollation;
13329 ReleaseSysCache(atttuple);
13330 attnum++;
13331 }
13332
13333 return attnum;
13334}

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

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

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

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

19694{
19695 PartitionSpec *newspec;
19696 ParseState *pstate;
19697 ParseNamespaceItem *nsitem;
19698 ListCell *l;
19699
19700 newspec = makeNode(PartitionSpec);
19701
19702 newspec->strategy = partspec->strategy;
19703 newspec->partParams = NIL;
19704 newspec->location = partspec->location;
19705
19706 /* Check valid number of columns for strategy */
19707 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19708 list_length(partspec->partParams) != 1)
19709 ereport(ERROR,
19710 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19711 errmsg("cannot use \"list\" partition strategy with more than one column")));
19712
19713 /*
19714 * Create a dummy ParseState and insert the target relation as its sole
19715 * rangetable entry. We need a ParseState for transformExpr.
19716 */
19717 pstate = make_parsestate(NULL);
19718 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19719 NULL, false, true);
19720 addNSItemToQuery(pstate, nsitem, true, true, true);
19721
19722 /* take care of any partition expressions */
19723 foreach(l, partspec->partParams)
19724 {
19726
19727 if (pelem->expr)
19728 {
19729 /* Copy, to avoid scribbling on the input */
19730 pelem = copyObject(pelem);
19731
19732 /* Now do parse transformation of the expression */
19733 pelem->expr = transformExpr(pstate, pelem->expr,
19735
19736 /* we have to fix its collations too */
19737 assign_expr_collations(pstate, pelem->expr);
19738 }
19739
19740 newspec->partParams = lappend(newspec->partParams, pelem);
19741 }
19742
19743 return newspec;
19744}
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:117
@ EXPR_KIND_PARTITION_EXPRESSION
Definition: parse_node.h:80
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:899
List * partParams
Definition: parsenodes.h:913
ParseLoc location
Definition: parsenodes.h:914
PartitionStrategy strategy
Definition: parsenodes.h:912

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

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

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

2415{
2416 char *relname = NameStr(reltuple->relname);
2417 AclResult aclresult;
2418
2419 /* Permissions checks */
2420 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2421 if (aclresult != ACLCHECK_OK)
2422 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2423 relname);
2424}
#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 2363 of file tablecmds.c.

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

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

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

15884{
15885 HeapTuple tup;
15886 Datum adatum;
15887 ArrayType *arr;
15888 Oid *rawarr;
15889 int numkeys;
15890 int i;
15891
15892 Assert(con->contype == CONSTR_FOREIGN);
15893 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15894
15895 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15896 if (!HeapTupleIsValid(tup)) /* should not happen */
15897 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15898
15899 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15900 Anum_pg_constraint_conpfeqop);
15901 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15902 numkeys = ARR_DIMS(arr)[0];
15903 /* test follows the one in ri_FetchConstraintInfo() */
15904 if (ARR_NDIM(arr) != 1 ||
15905 ARR_HASNULL(arr) ||
15906 ARR_ELEMTYPE(arr) != OIDOID)
15907 elog(ERROR, "conpfeqop is not a 1-D Oid array");
15908 rawarr = (Oid *) ARR_DATA_PTR(arr);
15909
15910 /* stash a List of the operator Oids in our Constraint node */
15911 for (i = 0; i < numkeys; i++)
15912 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15913
15914 ReleaseSysCache(tup);
15915}
#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 15854 of file tablecmds.c.

15855{
15856 if (CheckIndexCompatible(oldId,
15857 stmt->accessMethod,
15858 stmt->indexParams,
15859 stmt->excludeOpNames,
15860 stmt->iswithoutoverlaps))
15861 {
15862 Relation irel = index_open(oldId, NoLock);
15863
15864 /* If it's a partitioned index, there is no storage to share. */
15865 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15866 {
15867 stmt->oldNumber = irel->rd_locator.relNumber;
15868 stmt->oldCreateSubid = irel->rd_createSubid;
15869 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15870 }
15871 index_close(irel, NoLock);
15872 }
15873}
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 10614 of file tablecmds.c.

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

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

13668{
13669 TupleTableSlot *slot;
13670 TableScanDesc scan;
13671 Trigger trig = {0};
13672 Snapshot snapshot;
13673 MemoryContext oldcxt;
13674 MemoryContext perTupCxt;
13675
13677 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13678
13679 /*
13680 * Build a trigger call structure; we'll need it either way.
13681 */
13682 trig.tgoid = InvalidOid;
13683 trig.tgname = conname;
13685 trig.tgisinternal = true;
13686 trig.tgconstrrelid = RelationGetRelid(pkrel);
13687 trig.tgconstrindid = pkindOid;
13688 trig.tgconstraint = constraintOid;
13689 trig.tgdeferrable = false;
13690 trig.tginitdeferred = false;
13691 /* we needn't fill in remaining fields */
13692
13693 /*
13694 * See if we can do it with a single LEFT JOIN query. A false result
13695 * indicates we must proceed with the fire-the-trigger method. We can't do
13696 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13697 * left joins.
13698 */
13699 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13700 return;
13701
13702 /*
13703 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13704 * if that tuple had just been inserted. If any of those fail, it should
13705 * ereport(ERROR) and that's that.
13706 */
13707 snapshot = RegisterSnapshot(GetLatestSnapshot());
13708 slot = table_slot_create(rel, NULL);
13709 scan = table_beginscan(rel, snapshot, 0, NULL);
13710
13712 "validateForeignKeyConstraint",
13714 oldcxt = MemoryContextSwitchTo(perTupCxt);
13715
13716 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13717 {
13718 LOCAL_FCINFO(fcinfo, 0);
13719 TriggerData trigdata = {0};
13720
13722
13723 /*
13724 * Make a call to the trigger function
13725 *
13726 * No parameters are passed, but we do set a context
13727 */
13728 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13729
13730 /*
13731 * We assume RI_FKey_check_ins won't look at flinfo...
13732 */
13733 trigdata.type = T_TriggerData;
13735 trigdata.tg_relation = rel;
13736 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13737 trigdata.tg_trigslot = slot;
13738 trigdata.tg_trigger = &trig;
13739
13740 fcinfo->context = (Node *) &trigdata;
13741
13742 RI_FKey_check_ins(fcinfo);
13743
13744 MemoryContextReset(perTupCxt);
13745 }
13746
13747 MemoryContextSwitchTo(oldcxt);
13748 MemoryContextDelete(perTupCxt);
13749 table_endscan(scan);
13750 UnregisterSnapshot(snapshot);
13752}
#define MemSet(start, val, len)
Definition: c.h:1020
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 21769 of file tablecmds.c.

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

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

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

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

21872{
21873 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21874 {
21876 iinfo->ii_IndexAttrNumbers[i] - 1);
21877
21878 if (!att->attnotnull)
21879 ereport(ERROR,
21880 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21881 errmsg("invalid primary key definition"),
21882 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21883 NameStr(att->attname),
21884 RelationGetRelationName(partition)));
21885 }
21886}
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