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
 
struct  SplitPartitionContext
 

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
 
typedef struct SplitPartitionContext SplitPartitionContext
 

Enumerations

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

Functions

static void truncate_check_rel (Oid relid, Form_pg_class reltuple)
 
static void truncate_check_perms (Oid relid, Form_pg_class reltuple)
 
static void truncate_check_activity (Relation rel)
 
static void RangeVarCallbackForTruncate (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
static ListMergeAttributes (List *columns, const List *supers, char relpersistence, bool is_partition, List **supconstr, List **supnotnulls)
 
static ListMergeCheckConstraint (List *constraints, const char *name, Node *expr, bool is_enforced)
 
static void MergeChildAttribute (List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
 
static ColumnDefMergeInheritedAttribute (List *inh_columns, int exist_attno, const ColumnDef *newdef)
 
static void MergeAttributesIntoExisting (Relation child_rel, Relation parent_rel, bool ispartition)
 
static void MergeConstraintsIntoExisting (Relation child_rel, Relation parent_rel)
 
static void StoreCatalogInheritance (Oid relationId, List *supers, bool child_is_partition)
 
static void StoreCatalogInheritance1 (Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
 
static int findAttrByName (const char *attributeName, const List *columns)
 
static void AlterIndexNamespaces (Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
 
static void AlterSeqNamespaces (Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
 
static ObjectAddress ATExecAlterConstraint (List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
 
static bool ATExecAlterConstraintInternal (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, LOCKMODE lockmode)
 
static bool ATExecAlterConstrEnforceability (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
 
static bool ATExecAlterConstrDeferrability (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
 
static bool ATExecAlterConstrInheritability (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
 
static void AlterConstrTriggerDeferrability (Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
 
static void AlterConstrEnforceabilityRecurse (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
 
static void AlterConstrDeferrabilityRecurse (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
 
static void AlterConstrUpdateConstraintEntry (ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)
 
static ObjectAddress ATExecValidateConstraint (List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
 
static void QueueFKConstraintValidation (List **wqueue, Relation conrel, Relation fkrel, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
 
static void QueueCheckConstraintValidation (List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
 
static void QueueNNConstraintValidation (List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
 
static int transformColumnNameList (Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
 
static int transformFkeyGetPrimaryKey (Relation pkrel, Oid *indexOid, List **attnamelist, int16 *attnums, Oid *atttypids, Oid *attcollids, Oid *opclasses, bool *pk_has_without_overlaps)
 
static Oid transformFkeyCheckAttrs (Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
 
static void checkFkeyPermissions (Relation rel, int16 *attnums, int natts)
 
static CoercionPathType findFkeyCast (Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
 
static void validateForeignKeyConstraint (char *conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid, bool hasperiod)
 
static void CheckAlterTableIsSafe (Relation rel)
 
static void ATController (AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATPrepCmd (List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATRewriteCatalogs (List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATExecCmd (List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
 
static AlterTableCmdATParseTransformCmd (List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
 
static void ATRewriteTables (AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATRewriteTable (AlteredTableInfo *tab, Oid OIDNewHeap)
 
static AlteredTableInfoATGetQueueEntry (List **wqueue, Relation rel)
 
static void ATSimplePermissions (AlterTableType cmdtype, Relation rel, int allowed_targets)
 
static void ATSimpleRecursion (List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATCheckPartitionsNotInUse (Relation rel, LOCKMODE lockmode)
 
static void ATTypedTableRecursion (List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static Listfind_typed_table_dependencies (Oid typeOid, const char *typeName, DropBehavior behavior)
 
static void ATPrepAddColumn (List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static ObjectAddress ATExecAddColumn (List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd **cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
 
static bool check_for_column_name_collision (Relation rel, const char *colname, bool if_not_exists)
 
static void add_column_datatype_dependency (Oid relid, int32 attnum, Oid typid)
 
static void add_column_collation_dependency (Oid relid, int32 attnum, Oid collid)
 
static ObjectAddress ATExecDropNotNull (Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
 
static void set_attnotnull (List **wqueue, Relation rel, AttrNumber attnum, bool is_valid, bool queue_validation)
 
static ObjectAddress ATExecSetNotNull (List **wqueue, Relation rel, char *conName, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
 
static bool NotNullImpliedByRelConstraints (Relation rel, Form_pg_attribute attr)
 
static bool ConstraintImpliedByRelConstraint (Relation scanrel, List *testConstraint, List *provenConstraint)
 
static ObjectAddress ATExecColumnDefault (Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
 
static ObjectAddress ATExecCookedColumnDefault (Relation rel, AttrNumber attnum, Node *newDefault)
 
static ObjectAddress ATExecAddIdentity (Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
 
static ObjectAddress ATExecSetIdentity (Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
 
static ObjectAddress ATExecDropIdentity (Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
 
static ObjectAddress ATExecSetExpression (AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
 
static void ATPrepDropExpression (Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
 
static ObjectAddress ATExecDropExpression (Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
 
static ObjectAddress ATExecSetStatistics (Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
 
static ObjectAddress ATExecSetOptions (Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
 
static ObjectAddress ATExecSetStorage (Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
 
static void ATPrepDropColumn (List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static ObjectAddress ATExecDropColumn (List **wqueue, Relation rel, const char *colName, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode, ObjectAddresses *addrs)
 
static void ATPrepAddPrimaryKey (List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void verifyNotNullPKCompatible (HeapTuple tuple, const char *colname)
 
static ObjectAddress ATExecAddIndex (AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
 
static ObjectAddress ATExecAddStatistics (AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
 
static ObjectAddress ATExecAddConstraint (List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
 
static char * ChooseForeignKeyConstraintNameAddition (List *colnames)
 
static ObjectAddress ATExecAddIndexConstraint (AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
 
static ObjectAddress ATAddCheckNNConstraint (List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
 
static ObjectAddress ATAddForeignKeyConstraint (List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
 
static int validateFkOnDeleteSetColumns (int numfks, const int16 *fkattnums, int numfksetcols, int16 *fksetcolsattnums, List *fksetcols)
 
static ObjectAddress addFkConstraint (addFkConstraintSides fkside, char *constraintname, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool is_internal, bool with_period)
 
static void addFkRecurseReferenced (Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, Oid parentDelTrigger, Oid parentUpdTrigger, bool with_period)
 
static void addFkRecurseReferencing (List **wqueue, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, LOCKMODE lockmode, Oid parentInsTrigger, Oid parentUpdTrigger, bool with_period)
 
static void CloneForeignKeyConstraints (List **wqueue, Relation parentRel, Relation partitionRel)
 
static void CloneFkReferenced (Relation parentRel, Relation partitionRel)
 
static void CloneFkReferencing (List **wqueue, Relation parentRel, Relation partRel)
 
static void createForeignKeyCheckTriggers (Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
 
static void createForeignKeyActionTriggers (Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
 
static bool tryAttachPartitionForeignKey (List **wqueue, ForeignKeyCacheInfo *fk, Relation partition, Oid parentConstrOid, int numfks, AttrNumber *mapped_conkey, AttrNumber *confkey, Oid *conpfeqop, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
 
static void AttachPartitionForeignKey (List **wqueue, Relation partition, Oid partConstrOid, Oid parentConstrOid, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
 
static void RemoveInheritedConstraint (Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
 
static void DropForeignKeyConstraintTriggers (Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)
 
static void GetForeignKeyActionTriggers (Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)
 
static void GetForeignKeyCheckTriggers (Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
 
static void ATExecDropConstraint (Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
 
static ObjectAddress dropconstraint_internal (Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
 
static void ATPrepAlterColumnType (List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static bool ATColumnChangeRequiresRewrite (Node *expr, AttrNumber varattno)
 
static ObjectAddress ATExecAlterColumnType (AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode)
 
static void RememberAllDependentForRebuilding (AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
 
static void RememberConstraintForRebuilding (Oid conoid, AlteredTableInfo *tab)
 
static void RememberIndexForRebuilding (Oid indoid, AlteredTableInfo *tab)
 
static void RememberStatisticsForRebuilding (Oid stxoid, AlteredTableInfo *tab)
 
static void ATPostAlterTypeCleanup (List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
 
static void ATPostAlterTypeParse (Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
 
static void RebuildConstraintComment (AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
 
static void TryReuseIndex (Oid oldId, IndexStmt *stmt)
 
static void TryReuseForeignKey (Oid oldId, Constraint *con)
 
static ObjectAddress ATExecAlterColumnGenericOptions (Relation rel, const char *colName, List *options, LOCKMODE lockmode)
 
static void change_owner_fix_column_acls (Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
 
static void change_owner_recurse_to_sequences (Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
 
static ObjectAddress ATExecClusterOn (Relation rel, const char *indexName, LOCKMODE lockmode)
 
static void ATExecDropCluster (Relation rel, LOCKMODE lockmode)
 
static void ATPrepSetAccessMethod (AlteredTableInfo *tab, Relation rel, const char *amname)
 
static void ATExecSetAccessMethodNoStorage (Relation rel, Oid newAccessMethodId)
 
static void ATPrepChangePersistence (AlteredTableInfo *tab, Relation rel, bool toLogged)
 
static void ATPrepSetTableSpace (AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
 
static void ATExecSetTableSpace (Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 
static void ATExecSetTableSpaceNoStorage (Relation rel, Oid newTableSpace)
 
static void ATExecSetRelOptions (Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
 
static void ATExecEnableDisableTrigger (Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
 
static void ATExecEnableDisableRule (Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
 
static void ATPrepAddInherit (Relation child_rel)
 
static ObjectAddress ATExecAddInherit (Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
 
static ObjectAddress ATExecDropInherit (Relation rel, RangeVar *parent, LOCKMODE lockmode)
 
static void drop_parent_dependency (Oid relid, Oid refclassid, Oid refobjid, DependencyType deptype)
 
static ObjectAddress ATExecAddOf (Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 
static void ATExecDropOf (Relation rel, LOCKMODE lockmode)
 
static void ATExecReplicaIdentity (Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
 
static void ATExecGenericOptions (Relation rel, List *options)
 
static void ATExecSetRowSecurity (Relation rel, bool rls)
 
static void ATExecForceNoForceRowSecurity (Relation rel, bool force_rls)
 
static ObjectAddress ATExecSetCompression (Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
 
static void index_copy_data (Relation rel, RelFileLocator newrlocator)
 
static const char * storage_name (char c)
 
static void RangeVarCallbackForDropRelation (const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)
 
static void RangeVarCallbackForAlterRelation (const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
 
static PartitionSpectransformPartitionSpec (Relation rel, PartitionSpec *partspec)
 
static void ComputePartitionAttrs (ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, PartitionStrategy strategy)
 
static void CreateInheritance (Relation child_rel, Relation parent_rel, bool ispartition)
 
static void RemoveInheritance (Relation child_rel, Relation parent_rel, bool expect_detached)
 
static ObjectAddress ATExecAttachPartition (List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
 
static void AttachPartitionEnsureIndexes (List **wqueue, Relation rel, Relation attachrel)
 
static void QueuePartitionConstraintValidation (List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
 
static void CloneRowTriggersToPartition (Relation parent, Relation partition)
 
static void DropClonedTriggersFromPartition (Oid partitionId)
 
static ObjectAddress ATExecDetachPartition (List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
 
static void DetachPartitionFinalize (Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
 
static ObjectAddress ATExecDetachPartitionFinalize (Relation rel, RangeVar *name)
 
static ObjectAddress ATExecAttachPartitionIdx (List **wqueue, Relation parentIdx, RangeVar *name)
 
static void validatePartitionedIndex (Relation partedIdx, Relation partedTbl)
 
static void refuseDupeIndexAttach (Relation parentIdx, Relation partIdx, Relation partitionTbl)
 
static void verifyPartitionIndexNotNull (IndexInfo *iinfo, Relation partition)
 
static ListGetParentedForeignKeyRefs (Relation partition)
 
static void ATDetachCheckNoForeignKeyRefs (Relation partition)
 
static char GetAttributeCompression (Oid atttypid, const char *compression)
 
static char GetAttributeStorage (Oid atttypid, const char *storagemode)
 
static void ATExecMergePartitions (List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
 
static void ATExecSplitPartition (List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
 
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 attachPartitionTable (List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
 
static void RangeVarCallbackForAttachIndex (const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
 
static void buildExpressionExecutionStates (AlteredTableInfo *tab, Relation newPartRel, EState *estate)
 
static void evaluateGeneratedExpressionsAndCheckConstraints (AlteredTableInfo *tab, Relation newPartRel, TupleTableSlot *insertslot, ExprContext *econtext)
 
static ListgetAttributesList (Relation parent_rel)
 
static void createTableConstraints (List **wqueue, AlteredTableInfo *tab, Relation parent_rel, Relation newRel)
 
static Relation createPartitionTable (List **wqueue, RangeVar *newPartName, Relation parent_rel, Oid ownerId)
 
static void MergePartitionsMoveRows (List **wqueue, List *mergingPartitions, Relation newPartRel)
 
static void detachPartitionTable (Relation parent_rel, Relation child_rel, Oid defaultPartOid)
 
static SplitPartitionContextcreateSplitPartitionContext (Relation partRel)
 
static void deleteSplitPartitionContext (SplitPartitionContext *pc, List **wqueue, int ti_options)
 
static void SplitPartitionMoveRows (List **wqueue, Relation rel, Relation splitRel, List *partlist, List *newPartRels)
 

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

◆ SplitPartitionContext

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

7746{
7747 ObjectAddress myself,
7748 referenced;
7749
7750 /* We know the default collation is pinned, so don't bother recording it */
7751 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7752 {
7753 myself.classId = RelationRelationId;
7754 myself.objectId = relid;
7755 myself.objectSubId = attnum;
7756 referenced.classId = CollationRelationId;
7757 referenced.objectId = collid;
7758 referenced.objectSubId = 0;
7759 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7760 }
7761}
#define OidIsValid(objectId)
Definition: c.h:788
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 7727 of file tablecmds.c.

7728{
7729 ObjectAddress myself,
7730 referenced;
7731
7732 myself.classId = RelationRelationId;
7733 myself.objectId = relid;
7734 myself.objectSubId = attnum;
7735 referenced.classId = TypeRelationId;
7736 referenced.objectId = typid;
7737 referenced.objectSubId = 0;
7738 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7739}

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

10757{
10758 ObjectAddress address;
10759 Oid constrOid;
10760 char *conname;
10761 bool conislocal;
10762 int16 coninhcount;
10763 bool connoinherit;
10764
10765 /*
10766 * Verify relkind for each referenced partition. At the top level, this
10767 * is redundant with a previous check, but we need it when recursing.
10768 */
10769 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10770 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10771 ereport(ERROR,
10772 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10773 errmsg("referenced relation \"%s\" is not a table",
10774 RelationGetRelationName(pkrel))));
10775
10776 /*
10777 * Caller supplies us with a constraint name; however, it may be used in
10778 * this partition, so come up with a different one in that case. Unless
10779 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10780 * supplied name with an underscore and digit(s) appended.
10781 */
10783 RelationGetRelid(rel),
10784 constraintname))
10785 conname = ChooseConstraintName(constraintname,
10786 NULL,
10787 "",
10789 else
10790 conname = constraintname;
10791
10792 if (fkconstraint->conname == NULL)
10793 fkconstraint->conname = pstrdup(conname);
10794
10795 if (OidIsValid(parentConstr))
10796 {
10797 conislocal = false;
10798 coninhcount = 1;
10799 connoinherit = false;
10800 }
10801 else
10802 {
10803 conislocal = true;
10804 coninhcount = 0;
10805
10806 /*
10807 * always inherit for partitioned tables, never for legacy inheritance
10808 */
10809 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10810 }
10811
10812 /*
10813 * Record the FK constraint in pg_constraint.
10814 */
10815 constrOid = CreateConstraintEntry(conname,
10817 CONSTRAINT_FOREIGN,
10818 fkconstraint->deferrable,
10819 fkconstraint->initdeferred,
10820 fkconstraint->is_enforced,
10821 fkconstraint->initially_valid,
10822 parentConstr,
10823 RelationGetRelid(rel),
10824 fkattnum,
10825 numfks,
10826 numfks,
10827 InvalidOid, /* not a domain constraint */
10828 indexOid,
10829 RelationGetRelid(pkrel),
10830 pkattnum,
10831 pfeqoperators,
10832 ppeqoperators,
10833 ffeqoperators,
10834 numfks,
10835 fkconstraint->fk_upd_action,
10836 fkconstraint->fk_del_action,
10837 fkdelsetcols,
10838 numfkdelsetcols,
10839 fkconstraint->fk_matchtype,
10840 NULL, /* no exclusion constraint */
10841 NULL, /* no check constraint */
10842 NULL,
10843 conislocal, /* islocal */
10844 coninhcount, /* inhcount */
10845 connoinherit, /* conNoInherit */
10846 with_period, /* conPeriod */
10847 is_internal); /* is_internal */
10848
10849 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10850
10851 /*
10852 * In partitioning cases, create the dependency entries for this
10853 * constraint. (For non-partitioned cases, relevant entries were created
10854 * by CreateConstraintEntry.)
10855 *
10856 * On the referenced side, we need the constraint to have an internal
10857 * dependency on its parent constraint; this means that this constraint
10858 * cannot be dropped on its own -- only through the parent constraint. It
10859 * also means the containing partition cannot be dropped on its own, but
10860 * it can be detached, at which point this dependency is removed (after
10861 * verifying that no rows are referenced via this FK.)
10862 *
10863 * When processing the referencing side, we link the constraint via the
10864 * special partitioning dependencies: the parent constraint is the primary
10865 * dependent, and the partition on which the foreign key exists is the
10866 * secondary dependency. That way, this constraint is dropped if either
10867 * of these objects is.
10868 *
10869 * Note that this is only necessary for the subsidiary pg_constraint rows
10870 * in partitions; the topmost row doesn't need any of this.
10871 */
10872 if (OidIsValid(parentConstr))
10873 {
10874 ObjectAddress referenced;
10875
10876 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10877
10878 Assert(fkside != addFkBothSides);
10879 if (fkside == addFkReferencedSide)
10880 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10881 else
10882 {
10883 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10884 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10885 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10886 }
10887 }
10888
10889 /* make new constraint visible, in case we add more */
10891
10892 return address;
10893}
int16_t int16
Definition: c.h:547
@ DEPENDENCY_INTERNAL
Definition: dependency.h:35
@ DEPENDENCY_PARTITION_PRI
Definition: dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition: dependency.h:37
int errcode(int sqlerrcode)
Definition: elog.c:863
int errmsg(const char *fmt,...)
Definition: elog.c:1080
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:150
Assert(PointerIsAligned(start, uint64))
char * pstrdup(const char *in)
Definition: mcxt.c:1759
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isEnforced, bool isValidated, Oid parentConstrId, Oid relId, const int16 *constraintKey, int constraintNKeys, int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, const int16 *fkDeleteSetCols, int numFkDeleteSetCols, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, bool conIsLocal, int16 conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
Definition: pg_constraint.c:51
bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, const char *conname)
char * ChooseConstraintName(const char *name1, const char *name2, const char *label, Oid namespaceid, List *others)
@ CONSTRAINT_RELATION
#define NIL
Definition: pg_list.h:68
#define InvalidOid
Definition: postgres_ext.h:37
unsigned int Oid
Definition: postgres_ext.h:32
#define RelationGetRelid(relation)
Definition: rel.h:515
#define RelationGetRelationName(relation)
Definition: rel.h:549
#define RelationGetNamespace(relation)
Definition: rel.h:556
bool initdeferred
Definition: parsenodes.h:2864
char fk_upd_action
Definition: parsenodes.h:2898
bool is_enforced
Definition: parsenodes.h:2865
char fk_matchtype
Definition: parsenodes.h:2897
bool initially_valid
Definition: parsenodes.h:2867
bool deferrable
Definition: parsenodes.h:2863
char * conname
Definition: parsenodes.h:2862
char fk_del_action
Definition: parsenodes.h:2899
Form_pg_class rd_rel
Definition: rel.h:111
void CommandCounterIncrement(void)
Definition: xact.c:1101

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

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

◆ addFkRecurseReferenced()

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

Definition at line 10928 of file tablecmds.c.

10937{
10938 Oid deleteTriggerOid = InvalidOid,
10939 updateTriggerOid = InvalidOid;
10940
10943
10944 /*
10945 * Create action triggers to enforce the constraint, or skip them if the
10946 * constraint is NOT ENFORCED.
10947 */
10948 if (fkconstraint->is_enforced)
10950 RelationGetRelid(pkrel),
10951 fkconstraint,
10952 parentConstr, indexOid,
10953 parentDelTrigger, parentUpdTrigger,
10954 &deleteTriggerOid, &updateTriggerOid);
10955
10956 /*
10957 * If the referenced table is partitioned, recurse on ourselves to handle
10958 * each partition. We need one pg_constraint row created for each
10959 * partition in addition to the pg_constraint row for the parent table.
10960 */
10961 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10962 {
10963 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10964
10965 for (int i = 0; i < pd->nparts; i++)
10966 {
10967 Relation partRel;
10968 AttrMap *map;
10969 AttrNumber *mapped_pkattnum;
10970 Oid partIndexId;
10971 ObjectAddress address;
10972
10973 /* XXX would it be better to acquire these locks beforehand? */
10974 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10975
10976 /*
10977 * Map the attribute numbers in the referenced side of the FK
10978 * definition to match the partition's column layout.
10979 */
10981 RelationGetDescr(pkrel),
10982 false);
10983 if (map)
10984 {
10985 mapped_pkattnum = palloc_array(AttrNumber, numfks);
10986 for (int j = 0; j < numfks; j++)
10987 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10988 }
10989 else
10990 mapped_pkattnum = pkattnum;
10991
10992 /* Determine the index to use at this level */
10993 partIndexId = index_get_partition(partRel, indexOid);
10994 if (!OidIsValid(partIndexId))
10995 elog(ERROR, "index for %u not found in partition %s",
10996 indexOid, RelationGetRelationName(partRel));
10997
10998 /* Create entry at this level ... */
11000 fkconstraint->conname, fkconstraint, rel,
11001 partRel, partIndexId, parentConstr,
11002 numfks, mapped_pkattnum,
11003 fkattnum, pfeqoperators, ppeqoperators,
11004 ffeqoperators, numfkdelsetcols,
11005 fkdelsetcols, true, with_period);
11006 /* ... and recurse to our children */
11007 addFkRecurseReferenced(fkconstraint, rel, partRel,
11008 partIndexId, address.objectId, numfks,
11009 mapped_pkattnum, fkattnum,
11010 pfeqoperators, ppeqoperators, ffeqoperators,
11011 numfkdelsetcols, fkdelsetcols,
11012 old_check_ok,
11013 deleteTriggerOid, updateTriggerOid,
11014 with_period);
11015
11016 /* Done -- clean up (but keep the lock) */
11017 table_close(partRel, NoLock);
11018 if (map)
11019 {
11020 pfree(mapped_pkattnum);
11021 free_attrmap(map);
11022 }
11023 }
11024 }
11025}
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
#define palloc_array(type, count)
Definition: fe_memutils.h:76
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
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:71
Oid index_get_partition(Relation partition, Oid indexId)
Definition: partition.c:176
#define RelationGetDescr(relation)
Definition: rel.h:541
Definition: attmap.h:35
AttrNumber * attnums
Definition: attmap.h:36
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13885
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:10750
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:10928

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

11074{
11075 Oid insertTriggerOid = InvalidOid,
11076 updateTriggerOid = InvalidOid;
11077
11078 Assert(OidIsValid(parentConstr));
11081
11082 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11083 ereport(ERROR,
11084 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11085 errmsg("foreign key constraints are not supported on foreign tables")));
11086
11087 /*
11088 * Add check triggers if the constraint is ENFORCED, and if needed,
11089 * schedule them to be checked in Phase 3.
11090 *
11091 * If the relation is partitioned, drill down to do it to its partitions.
11092 */
11093 if (fkconstraint->is_enforced)
11095 RelationGetRelid(pkrel),
11096 fkconstraint,
11097 parentConstr,
11098 indexOid,
11099 parentInsTrigger, parentUpdTrigger,
11100 &insertTriggerOid, &updateTriggerOid);
11101
11102 if (rel->rd_rel->relkind == RELKIND_RELATION)
11103 {
11104 /*
11105 * Tell Phase 3 to check that the constraint is satisfied by existing
11106 * rows. We can skip this during table creation, when constraint is
11107 * specified as NOT ENFORCED, or when requested explicitly by
11108 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11109 * recreating a constraint following a SET DATA TYPE operation that
11110 * did not impugn its validity.
11111 */
11112 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11113 fkconstraint->is_enforced)
11114 {
11115 NewConstraint *newcon;
11116 AlteredTableInfo *tab;
11117
11118 tab = ATGetQueueEntry(wqueue, rel);
11119
11120 newcon = palloc0_object(NewConstraint);
11121 newcon->name = get_constraint_name(parentConstr);
11122 newcon->contype = CONSTR_FOREIGN;
11123 newcon->refrelid = RelationGetRelid(pkrel);
11124 newcon->refindid = indexOid;
11125 newcon->conid = parentConstr;
11126 newcon->conwithperiod = fkconstraint->fk_with_period;
11127 newcon->qual = (Node *) fkconstraint;
11128
11129 tab->constraints = lappend(tab->constraints, newcon);
11130 }
11131 }
11132 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11133 {
11135 Relation trigrel;
11136
11137 /*
11138 * Triggers of the foreign keys will be manipulated a bunch of times
11139 * in the loop below. To avoid repeatedly opening/closing the trigger
11140 * catalog relation, we open it here and pass it to the subroutines
11141 * called below.
11142 */
11143 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11144
11145 /*
11146 * Recurse to take appropriate action on each partition; either we
11147 * find an existing constraint to reparent to ours, or we create a new
11148 * one.
11149 */
11150 for (int i = 0; i < pd->nparts; i++)
11151 {
11152 Relation partition = table_open(pd->oids[i], lockmode);
11153 List *partFKs;
11154 AttrMap *attmap;
11155 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11156 bool attached;
11157 ObjectAddress address;
11158
11159 CheckAlterTableIsSafe(partition);
11160
11161 attmap = build_attrmap_by_name(RelationGetDescr(partition),
11162 RelationGetDescr(rel),
11163 false);
11164 for (int j = 0; j < numfks; j++)
11165 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11166
11167 /* Check whether an existing constraint can be repurposed */
11168 partFKs = copyObject(RelationGetFKeyList(partition));
11169 attached = false;
11170 foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11171 {
11173 fk,
11174 partition,
11175 parentConstr,
11176 numfks,
11177 mapped_fkattnum,
11178 pkattnum,
11179 pfeqoperators,
11180 insertTriggerOid,
11181 updateTriggerOid,
11182 trigrel))
11183 {
11184 attached = true;
11185 break;
11186 }
11187 }
11188 if (attached)
11189 {
11190 table_close(partition, NoLock);
11191 continue;
11192 }
11193
11194 /*
11195 * No luck finding a good constraint to reuse; create our own.
11196 */
11198 fkconstraint->conname, fkconstraint,
11199 partition, pkrel, indexOid, parentConstr,
11200 numfks, pkattnum,
11201 mapped_fkattnum, pfeqoperators,
11202 ppeqoperators, ffeqoperators,
11203 numfkdelsetcols, fkdelsetcols, true,
11204 with_period);
11205
11206 /* call ourselves to finalize the creation and we're done */
11207 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11208 indexOid,
11209 address.objectId,
11210 numfks,
11211 pkattnum,
11212 mapped_fkattnum,
11213 pfeqoperators,
11214 ppeqoperators,
11215 ffeqoperators,
11216 numfkdelsetcols,
11217 fkdelsetcols,
11218 old_check_ok,
11219 lockmode,
11220 insertTriggerOid,
11221 updateTriggerOid,
11222 with_period);
11223
11224 table_close(partition, NoLock);
11225 }
11226
11227 table_close(trigrel, RowExclusiveLock);
11228 }
11229}
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:175
#define palloc0_object(type)
Definition: fe_memutils.h:75
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:1172
#define copyObject(obj)
Definition: nodes.h:232
@ CONSTR_FOREIGN
Definition: parsenodes.h:2837
#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:2895
bool skip_validation
Definition: parsenodes.h:2866
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:6586
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:11066
static void CheckAlterTableIsSafe(Relation rel)
Definition: tablecmds.c:4446
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:14020
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:11722

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

6621{
6622 switch (cmdtype)
6623 {
6624 case AT_AddColumn:
6625 case AT_AddColumnToView:
6626 return "ADD COLUMN";
6627 case AT_ColumnDefault:
6629 return "ALTER COLUMN ... SET DEFAULT";
6630 case AT_DropNotNull:
6631 return "ALTER COLUMN ... DROP NOT NULL";
6632 case AT_SetNotNull:
6633 return "ALTER COLUMN ... SET NOT NULL";
6634 case AT_SetExpression:
6635 return "ALTER COLUMN ... SET EXPRESSION";
6636 case AT_DropExpression:
6637 return "ALTER COLUMN ... DROP EXPRESSION";
6638 case AT_SetStatistics:
6639 return "ALTER COLUMN ... SET STATISTICS";
6640 case AT_SetOptions:
6641 return "ALTER COLUMN ... SET";
6642 case AT_ResetOptions:
6643 return "ALTER COLUMN ... RESET";
6644 case AT_SetStorage:
6645 return "ALTER COLUMN ... SET STORAGE";
6646 case AT_SetCompression:
6647 return "ALTER COLUMN ... SET COMPRESSION";
6648 case AT_DropColumn:
6649 return "DROP COLUMN";
6650 case AT_AddIndex:
6651 case AT_ReAddIndex:
6652 return NULL; /* not real grammar */
6653 case AT_AddConstraint:
6654 case AT_ReAddConstraint:
6657 return "ADD CONSTRAINT";
6658 case AT_AlterConstraint:
6659 return "ALTER CONSTRAINT";
6661 return "VALIDATE CONSTRAINT";
6662 case AT_DropConstraint:
6663 return "DROP CONSTRAINT";
6664 case AT_ReAddComment:
6665 return NULL; /* not real grammar */
6666 case AT_AlterColumnType:
6667 return "ALTER COLUMN ... SET DATA TYPE";
6669 return "ALTER COLUMN ... OPTIONS";
6670 case AT_ChangeOwner:
6671 return "OWNER TO";
6672 case AT_ClusterOn:
6673 return "CLUSTER ON";
6674 case AT_DropCluster:
6675 return "SET WITHOUT CLUSTER";
6676 case AT_SetAccessMethod:
6677 return "SET ACCESS METHOD";
6678 case AT_SetLogged:
6679 return "SET LOGGED";
6680 case AT_SetUnLogged:
6681 return "SET UNLOGGED";
6682 case AT_DropOids:
6683 return "SET WITHOUT OIDS";
6684 case AT_SetTableSpace:
6685 return "SET TABLESPACE";
6686 case AT_SetRelOptions:
6687 return "SET";
6688 case AT_ResetRelOptions:
6689 return "RESET";
6691 return NULL; /* not real grammar */
6692 case AT_EnableTrig:
6693 return "ENABLE TRIGGER";
6695 return "ENABLE ALWAYS TRIGGER";
6697 return "ENABLE REPLICA TRIGGER";
6698 case AT_DisableTrig:
6699 return "DISABLE TRIGGER";
6700 case AT_EnableTrigAll:
6701 return "ENABLE TRIGGER ALL";
6702 case AT_DisableTrigAll:
6703 return "DISABLE TRIGGER ALL";
6704 case AT_EnableTrigUser:
6705 return "ENABLE TRIGGER USER";
6706 case AT_DisableTrigUser:
6707 return "DISABLE TRIGGER USER";
6708 case AT_EnableRule:
6709 return "ENABLE RULE";
6711 return "ENABLE ALWAYS RULE";
6713 return "ENABLE REPLICA RULE";
6714 case AT_DisableRule:
6715 return "DISABLE RULE";
6716 case AT_AddInherit:
6717 return "INHERIT";
6718 case AT_DropInherit:
6719 return "NO INHERIT";
6720 case AT_AddOf:
6721 return "OF";
6722 case AT_DropOf:
6723 return "NOT OF";
6724 case AT_ReplicaIdentity:
6725 return "REPLICA IDENTITY";
6727 return "ENABLE ROW SECURITY";
6729 return "DISABLE ROW SECURITY";
6731 return "FORCE ROW SECURITY";
6733 return "NO FORCE ROW SECURITY";
6734 case AT_GenericOptions:
6735 return "OPTIONS";
6736 case AT_AttachPartition:
6737 return "ATTACH PARTITION";
6738 case AT_DetachPartition:
6739 return "DETACH PARTITION";
6741 return "DETACH PARTITION ... FINALIZE";
6742 case AT_MergePartitions:
6743 return "MERGE PARTITIONS";
6744 case AT_SplitPartition:
6745 return "SPLIT PARTITION";
6746 case AT_AddIdentity:
6747 return "ALTER COLUMN ... ADD IDENTITY";
6748 case AT_SetIdentity:
6749 return "ALTER COLUMN ... SET";
6750 case AT_DropIdentity:
6751 return "ALTER COLUMN ... DROP IDENTITY";
6752 case AT_ReAddStatistics:
6753 return NULL; /* not real grammar */
6754 }
6755
6756 return NULL;
6757}
@ AT_AddIndexConstraint
Definition: parsenodes.h:2464
@ AT_MergePartitions
Definition: parsenodes.h:2506
@ AT_DropOf
Definition: parsenodes.h:2495
@ AT_SetOptions
Definition: parsenodes.h:2452
@ AT_DropIdentity
Definition: parsenodes.h:2509
@ AT_DisableTrigUser
Definition: parsenodes.h:2487
@ AT_DropNotNull
Definition: parsenodes.h:2447
@ AT_AddOf
Definition: parsenodes.h:2494
@ AT_ResetOptions
Definition: parsenodes.h:2453
@ AT_ReplicaIdentity
Definition: parsenodes.h:2496
@ AT_ReplaceRelOptions
Definition: parsenodes.h:2479
@ AT_EnableRowSecurity
Definition: parsenodes.h:2497
@ AT_AddColumnToView
Definition: parsenodes.h:2444
@ AT_ResetRelOptions
Definition: parsenodes.h:2478
@ AT_EnableReplicaTrig
Definition: parsenodes.h:2482
@ AT_DropOids
Definition: parsenodes.h:2474
@ AT_SetIdentity
Definition: parsenodes.h:2508
@ AT_ReAddStatistics
Definition: parsenodes.h:2510
@ AT_SetUnLogged
Definition: parsenodes.h:2473
@ AT_DisableTrig
Definition: parsenodes.h:2483
@ AT_SetCompression
Definition: parsenodes.h:2455
@ AT_DropExpression
Definition: parsenodes.h:2450
@ AT_AddIndex
Definition: parsenodes.h:2457
@ AT_EnableReplicaRule
Definition: parsenodes.h:2490
@ AT_ReAddIndex
Definition: parsenodes.h:2458
@ AT_DropConstraint
Definition: parsenodes.h:2465
@ AT_SetNotNull
Definition: parsenodes.h:2448
@ AT_ClusterOn
Definition: parsenodes.h:2470
@ AT_AddIdentity
Definition: parsenodes.h:2507
@ AT_ForceRowSecurity
Definition: parsenodes.h:2499
@ AT_EnableAlwaysRule
Definition: parsenodes.h:2489
@ AT_SetAccessMethod
Definition: parsenodes.h:2475
@ AT_AlterColumnType
Definition: parsenodes.h:2467
@ AT_DetachPartitionFinalize
Definition: parsenodes.h:2504
@ AT_AddInherit
Definition: parsenodes.h:2492
@ AT_ReAddDomainConstraint
Definition: parsenodes.h:2461
@ AT_EnableTrig
Definition: parsenodes.h:2480
@ AT_DropColumn
Definition: parsenodes.h:2456
@ AT_ReAddComment
Definition: parsenodes.h:2466
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:2468
@ AT_DisableTrigAll
Definition: parsenodes.h:2485
@ AT_EnableRule
Definition: parsenodes.h:2488
@ AT_NoForceRowSecurity
Definition: parsenodes.h:2500
@ AT_DetachPartition
Definition: parsenodes.h:2503
@ AT_SetStatistics
Definition: parsenodes.h:2451
@ AT_AttachPartition
Definition: parsenodes.h:2502
@ AT_AddConstraint
Definition: parsenodes.h:2459
@ AT_DropInherit
Definition: parsenodes.h:2493
@ AT_EnableAlwaysTrig
Definition: parsenodes.h:2481
@ AT_SetLogged
Definition: parsenodes.h:2472
@ AT_SetStorage
Definition: parsenodes.h:2454
@ AT_DisableRule
Definition: parsenodes.h:2491
@ AT_DisableRowSecurity
Definition: parsenodes.h:2498
@ AT_SetRelOptions
Definition: parsenodes.h:2477
@ AT_ChangeOwner
Definition: parsenodes.h:2469
@ AT_EnableTrigUser
Definition: parsenodes.h:2486
@ AT_SetExpression
Definition: parsenodes.h:2449
@ AT_ReAddConstraint
Definition: parsenodes.h:2460
@ AT_SetTableSpace
Definition: parsenodes.h:2476
@ AT_GenericOptions
Definition: parsenodes.h:2501
@ AT_ColumnDefault
Definition: parsenodes.h:2445
@ AT_CookedColumnDefault
Definition: parsenodes.h:2446
@ AT_AlterConstraint
Definition: parsenodes.h:2462
@ AT_EnableTrigAll
Definition: parsenodes.h:2484
@ AT_SplitPartition
Definition: parsenodes.h:2505
@ AT_DropCluster
Definition: parsenodes.h:2471
@ AT_ValidateConstraint
Definition: parsenodes.h:2463
@ AT_AddColumn
Definition: parsenodes.h:2443

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_MergePartitions, 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, AT_SplitPartition, 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 12840 of file tablecmds.c.

12844{
12845 Form_pg_constraint currcon;
12846 Oid conoid;
12847 ScanKeyData pkey;
12848 SysScanDesc pscan;
12849 HeapTuple childtup;
12850
12851 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12852 conoid = currcon->oid;
12853
12854 ScanKeyInit(&pkey,
12855 Anum_pg_constraint_conparentid,
12856 BTEqualStrategyNumber, F_OIDEQ,
12857 ObjectIdGetDatum(conoid));
12858
12859 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12860 true, NULL, 1, &pkey);
12861
12862 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12863 {
12864 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12865 Relation childrel;
12866
12867 childrel = table_open(childcon->conrelid, lockmode);
12868
12869 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12870 childtup, recurse, otherrelids, lockmode);
12871 table_close(childrel, NoLock);
12872 }
12873
12874 systable_endscan(pscan);
12875}
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:12586

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

12799{
12800 Form_pg_constraint currcon;
12801 Oid conoid;
12802 ScanKeyData pkey;
12803 SysScanDesc pscan;
12804 HeapTuple childtup;
12805
12806 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12807 conoid = currcon->oid;
12808
12809 ScanKeyInit(&pkey,
12810 Anum_pg_constraint_conparentid,
12811 BTEqualStrategyNumber, F_OIDEQ,
12812 ObjectIdGetDatum(conoid));
12813
12814 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12815 true, NULL, 1, &pkey);
12816
12817 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12818 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12819 pkrelid, childtup, lockmode,
12820 ReferencedParentDelTrigger,
12821 ReferencedParentUpdTrigger,
12822 ReferencingParentInsTrigger,
12823 ReferencingParentUpdTrigger);
12824
12825 systable_endscan(pscan);
12826}
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:12440

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

12725{
12726 HeapTuple tgtuple;
12727 ScanKeyData tgkey;
12728 SysScanDesc tgscan;
12729
12730 ScanKeyInit(&tgkey,
12731 Anum_pg_trigger_tgconstraint,
12732 BTEqualStrategyNumber, F_OIDEQ,
12733 ObjectIdGetDatum(conoid));
12734 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12735 NULL, 1, &tgkey);
12736 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12737 {
12738 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12739 Form_pg_trigger copy_tg;
12740 HeapTuple tgCopyTuple;
12741
12742 /*
12743 * Remember OIDs of other relation(s) involved in FK constraint.
12744 * (Note: it's likely that we could skip forcing a relcache inval for
12745 * other rels that don't have a trigger whose properties change, but
12746 * let's be conservative.)
12747 */
12748 if (tgform->tgrelid != RelationGetRelid(rel))
12749 *otherrelids = list_append_unique_oid(*otherrelids,
12750 tgform->tgrelid);
12751
12752 /*
12753 * Update enable status and deferrability of RI_FKey_noaction_del,
12754 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12755 * triggers, but not others; see createForeignKeyActionTriggers and
12756 * CreateFKCheckTrigger.
12757 */
12758 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12759 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12760 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12761 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12762 continue;
12763
12764 tgCopyTuple = heap_copytuple(tgtuple);
12765 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12766
12767 copy_tg->tgdeferrable = deferrable;
12768 copy_tg->tginitdeferred = initdeferred;
12769 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12770
12771 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12772
12773 heap_freetuple(tgCopyTuple);
12774 }
12775
12776 systable_endscan(tgscan);
12777}
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:778
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition: indexing.c:313
List * list_append_unique_oid(List *list, Oid datum)
Definition: list.c:1380
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:80
ItemPointerData t_self
Definition: htup.h:65

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

Referenced by ATExecAlterConstrDeferrability().

◆ AlterConstrUpdateConstraintEntry()

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

Definition at line 12882 of file tablecmds.c.

12884{
12885 HeapTuple copyTuple;
12886 Form_pg_constraint copy_con;
12887
12888 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12889 cmdcon->alterInheritability);
12890
12891 copyTuple = heap_copytuple(contuple);
12892 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12893
12894 if (cmdcon->alterEnforceability)
12895 {
12896 copy_con->conenforced = cmdcon->is_enforced;
12897
12898 /*
12899 * NB: The convalidated status is irrelevant when the constraint is
12900 * set to NOT ENFORCED, but for consistency, it should still be set
12901 * appropriately. Similarly, if the constraint is later changed to
12902 * ENFORCED, validation will be performed during phase 3, so it makes
12903 * sense to mark it as valid in that case.
12904 */
12905 copy_con->convalidated = cmdcon->is_enforced;
12906 }
12907 if (cmdcon->alterDeferrability)
12908 {
12909 copy_con->condeferrable = cmdcon->deferrable;
12910 copy_con->condeferred = cmdcon->initdeferred;
12911 }
12912 if (cmdcon->alterInheritability)
12913 copy_con->connoinherit = cmdcon->noinherit;
12914
12915 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12916 InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12917
12918 /* Make new constraint flags visible to others */
12919 CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12920
12921 heap_freetuple(copyTuple);
12922}
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 19157 of file tablecmds.c.

19159{
19160 List *indexList;
19161 ListCell *l;
19162
19163 indexList = RelationGetIndexList(rel);
19164
19165 foreach(l, indexList)
19166 {
19167 Oid indexOid = lfirst_oid(l);
19168 ObjectAddress thisobj;
19169
19170 thisobj.classId = RelationRelationId;
19171 thisobj.objectId = indexOid;
19172 thisobj.objectSubId = 0;
19173
19174 /*
19175 * Note: currently, the index will not have its own dependency on the
19176 * namespace, so we don't need to do changeDependencyFor(). There's no
19177 * row type in pg_type, either.
19178 *
19179 * XXX this objsMoved test may be pointless -- surely we have a single
19180 * dependency link from a relation to each index?
19181 */
19182 if (!object_address_present(&thisobj, objsMoved))
19183 {
19184 AlterRelationNamespaceInternal(classRel, indexOid,
19185 oldNspOid, newNspOid,
19186 false, objsMoved);
19187 add_exact_object_address(&thisobj, objsMoved);
19188 }
19189 }
19190
19191 list_free(indexList);
19192}
bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs)
Definition: dependency.c:2769
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2709
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:19080

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

19084{
19085 HeapTuple classTup;
19086 Form_pg_class classForm;
19087 ObjectAddress thisobj;
19088 bool already_done = false;
19089
19090 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19091 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
19092 if (!HeapTupleIsValid(classTup))
19093 elog(ERROR, "cache lookup failed for relation %u", relOid);
19094 classForm = (Form_pg_class) GETSTRUCT(classTup);
19095
19096 Assert(classForm->relnamespace == oldNspOid);
19097
19098 thisobj.classId = RelationRelationId;
19099 thisobj.objectId = relOid;
19100 thisobj.objectSubId = 0;
19101
19102 /*
19103 * If the object has already been moved, don't move it again. If it's
19104 * already in the right place, don't move it, but still fire the object
19105 * access hook.
19106 */
19107 already_done = object_address_present(&thisobj, objsMoved);
19108 if (!already_done && oldNspOid != newNspOid)
19109 {
19110 ItemPointerData otid = classTup->t_self;
19111
19112 /* check for duplicate name (more friendly than unique-index failure) */
19113 if (get_relname_relid(NameStr(classForm->relname),
19114 newNspOid) != InvalidOid)
19115 ereport(ERROR,
19116 (errcode(ERRCODE_DUPLICATE_TABLE),
19117 errmsg("relation \"%s\" already exists in schema \"%s\"",
19118 NameStr(classForm->relname),
19119 get_namespace_name(newNspOid))));
19120
19121 /* classTup is a copy, so OK to scribble on */
19122 classForm->relnamespace = newNspOid;
19123
19124 CatalogTupleUpdate(classRel, &otid, classTup);
19125 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19126
19127
19128 /* Update dependency on schema if caller said so */
19129 if (hasDependEntry &&
19130 changeDependencyFor(RelationRelationId,
19131 relOid,
19132 NamespaceRelationId,
19133 oldNspOid,
19134 newNspOid) != 1)
19135 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19136 NameStr(classForm->relname));
19137 }
19138 else
19139 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19140 if (!already_done)
19141 {
19142 add_exact_object_address(&thisobj, objsMoved);
19143
19144 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19145 }
19146
19147 heap_freetuple(classTup);
19148}
#define NameStr(name)
Definition: c.h:765
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:3531
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:2050
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 19202 of file tablecmds.c.

19205{
19206 Relation depRel;
19207 SysScanDesc scan;
19208 ScanKeyData key[2];
19209 HeapTuple tup;
19210
19211 /*
19212 * SERIAL sequences are those having an auto dependency on one of the
19213 * table's columns (we don't care *which* column, exactly).
19214 */
19215 depRel = table_open(DependRelationId, AccessShareLock);
19216
19217 ScanKeyInit(&key[0],
19218 Anum_pg_depend_refclassid,
19219 BTEqualStrategyNumber, F_OIDEQ,
19220 ObjectIdGetDatum(RelationRelationId));
19221 ScanKeyInit(&key[1],
19222 Anum_pg_depend_refobjid,
19223 BTEqualStrategyNumber, F_OIDEQ,
19225 /* we leave refobjsubid unspecified */
19226
19227 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19228 NULL, 2, key);
19229
19230 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19231 {
19232 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19233 Relation seqRel;
19234
19235 /* skip dependencies other than auto dependencies on columns */
19236 if (depForm->refobjsubid == 0 ||
19237 depForm->classid != RelationRelationId ||
19238 depForm->objsubid != 0 ||
19239 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19240 continue;
19241
19242 /* Use relation_open just in case it's an index */
19243 seqRel = relation_open(depForm->objid, lockmode);
19244
19245 /* skip non-sequence relations */
19246 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19247 {
19248 /* No need to keep the lock */
19249 relation_close(seqRel, lockmode);
19250 continue;
19251 }
19252
19253 /* Fix the pg_class and pg_depend entries */
19254 AlterRelationNamespaceInternal(classRel, depForm->objid,
19255 oldNspOid, newNspOid,
19256 true, objsMoved);
19257
19258 /*
19259 * Sequences used to have entries in pg_type, but no longer do. If we
19260 * ever re-instate that, we'll need to move the pg_type entry to the
19261 * new namespace, too (using AlterTypeNamespaceInternal).
19262 */
19263 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19264
19265 /* Now we can close it. Keep the lock till end of transaction. */
19266 relation_close(seqRel, NoLock);
19267 }
19268
19269 systable_endscan(scan);
19270
19272}
@ DEPENDENCY_AUTO
Definition: dependency.h:34
#define AccessShareLock
Definition: lockdefs.h:36
FormData_pg_depend * Form_pg_depend
Definition: pg_depend.h:72
#define RelationGetForm(relation)
Definition: rel.h:509
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47

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

Referenced by AlterTableNamespaceInternal().

◆ AlterTable()

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

Definition at line 4531 of file tablecmds.c.

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

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

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4605 of file tablecmds.c.

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

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_MergePartitions, 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_SplitPartition, 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 4560 of file tablecmds.c.

4561{
4562 Relation rel;
4563 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4564
4565 rel = relation_open(relid, lockmode);
4566
4568
4569 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4570}
void EventTriggerAlterTableRelid(Oid objectId)
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition: tablecmds.c:4605

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4472 of file tablecmds.c.

4473{
4474 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4475 stmt->missing_ok ? RVR_MISSING_OK : 0,
4477 stmt);
4478}
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:19612

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

Referenced by ProcessUtilitySlow().

◆ AlterTableMoveAll()

Oid AlterTableMoveAll ( AlterTableMoveAllStmt stmt)

Definition at line 17013 of file tablecmds.c.

17014{
17015 List *relations = NIL;
17016 ListCell *l;
17017 ScanKeyData key[1];
17018 Relation rel;
17019 TableScanDesc scan;
17020 HeapTuple tuple;
17021 Oid orig_tablespaceoid;
17022 Oid new_tablespaceoid;
17023 List *role_oids = roleSpecsToIds(stmt->roles);
17024
17025 /* Ensure we were not asked to move something we can't */
17026 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17027 stmt->objtype != OBJECT_MATVIEW)
17028 ereport(ERROR,
17029 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17030 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17031
17032 /* Get the orig and new tablespace OIDs */
17033 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17034 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17035
17036 /* Can't move shared relations in to or out of pg_global */
17037 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17038 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
17039 new_tablespaceoid == GLOBALTABLESPACE_OID)
17040 ereport(ERROR,
17041 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
17042 errmsg("cannot move relations in to or out of pg_global tablespace")));
17043
17044 /*
17045 * Must have CREATE rights on the new tablespace, unless it is the
17046 * database default tablespace (which all users implicitly have CREATE
17047 * rights on).
17048 */
17049 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
17050 {
17051 AclResult aclresult;
17052
17053 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
17054 ACL_CREATE);
17055 if (aclresult != ACLCHECK_OK)
17057 get_tablespace_name(new_tablespaceoid));
17058 }
17059
17060 /*
17061 * Now that the checks are done, check if we should set either to
17062 * InvalidOid because it is our database's default tablespace.
17063 */
17064 if (orig_tablespaceoid == MyDatabaseTableSpace)
17065 orig_tablespaceoid = InvalidOid;
17066
17067 if (new_tablespaceoid == MyDatabaseTableSpace)
17068 new_tablespaceoid = InvalidOid;
17069
17070 /* no-op */
17071 if (orig_tablespaceoid == new_tablespaceoid)
17072 return new_tablespaceoid;
17073
17074 /*
17075 * Walk the list of objects in the tablespace and move them. This will
17076 * only find objects in our database, of course.
17077 */
17078 ScanKeyInit(&key[0],
17079 Anum_pg_class_reltablespace,
17080 BTEqualStrategyNumber, F_OIDEQ,
17081 ObjectIdGetDatum(orig_tablespaceoid));
17082
17083 rel = table_open(RelationRelationId, AccessShareLock);
17084 scan = table_beginscan_catalog(rel, 1, key);
17085 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17086 {
17087 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
17088 Oid relOid = relForm->oid;
17089
17090 /*
17091 * Do not move objects in pg_catalog as part of this, if an admin
17092 * really wishes to do so, they can issue the individual ALTER
17093 * commands directly.
17094 *
17095 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17096 * (TOAST will be moved with the main table).
17097 */
17098 if (IsCatalogNamespace(relForm->relnamespace) ||
17099 relForm->relisshared ||
17100 isAnyTempNamespace(relForm->relnamespace) ||
17101 IsToastNamespace(relForm->relnamespace))
17102 continue;
17103
17104 /* Only move the object type requested */
17105 if ((stmt->objtype == OBJECT_TABLE &&
17106 relForm->relkind != RELKIND_RELATION &&
17107 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17108 (stmt->objtype == OBJECT_INDEX &&
17109 relForm->relkind != RELKIND_INDEX &&
17110 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17111 (stmt->objtype == OBJECT_MATVIEW &&
17112 relForm->relkind != RELKIND_MATVIEW))
17113 continue;
17114
17115 /* Check if we are only moving objects owned by certain roles */
17116 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17117 continue;
17118
17119 /*
17120 * Handle permissions-checking here since we are locking the tables
17121 * and also to avoid doing a bunch of work only to fail part-way. Note
17122 * that permissions will also be checked by AlterTableInternal().
17123 *
17124 * Caller must be considered an owner on the table to move it.
17125 */
17126 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17128 NameStr(relForm->relname));
17129
17130 if (stmt->nowait &&
17132 ereport(ERROR,
17133 (errcode(ERRCODE_OBJECT_IN_USE),
17134 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17135 get_namespace_name(relForm->relnamespace),
17136 NameStr(relForm->relname))));
17137 else
17139
17140 /* Add to our list of objects to move */
17141 relations = lappend_oid(relations, relOid);
17142 }
17143
17144 table_endscan(scan);
17146
17147 if (relations == NIL)
17149 (errcode(ERRCODE_NO_DATA_FOUND),
17150 errmsg("no matching relations in tablespace \"%s\" found",
17151 orig_tablespaceoid == InvalidOid ? "(database default)" :
17152 get_tablespace_name(orig_tablespaceoid))));
17153
17154 /* Everything is locked, loop through and move all of the relations. */
17155 foreach(l, relations)
17156 {
17157 List *cmds = NIL;
17159
17161 cmd->name = stmt->new_tablespacename;
17162
17163 cmds = lappend(cmds, cmd);
17164
17166 /* OID is set by AlterTableInternal */
17167 AlterTableInternal(lfirst_oid(l), cmds, false);
17169 }
17170
17171 return new_tablespaceoid;
17172}
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:2654
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3836
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4090
char * get_tablespace_name(Oid spc_oid)
Definition: tablespace.c:1472
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
Definition: tablespace.c:1426
bool IsToastNamespace(Oid namespaceId)
Definition: catalog.c:261
bool IsCatalogNamespace(Oid namespaceId)
Definition: catalog.c:243
#define NOTICE
Definition: elog.h:35
void EventTriggerAlterTableStart(Node *parsetree)
void EventTriggerAlterTableEnd(void)
Oid MyDatabaseTableSpace
Definition: globals.c:96
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1361
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:722
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:151
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:107
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2168
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:2374
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2393
@ OBJECT_INDEX
Definition: parsenodes.h:2371
@ OBJECT_TABLE
Definition: parsenodes.h:2392
#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:4560
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 18972 of file tablecmds.c.

18973{
18974 Relation rel;
18975 Oid relid;
18976 Oid oldNspOid;
18977 Oid nspOid;
18978 RangeVar *newrv;
18979 ObjectAddresses *objsMoved;
18980 ObjectAddress myself;
18981
18983 stmt->missing_ok ? RVR_MISSING_OK : 0,
18985 stmt);
18986
18987 if (!OidIsValid(relid))
18988 {
18990 (errmsg("relation \"%s\" does not exist, skipping",
18991 stmt->relation->relname)));
18992 return InvalidObjectAddress;
18993 }
18994
18995 rel = relation_open(relid, NoLock);
18996
18997 oldNspOid = RelationGetNamespace(rel);
18998
18999 /* If it's an owned sequence, disallow moving it by itself. */
19000 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19001 {
19002 Oid tableId;
19003 int32 colId;
19004
19005 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19006 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
19007 ereport(ERROR,
19008 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19009 errmsg("cannot move an owned sequence into another schema"),
19010 errdetail("Sequence \"%s\" is linked to table \"%s\".",
19012 get_rel_name(tableId))));
19013 }
19014
19015 /* Get and lock schema OID and check its permissions. */
19016 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19017 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
19018
19019 /* common checks on switching namespaces */
19020 CheckSetNamespace(oldNspOid, nspOid);
19021
19022 objsMoved = new_object_addresses();
19023 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
19024 free_object_addresses(objsMoved);
19025
19026 ObjectAddressSet(myself, RelationRelationId, relid);
19027
19028 if (oldschema)
19029 *oldschema = oldNspOid;
19030
19031 /* close rel, but keep lock until commit */
19032 relation_close(rel, NoLock);
19033
19034 return myself;
19035}
int32_t int32
Definition: c.h:548
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2664
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2949
int errdetail(const char *fmt,...)
Definition: elog.c:1216
char * get_rel_name(Oid relid)
Definition: lsyscache.c:2093
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:19043

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

19045{
19046 Relation classRel;
19047
19048 Assert(objsMoved != NULL);
19049
19050 /* OK, modify the pg_class row and pg_depend entry */
19051 classRel = table_open(RelationRelationId, RowExclusiveLock);
19052
19053 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
19054 nspOid, true, objsMoved);
19055
19056 /* Fix the table's row type too, if it has one */
19057 if (OidIsValid(rel->rd_rel->reltype))
19058 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
19059 false, /* isImplicitArray */
19060 false, /* ignoreDependent */
19061 false, /* errorOnTableType */
19062 objsMoved);
19063
19064 /* Fix other dependent stuff */
19065 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
19066 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
19067 objsMoved, AccessExclusiveLock);
19068 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
19069 false, objsMoved);
19070
19071 table_close(classRel, RowExclusiveLock);
19072}
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:19202
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19157
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition: typecmds.c:4169

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

Referenced by AlterObjectNamespace_oid(), and AlterTableNamespace().

◆ ATAddCheckNNConstraint()

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

Definition at line 9940 of file tablecmds.c.

9943{
9944 List *newcons;
9945 ListCell *lcon;
9946 List *children;
9947 ListCell *child;
9949
9950 /* Guard against stack overflow due to overly deep inheritance tree. */
9952
9953 /* At top level, permission check was done in ATPrepCmd, else do it */
9954 if (recursing)
9957
9958 /*
9959 * Call AddRelationNewConstraints to do the work, making sure it works on
9960 * a copy of the Constraint so transformExpr can't modify the original. It
9961 * returns a list of cooked constraints.
9962 *
9963 * If the constraint ends up getting merged with a pre-existing one, it's
9964 * omitted from the returned list, which is what we want: we do not need
9965 * to do any validation work. That can only happen at child tables,
9966 * though, since we disallow merging at the top level.
9967 */
9968 newcons = AddRelationNewConstraints(rel, NIL,
9969 list_make1(copyObject(constr)),
9970 recursing || is_readd, /* allow_merge */
9971 !recursing, /* is_local */
9972 is_readd, /* is_internal */
9973 NULL); /* queryString not available
9974 * here */
9975
9976 /* we don't expect more than one constraint here */
9977 Assert(list_length(newcons) <= 1);
9978
9979 /* Add each to-be-validated constraint to Phase 3's queue */
9980 foreach(lcon, newcons)
9981 {
9982 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9983
9984 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9985 {
9986 NewConstraint *newcon;
9987
9988 newcon = palloc0_object(NewConstraint);
9989 newcon->name = ccon->name;
9990 newcon->contype = ccon->contype;
9991 newcon->qual = ccon->expr;
9992
9993 tab->constraints = lappend(tab->constraints, newcon);
9994 }
9995
9996 /* Save the actually assigned name if it was defaulted */
9997 if (constr->conname == NULL)
9998 constr->conname = ccon->name;
9999
10000 /*
10001 * If adding a valid not-null constraint, set the pg_attribute flag
10002 * and tell phase 3 to verify existing rows, if needed. For an
10003 * invalid constraint, just set attnotnull, without queueing
10004 * verification.
10005 */
10006 if (constr->contype == CONSTR_NOTNULL)
10007 set_attnotnull(wqueue, rel, ccon->attnum,
10008 !constr->skip_validation,
10009 !constr->skip_validation);
10010
10011 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
10012 }
10013
10014 /* At this point we must have a locked-down name to use */
10015 Assert(newcons == NIL || constr->conname != NULL);
10016
10017 /* Advance command counter in case same table is visited multiple times */
10019
10020 /*
10021 * If the constraint got merged with an existing constraint, we're done.
10022 * We mustn't recurse to child tables in this case, because they've
10023 * already got the constraint, and visiting them again would lead to an
10024 * incorrect value for coninhcount.
10025 */
10026 if (newcons == NIL)
10027 return address;
10028
10029 /*
10030 * If adding a NO INHERIT constraint, no need to find our children.
10031 */
10032 if (constr->is_no_inherit)
10033 return address;
10034
10035 /*
10036 * Propagate to children as appropriate. Unlike most other ALTER
10037 * routines, we have to do this one level of recursion at a time; we can't
10038 * use find_all_inheritors to do it in one pass.
10039 */
10040 children =
10042
10043 /*
10044 * Check if ONLY was specified with ALTER TABLE. If so, allow the
10045 * constraint creation only if there are no children currently. Error out
10046 * otherwise.
10047 */
10048 if (!recurse && children != NIL)
10049 ereport(ERROR,
10050 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10051 errmsg("constraint must be added to child tables too")));
10052
10053 /*
10054 * Recurse to create the constraint on each child.
10055 */
10056 foreach(child, children)
10057 {
10058 Oid childrelid = lfirst_oid(child);
10059 Relation childrel;
10060 AlteredTableInfo *childtab;
10061
10062 /* find_inheritance_children already got lock */
10063 childrel = table_open(childrelid, NoLock);
10064 CheckAlterTableIsSafe(childrel);
10065
10066 /* Find or create work queue entry for this table */
10067 childtab = ATGetQueueEntry(wqueue, childrel);
10068
10069 /* Recurse to this child */
10070 ATAddCheckNNConstraint(wqueue, childtab, childrel,
10071 constr, recurse, true, is_readd, lockmode);
10072
10073 table_close(childrel, NoLock);
10074 }
10075
10076 return address;
10077}
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:2829
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:2868
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:9940
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition: tablecmds.c:6767
#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:7868

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_object, NewConstraint::qual, RelationGetRelid, set_attnotnull(), CookedConstraint::skip_validation, Constraint::skip_validation, table_close(), and table_open().

Referenced by ATAddCheckNNConstraint(), and ATExecAddConstraint().

◆ ATAddForeignKeyConstraint()

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

Definition at line 10095 of file tablecmds.c.

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

6890{
6891 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6892 {
6893 List *inh;
6894 ListCell *cell;
6895
6896 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6897 /* first element is the parent rel; must ignore it */
6898 for_each_from(cell, inh, 1)
6899 {
6900 Relation childrel;
6901
6902 /* find_all_inheritors already got lock */
6903 childrel = table_open(lfirst_oid(cell), NoLock);
6904 CheckAlterTableIsSafe(childrel);
6905 table_close(childrel, NoLock);
6906 }
6907 list_free(inh);
6908 }
6909}
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 14707 of file tablecmds.c.

14708{
14709 Assert(expr != NULL);
14710
14711 for (;;)
14712 {
14713 /* only one varno, so no need to check that */
14714 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14715 return false;
14716 else if (IsA(expr, RelabelType))
14717 expr = (Node *) ((RelabelType *) expr)->arg;
14718 else if (IsA(expr, CoerceToDomain))
14719 {
14720 CoerceToDomain *d = (CoerceToDomain *) expr;
14721
14723 return true;
14724 expr = (Node *) d->arg;
14725 }
14726 else if (IsA(expr, FuncExpr))
14727 {
14728 FuncExpr *f = (FuncExpr *) expr;
14729
14730 switch (f->funcid)
14731 {
14732 case F_TIMESTAMPTZ_TIMESTAMP:
14733 case F_TIMESTAMP_TIMESTAMPTZ:
14735 return true;
14736 else
14737 expr = linitial(f->args);
14738 break;
14739 default:
14740 return true;
14741 }
14742 }
14743 else
14744 return true;
14745 }
14746}
bool TimestampTimestampTzRequiresRewrite(void)
Definition: timestamp.c:6417
void * arg
#define linitial(l)
Definition: pg_list.h:178
Oid funcid
Definition: primnodes.h:782
List * args
Definition: primnodes.h:800
Definition: primnodes.h:262
bool DomainHasConstraints(Oid type_id)
Definition: typcache.c:1488

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

Referenced by ATPrepAlterColumnType().

◆ ATController()

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

Definition at line 4872 of file tablecmds.c.

4875{
4876 List *wqueue = NIL;
4877 ListCell *lcmd;
4878
4879 /* Phase 1: preliminary examination of commands, create work queue */
4880 foreach(lcmd, cmds)
4881 {
4882 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4883
4884 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4885 }
4886
4887 /* Close the relation, but keep lock until commit */
4888 relation_close(rel, NoLock);
4889
4890 /* Phase 2: update system catalogs */
4891 ATRewriteCatalogs(&wqueue, lockmode, context);
4892
4893 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4894 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4895}
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5310
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4907
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5862

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

21986{
21987 List *constraints;
21988 ListCell *cell;
21989
21990 constraints = GetParentedForeignKeyRefs(partition);
21991
21992 foreach(cell, constraints)
21993 {
21994 Oid constrOid = lfirst_oid(cell);
21995 HeapTuple tuple;
21996 Form_pg_constraint constrForm;
21997 Relation rel;
21998 Trigger trig = {0};
21999
22000 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
22001 if (!HeapTupleIsValid(tuple))
22002 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22003 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
22004
22005 Assert(OidIsValid(constrForm->conparentid));
22006 Assert(constrForm->confrelid == RelationGetRelid(partition));
22007
22008 /* prevent data changes into the referencing table until commit */
22009 rel = table_open(constrForm->conrelid, ShareLock);
22010
22011 trig.tgoid = InvalidOid;
22012 trig.tgname = NameStr(constrForm->conname);
22014 trig.tgisinternal = true;
22015 trig.tgconstrrelid = RelationGetRelid(partition);
22016 trig.tgconstrindid = constrForm->conindid;
22017 trig.tgconstraint = constrForm->oid;
22018 trig.tgdeferrable = false;
22019 trig.tginitdeferred = false;
22020 /* we needn't fill in remaining fields */
22021
22022 RI_PartitionRemove_Check(&trig, rel, partition);
22023
22024 ReleaseSysCache(tuple);
22025
22026 table_close(rel, NoLock);
22027 }
22028}
#define ShareLock
Definition: lockdefs.h:40
void RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1813
char tgenabled
Definition: reltrigger.h:30
Oid tgoid
Definition: reltrigger.h:25
Oid tgconstrindid
Definition: reltrigger.h:34
Oid tgconstraint
Definition: reltrigger.h:35
Oid tgconstrrelid
Definition: reltrigger.h:33
char * tgname
Definition: reltrigger.h:27
bool tgdeferrable
Definition: reltrigger.h:36
bool tginitdeferred
Definition: reltrigger.h:37
bool tgisinternal
Definition: reltrigger.h:31
static List * GetParentedForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21932
#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 19485 of file tablecmds.c.

19487{
19488 ListCell *cur_item;
19489
19490 foreach(cur_item, on_commits)
19491 {
19492 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19493
19494 if (!isCommit && oc->creating_subid == mySubid)
19495 {
19496 /* cur_item must be removed */
19498 pfree(oc);
19499 }
19500 else
19501 {
19502 /* cur_item must be preserved */
19503 if (oc->creating_subid == mySubid)
19504 oc->creating_subid = parentSubid;
19505 if (oc->deleting_subid == mySubid)
19506 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19507 }
19508 }
19509}
#define InvalidSubTransactionId
Definition: c.h:677
#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 19453 of file tablecmds.c.

19454{
19455 ListCell *cur_item;
19456
19457 foreach(cur_item, on_commits)
19458 {
19459 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19460
19461 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19463 {
19464 /* cur_item must be removed */
19466 pfree(oc);
19467 }
19468 else
19469 {
19470 /* cur_item must be preserved */
19473 }
19474 }
19475}

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

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

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

9827{
9829
9830 Assert(IsA(newConstraint, Constraint));
9831
9832 /*
9833 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9834 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9835 * parse_utilcmd.c).
9836 */
9837 switch (newConstraint->contype)
9838 {
9839 case CONSTR_CHECK:
9840 case CONSTR_NOTNULL:
9841 address =
9842 ATAddCheckNNConstraint(wqueue, tab, rel,
9843 newConstraint, recurse, false, is_readd,
9844 lockmode);
9845 break;
9846
9847 case CONSTR_FOREIGN:
9848
9849 /*
9850 * Assign or validate constraint name
9851 */
9852 if (newConstraint->conname)
9853 {
9855 RelationGetRelid(rel),
9856 newConstraint->conname))
9857 ereport(ERROR,
9859 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9860 newConstraint->conname,
9862 }
9863 else
9864 newConstraint->conname =
9867 "fkey",
9869 NIL);
9870
9871 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9872 newConstraint,
9873 recurse, false,
9874 lockmode);
9875 break;
9876
9877 default:
9878 elog(ERROR, "unrecognized constraint type: %d",
9879 (int) newConstraint->contype);
9880 }
9881
9882 return address;
9883}
@ CONSTR_CHECK
Definition: parsenodes.h:2833
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition: tablecmds.c:9898
static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:10095

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

8270{
8271 Relation attrelation;
8272 HeapTuple tuple;
8273 Form_pg_attribute attTup;
8275 ObjectAddress address;
8276 ColumnDef *cdef = castNode(ColumnDef, def);
8277 bool ispartitioned;
8278
8279 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8280 if (ispartitioned && !recurse)
8281 ereport(ERROR,
8282 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8283 errmsg("cannot add identity to a column of only the partitioned table"),
8284 errhint("Do not specify the ONLY keyword.")));
8285
8286 if (rel->rd_rel->relispartition && !recursing)
8287 ereport(ERROR,
8288 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8289 errmsg("cannot add identity to a column of a partition"));
8290
8291 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8292
8293 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8294 if (!HeapTupleIsValid(tuple))
8295 ereport(ERROR,
8296 (errcode(ERRCODE_UNDEFINED_COLUMN),
8297 errmsg("column \"%s\" of relation \"%s\" does not exist",
8298 colName, RelationGetRelationName(rel))));
8299 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8300 attnum = attTup->attnum;
8301
8302 /* Can't alter a system attribute */
8303 if (attnum <= 0)
8304 ereport(ERROR,
8305 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8306 errmsg("cannot alter system column \"%s\"",
8307 colName)));
8308
8309 /*
8310 * Creating a column as identity implies NOT NULL, so adding the identity
8311 * to an existing column that is not NOT NULL would create a state that
8312 * cannot be reproduced without contortions.
8313 */
8314 if (!attTup->attnotnull)
8315 ereport(ERROR,
8316 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8317 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8318 colName, RelationGetRelationName(rel))));
8319
8320 /*
8321 * On the other hand, if a not-null constraint exists, then verify that
8322 * it's compatible.
8323 */
8324 if (attTup->attnotnull)
8325 {
8326 HeapTuple contup;
8327 Form_pg_constraint conForm;
8328
8330 attnum);
8331 if (!HeapTupleIsValid(contup))
8332 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8333 colName, RelationGetRelationName(rel));
8334
8335 conForm = (Form_pg_constraint) GETSTRUCT(contup);
8336 if (!conForm->convalidated)
8337 ereport(ERROR,
8338 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8339 errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8340 NameStr(conForm->conname), RelationGetRelationName(rel)),
8341 errhint("You might need to validate it using %s.",
8342 "ALTER TABLE ... VALIDATE CONSTRAINT"));
8343 }
8344
8345 if (attTup->attidentity)
8346 ereport(ERROR,
8347 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8348 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8349 colName, RelationGetRelationName(rel))));
8350
8351 if (attTup->atthasdef)
8352 ereport(ERROR,
8353 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8354 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8355 colName, RelationGetRelationName(rel))));
8356
8357 attTup->attidentity = cdef->identity;
8358 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8359
8360 InvokeObjectPostAlterHook(RelationRelationId,
8361 RelationGetRelid(rel),
8362 attTup->attnum);
8363 ObjectAddressSubSet(address, RelationRelationId,
8364 RelationGetRelid(rel), attnum);
8365 heap_freetuple(tuple);
8366
8367 table_close(attrelation, RowExclusiveLock);
8368
8369 /*
8370 * Recurse to propagate the identity column to partitions. Identity is
8371 * not inherited in regular inheritance children.
8372 */
8373 if (recurse && ispartitioned)
8374 {
8375 List *children;
8376 ListCell *lc;
8377
8378 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8379
8380 foreach(lc, children)
8381 {
8382 Relation childrel;
8383
8384 childrel = table_open(lfirst_oid(lc), NoLock);
8385 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8386 table_close(childrel, NoLock);
8387 }
8388 }
8389
8390 return address;
8391}
int errhint(const char *fmt,...)
Definition: elog.c:1330
HeapTuple findNotNullConstraintAttnum(Oid relid, AttrNumber attnum)
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8268

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

Referenced by ATExecAddIdentity(), and ATExecCmd().

◆ ATExecAddIndex()

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

Definition at line 9648 of file tablecmds.c.

9650{
9651 bool check_rights;
9652 bool skip_build;
9653 bool quiet;
9654 ObjectAddress address;
9655
9657 Assert(!stmt->concurrent);
9658
9659 /* The IndexStmt has already been through transformIndexStmt */
9660 Assert(stmt->transformed);
9661
9662 /* suppress schema rights check when rebuilding existing index */
9663 check_rights = !is_rebuild;
9664 /* skip index build if phase 3 will do it or we're reusing an old one */
9665 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9666 /* suppress notices when rebuilding existing index */
9667 quiet = is_rebuild;
9668
9669 address = DefineIndex(RelationGetRelid(rel),
9670 stmt,
9671 InvalidOid, /* no predefined OID */
9672 InvalidOid, /* no parent index */
9673 InvalidOid, /* no parent constraint */
9674 -1, /* total_parts unknown */
9675 true, /* is_alter_table */
9676 check_rights,
9677 false, /* check_not_in_use - we did it already */
9678 skip_build,
9679 quiet);
9680
9681 /*
9682 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9683 * new index instead of building from scratch. Restore associated fields.
9684 * This may store InvalidSubTransactionId in both fields, in which case
9685 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9686 * this after the CCI that made catalog rows visible to any rebuild. The
9687 * DROP of the old edition of this index will have scheduled the storage
9688 * for deletion at commit, so cancel that pending deletion.
9689 */
9690 if (RelFileNumberIsValid(stmt->oldNumber))
9691 {
9692 Relation irel = index_open(address.objectId, NoLock);
9693
9694 irel->rd_createSubid = stmt->oldCreateSubid;
9695 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9697 index_close(irel, NoLock);
9698 }
9699
9700 return address;
9701}
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 9732 of file tablecmds.c.

9734{
9735 Oid index_oid = stmt->indexOid;
9736 Relation indexRel;
9737 char *indexName;
9738 IndexInfo *indexInfo;
9739 char *constraintName;
9740 char constraintType;
9741 ObjectAddress address;
9742 bits16 flags;
9743
9745 Assert(OidIsValid(index_oid));
9746 Assert(stmt->isconstraint);
9747
9748 /*
9749 * Doing this on partitioned tables is not a simple feature to implement,
9750 * so let's punt for now.
9751 */
9752 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9753 ereport(ERROR,
9754 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9755 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9756
9757 indexRel = index_open(index_oid, AccessShareLock);
9758
9759 indexName = pstrdup(RelationGetRelationName(indexRel));
9760
9761 indexInfo = BuildIndexInfo(indexRel);
9762
9763 /* this should have been checked at parse time */
9764 if (!indexInfo->ii_Unique)
9765 elog(ERROR, "index \"%s\" is not unique", indexName);
9766
9767 /*
9768 * Determine name to assign to constraint. We require a constraint to
9769 * have the same name as the underlying index; therefore, use the index's
9770 * existing name as the default constraint name, and if the user
9771 * explicitly gives some other name for the constraint, rename the index
9772 * to match.
9773 */
9774 constraintName = stmt->idxname;
9775 if (constraintName == NULL)
9776 constraintName = indexName;
9777 else if (strcmp(constraintName, indexName) != 0)
9778 {
9780 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9781 indexName, constraintName)));
9782 RenameRelationInternal(index_oid, constraintName, false, true);
9783 }
9784
9785 /* Extra checks needed if making primary key */
9786 if (stmt->primary)
9787 index_check_primary_key(rel, indexInfo, true, stmt);
9788
9789 /* Note we currently don't support EXCLUSION constraints here */
9790 if (stmt->primary)
9791 constraintType = CONSTRAINT_PRIMARY;
9792 else
9793 constraintType = CONSTRAINT_UNIQUE;
9794
9795 /* Create the catalog entries for the constraint */
9798 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9799 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9801
9802 address = index_constraint_create(rel,
9803 index_oid,
9804 InvalidOid,
9805 indexInfo,
9806 constraintName,
9807 constraintType,
9808 flags,
9810 false); /* is_internal */
9811
9812 index_close(indexRel, NoLock);
9813
9814 return address;
9815}
uint16 bits16
Definition: c.h:560
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:4267

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

17290{
17291 Relation parent_rel;
17292 List *children;
17293 ObjectAddress address;
17294 const char *trigger_name;
17295
17296 /*
17297 * A self-exclusive lock is needed here. See the similar case in
17298 * MergeAttributes() for a full explanation.
17299 */
17300 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17301
17302 /*
17303 * Must be owner of both parent and child -- child was checked by
17304 * ATSimplePermissions call in ATPrepCmd
17305 */
17308
17309 /* Permanent rels cannot inherit from temporary ones */
17310 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17311 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17312 ereport(ERROR,
17313 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17314 errmsg("cannot inherit from temporary relation \"%s\"",
17315 RelationGetRelationName(parent_rel))));
17316
17317 /* If parent rel is temp, it must belong to this session */
17318 if (RELATION_IS_OTHER_TEMP(parent_rel))
17319 ereport(ERROR,
17320 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17321 errmsg("cannot inherit from temporary relation of another session")));
17322
17323 /* Ditto for the child */
17324 if (RELATION_IS_OTHER_TEMP(child_rel))
17325 ereport(ERROR,
17326 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17327 errmsg("cannot inherit to temporary relation of another session")));
17328
17329 /* Prevent partitioned tables from becoming inheritance parents */
17330 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17331 ereport(ERROR,
17332 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17333 errmsg("cannot inherit from partitioned table \"%s\"",
17334 parent->relname)));
17335
17336 /* Likewise for partitions */
17337 if (parent_rel->rd_rel->relispartition)
17338 ereport(ERROR,
17339 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17340 errmsg("cannot inherit from a partition")));
17341
17342 /*
17343 * Prevent circularity by seeing if proposed parent inherits from child.
17344 * (In particular, this disallows making a rel inherit from itself.)
17345 *
17346 * This is not completely bulletproof because of race conditions: in
17347 * multi-level inheritance trees, someone else could concurrently be
17348 * making another inheritance link that closes the loop but does not join
17349 * either of the rels we have locked. Preventing that seems to require
17350 * exclusive locks on the entire inheritance tree, which is a cure worse
17351 * than the disease. find_all_inheritors() will cope with circularity
17352 * anyway, so don't sweat it too much.
17353 *
17354 * We use weakest lock we can on child's children, namely AccessShareLock.
17355 */
17356 children = find_all_inheritors(RelationGetRelid(child_rel),
17357 AccessShareLock, NULL);
17358
17359 if (list_member_oid(children, RelationGetRelid(parent_rel)))
17360 ereport(ERROR,
17361 (errcode(ERRCODE_DUPLICATE_TABLE),
17362 errmsg("circular inheritance not allowed"),
17363 errdetail("\"%s\" is already a child of \"%s\".",
17364 parent->relname,
17365 RelationGetRelationName(child_rel))));
17366
17367 /*
17368 * If child_rel has row-level triggers with transition tables, we
17369 * currently don't allow it to become an inheritance child. See also
17370 * prohibitions in ATExecAttachPartition() and CreateTrigger().
17371 */
17372 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17373 if (trigger_name != NULL)
17374 ereport(ERROR,
17375 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17376 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17377 trigger_name, RelationGetRelationName(child_rel)),
17378 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17379
17380 /* OK to create inheritance */
17381 CreateInheritance(child_rel, parent_rel, false);
17382
17383 ObjectAddressSet(address, RelationRelationId,
17384 RelationGetRelid(parent_rel));
17385
17386 /* keep our lock on the parent relation until commit */
17387 table_close(parent_rel, NoLock);
17388
17389 return address;
17390}
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:668
char * relname
Definition: primnodes.h:83
TriggerDesc * trigdesc
Definition: rel.h:117
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17400
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 18242 of file tablecmds.c.

18243{
18244 Oid relid = RelationGetRelid(rel);
18245 Type typetuple;
18246 Form_pg_type typeform;
18247 Oid typeid;
18248 Relation inheritsRelation,
18249 relationRelation;
18250 SysScanDesc scan;
18252 AttrNumber table_attno,
18253 type_attno;
18254 TupleDesc typeTupleDesc,
18255 tableTupleDesc;
18256 ObjectAddress tableobj,
18257 typeobj;
18258 HeapTuple classtuple;
18259
18260 /* Validate the type. */
18261 typetuple = typenameType(NULL, ofTypename, NULL);
18262 check_of_type(typetuple);
18263 typeform = (Form_pg_type) GETSTRUCT(typetuple);
18264 typeid = typeform->oid;
18265
18266 /* Fail if the table has any inheritance parents. */
18267 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18269 Anum_pg_inherits_inhrelid,
18270 BTEqualStrategyNumber, F_OIDEQ,
18271 ObjectIdGetDatum(relid));
18272 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18273 true, NULL, 1, &key);
18275 ereport(ERROR,
18276 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18277 errmsg("typed tables cannot inherit")));
18278 systable_endscan(scan);
18279 table_close(inheritsRelation, AccessShareLock);
18280
18281 /*
18282 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18283 * require that the order also match. However, attnotnull need not match.
18284 */
18285 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18286 tableTupleDesc = RelationGetDescr(rel);
18287 table_attno = 1;
18288 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18289 {
18290 Form_pg_attribute type_attr,
18291 table_attr;
18292 const char *type_attname,
18293 *table_attname;
18294
18295 /* Get the next non-dropped type attribute. */
18296 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18297 if (type_attr->attisdropped)
18298 continue;
18299 type_attname = NameStr(type_attr->attname);
18300
18301 /* Get the next non-dropped table attribute. */
18302 do
18303 {
18304 if (table_attno > tableTupleDesc->natts)
18305 ereport(ERROR,
18306 (errcode(ERRCODE_DATATYPE_MISMATCH),
18307 errmsg("table is missing column \"%s\"",
18308 type_attname)));
18309 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18310 table_attno++;
18311 } while (table_attr->attisdropped);
18312 table_attname = NameStr(table_attr->attname);
18313
18314 /* Compare name. */
18315 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18316 ereport(ERROR,
18317 (errcode(ERRCODE_DATATYPE_MISMATCH),
18318 errmsg("table has column \"%s\" where type requires \"%s\"",
18319 table_attname, type_attname)));
18320
18321 /* Compare type. */
18322 if (table_attr->atttypid != type_attr->atttypid ||
18323 table_attr->atttypmod != type_attr->atttypmod ||
18324 table_attr->attcollation != type_attr->attcollation)
18325 ereport(ERROR,
18326 (errcode(ERRCODE_DATATYPE_MISMATCH),
18327 errmsg("table \"%s\" has different type for column \"%s\"",
18328 RelationGetRelationName(rel), type_attname)));
18329 }
18330 ReleaseTupleDesc(typeTupleDesc);
18331
18332 /* Any remaining columns at the end of the table had better be dropped. */
18333 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18334 {
18335 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18336 table_attno - 1);
18337
18338 if (!table_attr->attisdropped)
18339 ereport(ERROR,
18340 (errcode(ERRCODE_DATATYPE_MISMATCH),
18341 errmsg("table has extra column \"%s\"",
18342 NameStr(table_attr->attname))));
18343 }
18344
18345 /* If the table was already typed, drop the existing dependency. */
18346 if (rel->rd_rel->reloftype)
18347 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18349
18350 /* Record a dependency on the new type. */
18351 tableobj.classId = RelationRelationId;
18352 tableobj.objectId = relid;
18353 tableobj.objectSubId = 0;
18354 typeobj.classId = TypeRelationId;
18355 typeobj.objectId = typeid;
18356 typeobj.objectSubId = 0;
18357 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18358
18359 /* Update pg_class.reloftype */
18360 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18361 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18362 if (!HeapTupleIsValid(classtuple))
18363 elog(ERROR, "cache lookup failed for relation %u", relid);
18364 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18365 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18366
18367 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18368
18369 heap_freetuple(classtuple);
18370 table_close(relationRelation, RowExclusiveLock);
18371
18372 ReleaseSysCache(typetuple);
18373
18374 return typeobj;
18375}
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:18190
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:7171
#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 9711 of file tablecmds.c.

9713{
9714 ObjectAddress address;
9715
9717
9718 /* The CreateStatsStmt has already been through transformStatsStmt */
9719 Assert(stmt->transformed);
9720
9721 address = CreateStatistics(stmt, !is_rebuild);
9722
9723 return address;
9724}
ObjectAddress CreateStatistics(CreateStatsStmt *stmt, bool check_rights)
Definition: statscmds.c:63

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

Referenced by ATExecCmd().

◆ ATExecAlterColumnGenericOptions()

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

Definition at line 15983 of file tablecmds.c.

15987{
15988 Relation ftrel;
15989 Relation attrel;
15990 ForeignServer *server;
15991 ForeignDataWrapper *fdw;
15992 HeapTuple tuple;
15993 HeapTuple newtuple;
15994 bool isnull;
15995 Datum repl_val[Natts_pg_attribute];
15996 bool repl_null[Natts_pg_attribute];
15997 bool repl_repl[Natts_pg_attribute];
15998 Datum datum;
15999 Form_pg_foreign_table fttableform;
16000 Form_pg_attribute atttableform;
16002 ObjectAddress address;
16003
16004 if (options == NIL)
16005 return InvalidObjectAddress;
16006
16007 /* First, determine FDW validator associated to the foreign table. */
16008 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
16009 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
16010 if (!HeapTupleIsValid(tuple))
16011 ereport(ERROR,
16012 (errcode(ERRCODE_UNDEFINED_OBJECT),
16013 errmsg("foreign table \"%s\" does not exist",
16015 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
16016 server = GetForeignServer(fttableform->ftserver);
16017 fdw = GetForeignDataWrapper(server->fdwid);
16018
16020 ReleaseSysCache(tuple);
16021
16022 attrel = table_open(AttributeRelationId, RowExclusiveLock);
16023 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
16024 if (!HeapTupleIsValid(tuple))
16025 ereport(ERROR,
16026 (errcode(ERRCODE_UNDEFINED_COLUMN),
16027 errmsg("column \"%s\" of relation \"%s\" does not exist",
16028 colName, RelationGetRelationName(rel))));
16029
16030 /* Prevent them from altering a system attribute */
16031 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
16032 attnum = atttableform->attnum;
16033 if (attnum <= 0)
16034 ereport(ERROR,
16035 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16036 errmsg("cannot alter system column \"%s\"", colName)));
16037
16038
16039 /* Initialize buffers for new tuple values */
16040 memset(repl_val, 0, sizeof(repl_val));
16041 memset(repl_null, false, sizeof(repl_null));
16042 memset(repl_repl, false, sizeof(repl_repl));
16043
16044 /* Extract the current options */
16045 datum = SysCacheGetAttr(ATTNAME,
16046 tuple,
16047 Anum_pg_attribute_attfdwoptions,
16048 &isnull);
16049 if (isnull)
16050 datum = PointerGetDatum(NULL);
16051
16052 /* Transform the options */
16053 datum = transformGenericOptions(AttributeRelationId,
16054 datum,
16055 options,
16056 fdw->fdwvalidator);
16057
16058 if (DatumGetPointer(datum) != NULL)
16059 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
16060 else
16061 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
16062
16063 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
16064
16065 /* Everything looks good - update the tuple */
16066
16067 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16068 repl_val, repl_null, repl_repl);
16069
16070 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16071
16072 InvokeObjectPostAlterHook(RelationRelationId,
16073 RelationGetRelid(rel),
16074 atttableform->attnum);
16075 ObjectAddressSubSet(address, RelationRelationId,
16076 RelationGetRelid(rel), attnum);
16077
16078 ReleaseSysCache(tuple);
16079
16081
16082 heap_freetuple(newtuple);
16083
16084 return address;
16085}
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 14754 of file tablecmds.c.

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

12228{
12229 Relation conrel;
12230 Relation tgrel;
12231 SysScanDesc scan;
12232 ScanKeyData skey[3];
12233 HeapTuple contuple;
12234 Form_pg_constraint currcon;
12235 ObjectAddress address;
12236
12237 /*
12238 * Disallow altering ONLY a partitioned table, as it would make no sense.
12239 * This is okay for legacy inheritance.
12240 */
12241 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12242 ereport(ERROR,
12243 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12244 errmsg("constraint must be altered in child tables too"),
12245 errhint("Do not specify the ONLY keyword."));
12246
12247
12248 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12249 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12250
12251 /*
12252 * Find and check the target constraint
12253 */
12254 ScanKeyInit(&skey[0],
12255 Anum_pg_constraint_conrelid,
12256 BTEqualStrategyNumber, F_OIDEQ,
12258 ScanKeyInit(&skey[1],
12259 Anum_pg_constraint_contypid,
12260 BTEqualStrategyNumber, F_OIDEQ,
12262 ScanKeyInit(&skey[2],
12263 Anum_pg_constraint_conname,
12264 BTEqualStrategyNumber, F_NAMEEQ,
12265 CStringGetDatum(cmdcon->conname));
12266 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12267 true, NULL, 3, skey);
12268
12269 /* There can be at most one matching row */
12270 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12271 ereport(ERROR,
12272 (errcode(ERRCODE_UNDEFINED_OBJECT),
12273 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12274 cmdcon->conname, RelationGetRelationName(rel))));
12275
12276 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12277 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12278 ereport(ERROR,
12279 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12280 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12281 cmdcon->conname, RelationGetRelationName(rel))));
12282 if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12283 ereport(ERROR,
12284 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12285 errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12286 cmdcon->conname, RelationGetRelationName(rel))));
12287 if (cmdcon->alterInheritability &&
12288 currcon->contype != CONSTRAINT_NOTNULL)
12289 ereport(ERROR,
12290 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12291 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12292 cmdcon->conname, RelationGetRelationName(rel)));
12293
12294 /* Refuse to modify inheritability of inherited constraints */
12295 if (cmdcon->alterInheritability &&
12296 cmdcon->noinherit && currcon->coninhcount > 0)
12297 ereport(ERROR,
12298 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12299 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12300 NameStr(currcon->conname),
12302
12303 /*
12304 * If it's not the topmost constraint, raise an error.
12305 *
12306 * Altering a non-topmost constraint leaves some triggers untouched, since
12307 * they are not directly connected to this constraint; also, pg_dump would
12308 * ignore the deferrability status of the individual constraint, since it
12309 * only dumps topmost constraints. Avoid these problems by refusing this
12310 * operation and telling the user to alter the parent constraint instead.
12311 */
12312 if (OidIsValid(currcon->conparentid))
12313 {
12314 HeapTuple tp;
12315 Oid parent = currcon->conparentid;
12316 char *ancestorname = NULL;
12317 char *ancestortable = NULL;
12318
12319 /* Loop to find the topmost constraint */
12320 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12321 {
12323
12324 /* If no parent, this is the constraint we want */
12325 if (!OidIsValid(contup->conparentid))
12326 {
12327 ancestorname = pstrdup(NameStr(contup->conname));
12328 ancestortable = get_rel_name(contup->conrelid);
12329 ReleaseSysCache(tp);
12330 break;
12331 }
12332
12333 parent = contup->conparentid;
12334 ReleaseSysCache(tp);
12335 }
12336
12337 ereport(ERROR,
12338 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12339 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12340 cmdcon->conname, RelationGetRelationName(rel)),
12341 ancestorname && ancestortable ?
12342 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12343 cmdcon->conname, ancestorname, ancestortable) : 0,
12344 errhint("You may alter the constraint it derives from instead.")));
12345 }
12346
12347 address = InvalidObjectAddress;
12348
12349 /*
12350 * Do the actual catalog work, and recurse if necessary.
12351 */
12352 if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12353 contuple, recurse, lockmode))
12354 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12355
12356 systable_endscan(scan);
12357
12360
12361 return address;
12362}
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:12369

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

12373{
12374 Form_pg_constraint currcon;
12375 bool changed = false;
12376 List *otherrelids = NIL;
12377
12378 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12379
12380 /*
12381 * Do the catalog work for the enforceability or deferrability change,
12382 * recurse if necessary.
12383 *
12384 * Note that even if deferrability is requested to be altered along with
12385 * enforceability, we don't need to explicitly update multiple entries in
12386 * pg_trigger related to deferrability.
12387 *
12388 * Modifying enforceability involves either creating or dropping the
12389 * trigger, during which the deferrability setting will be adjusted
12390 * automatically.
12391 */
12392 if (cmdcon->alterEnforceability &&
12393 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12394 currcon->conrelid, currcon->confrelid,
12395 contuple, lockmode, InvalidOid,
12397 changed = true;
12398
12399 else if (cmdcon->alterDeferrability &&
12400 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12401 contuple, recurse, &otherrelids,
12402 lockmode))
12403 {
12404 /*
12405 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12406 * the relations having the constraint itself; here we also invalidate
12407 * for relations that have any triggers that are part of the
12408 * constraint.
12409 */
12410 foreach_oid(relid, otherrelids)
12412
12413 changed = true;
12414 }
12415
12416 /*
12417 * Do the catalog work for the inheritability change.
12418 */
12419 if (cmdcon->alterInheritability &&
12420 ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12421 lockmode))
12422 changed = true;
12423
12424 return changed;
12425}
#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:12643

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

12590{
12591 Form_pg_constraint currcon;
12592 Oid refrelid;
12593 bool changed = false;
12594
12595 /* since this function recurses, it could be driven to stack overflow */
12597
12598 Assert(cmdcon->alterDeferrability);
12599
12600 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12601 refrelid = currcon->confrelid;
12602
12603 /* Should be foreign key constraint */
12604 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12605
12606 /*
12607 * If called to modify a constraint that's already in the desired state,
12608 * silently do nothing.
12609 */
12610 if (currcon->condeferrable != cmdcon->deferrable ||
12611 currcon->condeferred != cmdcon->initdeferred)
12612 {
12613 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12614 changed = true;
12615
12616 /*
12617 * Now we need to update the multiple entries in pg_trigger that
12618 * implement the constraint.
12619 */
12620 AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12621 cmdcon->deferrable,
12622 cmdcon->initdeferred, otherrelids);
12623 }
12624
12625 /*
12626 * If the table at either end of the constraint is partitioned, we need to
12627 * handle every constraint that is a child of this one.
12628 */
12629 if (recurse && changed &&
12630 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12631 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12632 AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12633 contuple, recurse, otherrelids,
12634 lockmode);
12635
12636 return changed;
12637}
static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)
Definition: tablecmds.c:12882
static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12840
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
Definition: tablecmds.c:12722

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

12448{
12449 Form_pg_constraint currcon;
12450 Oid conoid;
12451 Relation rel;
12452 bool changed = false;
12453
12454 /* Since this function recurses, it could be driven to stack overflow */
12456
12457 Assert(cmdcon->alterEnforceability);
12458
12459 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12460 conoid = currcon->oid;
12461
12462 /* Should be foreign key constraint */
12463 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12464
12465 rel = table_open(currcon->conrelid, lockmode);
12466
12467 if (currcon->conenforced != cmdcon->is_enforced)
12468 {
12469 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12470 changed = true;
12471 }
12472
12473 /* Drop triggers */
12474 if (!cmdcon->is_enforced)
12475 {
12476 /*
12477 * When setting a constraint to NOT ENFORCED, the constraint triggers
12478 * need to be dropped. Therefore, we must process the child relations
12479 * first, followed by the parent, to account for dependencies.
12480 */
12481 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12482 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12483 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12484 fkrelid, pkrelid, contuple,
12485 lockmode, InvalidOid, InvalidOid,
12487
12488 /* Drop all the triggers */
12490 }
12491 else if (changed) /* Create triggers */
12492 {
12493 Oid ReferencedDelTriggerOid = InvalidOid,
12494 ReferencedUpdTriggerOid = InvalidOid,
12495 ReferencingInsTriggerOid = InvalidOid,
12496 ReferencingUpdTriggerOid = InvalidOid;
12497
12498 /* Prepare the minimal information required for trigger creation. */
12499 Constraint *fkconstraint = makeNode(Constraint);
12500
12501 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12502 fkconstraint->fk_matchtype = currcon->confmatchtype;
12503 fkconstraint->fk_upd_action = currcon->confupdtype;
12504 fkconstraint->fk_del_action = currcon->confdeltype;
12505
12506 /* Create referenced triggers */
12507 if (currcon->conrelid == fkrelid)
12508 createForeignKeyActionTriggers(currcon->conrelid,
12509 currcon->confrelid,
12510 fkconstraint,
12511 conoid,
12512 currcon->conindid,
12513 ReferencedParentDelTrigger,
12514 ReferencedParentUpdTrigger,
12515 &ReferencedDelTriggerOid,
12516 &ReferencedUpdTriggerOid);
12517
12518 /* Create referencing triggers */
12519 if (currcon->confrelid == pkrelid)
12520 createForeignKeyCheckTriggers(currcon->conrelid,
12521 pkrelid,
12522 fkconstraint,
12523 conoid,
12524 currcon->conindid,
12525 ReferencingParentInsTrigger,
12526 ReferencingParentUpdTrigger,
12527 &ReferencingInsTriggerOid,
12528 &ReferencingUpdTriggerOid);
12529
12530 /*
12531 * Tell Phase 3 to check that the constraint is satisfied by existing
12532 * rows. Only applies to leaf partitions, and (for constraints that
12533 * reference a partitioned table) only if this is not one of the
12534 * pg_constraint rows that exist solely to support action triggers.
12535 */
12536 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12537 currcon->confrelid == pkrelid)
12538 {
12539 AlteredTableInfo *tab;
12540 NewConstraint *newcon;
12541
12542 newcon = palloc0_object(NewConstraint);
12543 newcon->name = fkconstraint->conname;
12544 newcon->contype = CONSTR_FOREIGN;
12545 newcon->refrelid = currcon->confrelid;
12546 newcon->refindid = currcon->conindid;
12547 newcon->conid = currcon->oid;
12548 newcon->qual = (Node *) fkconstraint;
12549
12550 /* Find or create work queue entry for this table */
12551 tab = ATGetQueueEntry(wqueue, rel);
12552 tab->constraints = lappend(tab->constraints, newcon);
12553 }
12554
12555 /*
12556 * If the table at either end of the constraint is partitioned, we
12557 * need to recurse and create triggers for each constraint that is a
12558 * child of this one.
12559 */
12560 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12561 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12562 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12563 fkrelid, pkrelid, contuple,
12564 lockmode, ReferencedDelTriggerOid,
12565 ReferencedUpdTriggerOid,
12566 ReferencingInsTriggerOid,
12567 ReferencingUpdTriggerOid);
12568 }
12569
12570 table_close(rel, NoLock);
12571
12572 return changed;
12573}
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:12791
static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)
Definition: tablecmds.c:12031

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

12646{
12647 Form_pg_constraint currcon;
12648 AttrNumber colNum;
12649 char *colName;
12650 List *children;
12651
12652 Assert(cmdcon->alterInheritability);
12653
12654 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12655
12656 /* The current implementation only works for NOT NULL constraints */
12657 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12658
12659 /*
12660 * If called to modify a constraint that's already in the desired state,
12661 * silently do nothing.
12662 */
12663 if (cmdcon->noinherit == currcon->connoinherit)
12664 return false;
12665
12666 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12668
12669 /* Fetch the column number and name */
12670 colNum = extractNotNullColumn(contuple);
12671 colName = get_attname(currcon->conrelid, colNum, false);
12672
12673 /*
12674 * Propagate the change to children. For this subcommand type we don't
12675 * recursively affect children, just the immediate level.
12676 */
12678 lockmode);
12679 foreach_oid(childoid, children)
12680 {
12681 ObjectAddress addr;
12682
12683 if (cmdcon->noinherit)
12684 {
12685 HeapTuple childtup;
12686 Form_pg_constraint childcon;
12687
12688 childtup = findNotNullConstraint(childoid, colName);
12689 if (!childtup)
12690 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12691 colName, childoid);
12692 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12693 Assert(childcon->coninhcount > 0);
12694 childcon->coninhcount--;
12695 childcon->conislocal = true;
12696 CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12697 heap_freetuple(childtup);
12698 }
12699 else
12700 {
12701 Relation childrel = table_open(childoid, NoLock);
12702
12703 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12704 colName, true, true, lockmode);
12705 if (OidIsValid(addr.objectId))
12707 table_close(childrel, NoLock);
12708 }
12709 }
12710
12711 return true;
12712}
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:918
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:7941

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

20312{
20313 Relation attachrel,
20314 catalog;
20315 List *attachrel_children;
20316 List *partConstraint;
20317 SysScanDesc scan;
20318 ScanKeyData skey;
20319 AttrNumber attno;
20320 int natts;
20321 TupleDesc tupleDesc;
20322 ObjectAddress address;
20323 const char *trigger_name;
20324 Oid defaultPartOid;
20325 List *partBoundConstraint;
20326 ParseState *pstate = make_parsestate(NULL);
20327
20328 pstate->p_sourcetext = context->queryString;
20329
20330 /*
20331 * We must lock the default partition if one exists, because attaching a
20332 * new partition will change its partition constraint.
20333 */
20334 defaultPartOid =
20336 if (OidIsValid(defaultPartOid))
20337 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20338
20339 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20340
20341 /*
20342 * XXX I think it'd be a good idea to grab locks on all tables referenced
20343 * by FKs at this point also.
20344 */
20345
20346 /*
20347 * Must be owner of both parent and source table -- parent was checked by
20348 * ATSimplePermissions call in ATPrepCmd
20349 */
20352
20353 /* A partition can only have one parent */
20354 if (attachrel->rd_rel->relispartition)
20355 ereport(ERROR,
20356 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20357 errmsg("\"%s\" is already a partition",
20358 RelationGetRelationName(attachrel))));
20359
20360 if (OidIsValid(attachrel->rd_rel->reloftype))
20361 ereport(ERROR,
20362 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20363 errmsg("cannot attach a typed table as partition")));
20364
20365 /*
20366 * Table being attached should not already be part of inheritance; either
20367 * as a child table...
20368 */
20369 catalog = table_open(InheritsRelationId, AccessShareLock);
20370 ScanKeyInit(&skey,
20371 Anum_pg_inherits_inhrelid,
20372 BTEqualStrategyNumber, F_OIDEQ,
20374 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20375 NULL, 1, &skey);
20377 ereport(ERROR,
20378 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20379 errmsg("cannot attach inheritance child as partition")));
20380 systable_endscan(scan);
20381
20382 /* ...or as a parent table (except the case when it is partitioned) */
20383 ScanKeyInit(&skey,
20384 Anum_pg_inherits_inhparent,
20385 BTEqualStrategyNumber, F_OIDEQ,
20387 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20388 1, &skey);
20390 attachrel->rd_rel->relkind == RELKIND_RELATION)
20391 ereport(ERROR,
20392 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20393 errmsg("cannot attach inheritance parent as partition")));
20394 systable_endscan(scan);
20395 table_close(catalog, AccessShareLock);
20396
20397 /*
20398 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20399 * particular, this disallows making a rel a partition of itself.)
20400 *
20401 * We do that by checking if rel is a member of the list of attachrel's
20402 * partitions provided the latter is partitioned at all. We want to avoid
20403 * having to construct this list again, so we request the strongest lock
20404 * on all partitions. We need the strongest lock, because we may decide
20405 * to scan them if we find out that the table being attached (or its leaf
20406 * partitions) may contain rows that violate the partition constraint. If
20407 * the table has a constraint that would prevent such rows, which by
20408 * definition is present in all the partitions, we need not scan the
20409 * table, nor its partitions. But we cannot risk a deadlock by taking a
20410 * weaker lock now and the stronger one only when needed.
20411 */
20412 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20413 AccessExclusiveLock, NULL);
20414 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20415 ereport(ERROR,
20416 (errcode(ERRCODE_DUPLICATE_TABLE),
20417 errmsg("circular inheritance not allowed"),
20418 errdetail("\"%s\" is already a child of \"%s\".",
20420 RelationGetRelationName(attachrel))));
20421
20422 /* If the parent is permanent, so must be all of its partitions. */
20423 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20424 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20425 ereport(ERROR,
20426 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20427 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20429
20430 /* Temp parent cannot have a partition that is itself not a temp */
20431 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20432 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20433 ereport(ERROR,
20434 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20435 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20437
20438 /* If the parent is temp, it must belong to this session */
20439 if (RELATION_IS_OTHER_TEMP(rel))
20440 ereport(ERROR,
20441 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20442 errmsg("cannot attach as partition of temporary relation of another session")));
20443
20444 /* Ditto for the partition */
20445 if (RELATION_IS_OTHER_TEMP(attachrel))
20446 ereport(ERROR,
20447 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20448 errmsg("cannot attach temporary relation of another session as partition")));
20449
20450 /*
20451 * Check if attachrel has any identity columns or any columns that aren't
20452 * in the parent.
20453 */
20454 tupleDesc = RelationGetDescr(attachrel);
20455 natts = tupleDesc->natts;
20456 for (attno = 1; attno <= natts; attno++)
20457 {
20458 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20459 char *attributeName = NameStr(attribute->attname);
20460
20461 /* Ignore dropped */
20462 if (attribute->attisdropped)
20463 continue;
20464
20465 if (attribute->attidentity)
20466 ereport(ERROR,
20467 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20468 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20469 RelationGetRelationName(attachrel), attributeName),
20470 errdetail("The new partition may not contain an identity column."));
20471
20472 /* Try to find the column in parent (matching on column name) */
20473 if (!SearchSysCacheExists2(ATTNAME,
20475 CStringGetDatum(attributeName)))
20476 ereport(ERROR,
20477 (errcode(ERRCODE_DATATYPE_MISMATCH),
20478 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20479 RelationGetRelationName(attachrel), attributeName,
20481 errdetail("The new partition may contain only the columns present in parent.")));
20482 }
20483
20484 /*
20485 * If child_rel has row-level triggers with transition tables, we
20486 * currently don't allow it to become a partition. See also prohibitions
20487 * in ATExecAddInherit() and CreateTrigger().
20488 */
20489 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20490 if (trigger_name != NULL)
20491 ereport(ERROR,
20492 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20493 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20494 trigger_name, RelationGetRelationName(attachrel)),
20495 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20496
20497 /*
20498 * Check that the new partition's bound is valid and does not overlap any
20499 * of existing partitions of the parent - note that it does not return on
20500 * error.
20501 */
20503 cmd->bound, pstate);
20504
20505 attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
20506
20507 /*
20508 * Generate a partition constraint from the partition bound specification.
20509 * If the parent itself is a partition, make sure to include its
20510 * constraint as well.
20511 */
20512 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20513
20514 /*
20515 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20516 * since it's needed later to construct the constraint expression for
20517 * validating against the default partition, if any.
20518 */
20519 partConstraint = list_concat_copy(partBoundConstraint,
20521
20522 /* Skip validation if there are no constraints to validate. */
20523 if (partConstraint)
20524 {
20525 /*
20526 * Run the partition quals through const-simplification similar to
20527 * check constraints. We skip canonicalize_qual, though, because
20528 * partition quals should be in canonical form already.
20529 */
20530 partConstraint =
20532 (Node *) partConstraint);
20533
20534 /* XXX this sure looks wrong */
20535 partConstraint = list_make1(make_ands_explicit(partConstraint));
20536
20537 /*
20538 * Adjust the generated constraint to match this partition's attribute
20539 * numbers.
20540 */
20541 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20542 rel);
20543
20544 /* Validate partition constraints against the table being attached. */
20545 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20546 false);
20547 }
20548
20549 /*
20550 * If we're attaching a partition other than the default partition and a
20551 * default one exists, then that partition's partition constraint changes,
20552 * so add an entry to the work queue to validate it, too. (We must not do
20553 * this when the partition being attached is the default one; we already
20554 * did it above!)
20555 */
20556 if (OidIsValid(defaultPartOid))
20557 {
20558 Relation defaultrel;
20559 List *defPartConstraint;
20560
20561 Assert(!cmd->bound->is_default);
20562
20563 /* we already hold a lock on the default partition */
20564 defaultrel = table_open(defaultPartOid, NoLock);
20565 defPartConstraint =
20566 get_proposed_default_constraint(partBoundConstraint);
20567
20568 /*
20569 * Map the Vars in the constraint expression from rel's attnos to
20570 * defaultrel's.
20571 */
20572 defPartConstraint =
20573 map_partition_varattnos(defPartConstraint,
20574 1, defaultrel, rel);
20575 QueuePartitionConstraintValidation(wqueue, defaultrel,
20576 defPartConstraint, true);
20577
20578 /* keep our lock until commit. */
20579 table_close(defaultrel, NoLock);
20580 }
20581
20582 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20583
20584 /*
20585 * If the partition we just attached is partitioned itself, invalidate
20586 * relcache for all descendent partitions too to ensure that their
20587 * rd_partcheck expression trees are rebuilt; partitions already locked at
20588 * the beginning of this function.
20589 */
20590 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20591 {
20592 ListCell *l;
20593
20594 foreach(l, attachrel_children)
20595 {
20597 }
20598 }
20599
20600 /* keep our lock until commit */
20601 table_close(attachrel, NoLock);
20602
20603 return address;
20604}
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2270
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:2889
List * get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:250
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:992
RangeVar * name
Definition: parsenodes.h:989
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition: syscache.h:102
static void attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
Definition: tablecmds.c:20280
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
Definition: tablecmds.c:20203

References AccessExclusiveLock, AccessShareLock, Assert(), AT_AttachPartition, ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, attachPartitionTable(), PartitionCmd::bound, BTEqualStrategyNumber, CacheInvalidateRelcacheByRelid(), check_new_partition_bound(), 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, 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 21622 of file tablecmds.c.

21623{
21624 Relation partIdx;
21625 Relation partTbl;
21626 Relation parentTbl;
21627 ObjectAddress address;
21628 Oid partIdxId;
21629 Oid currParent;
21631
21632 /*
21633 * We need to obtain lock on the index 'name' to modify it, but we also
21634 * need to read its owning table's tuple descriptor -- so we need to lock
21635 * both. To avoid deadlocks, obtain lock on the table before doing so on
21636 * the index. Furthermore, we need to examine the parent table of the
21637 * partition, so lock that one too.
21638 */
21639 state.partitionOid = InvalidOid;
21640 state.parentTblOid = parentIdx->rd_index->indrelid;
21641 state.lockedParentTbl = false;
21642 partIdxId =
21645 &state);
21646 /* Not there? */
21647 if (!OidIsValid(partIdxId))
21648 ereport(ERROR,
21649 (errcode(ERRCODE_UNDEFINED_OBJECT),
21650 errmsg("index \"%s\" does not exist", name->relname)));
21651
21652 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21653 partIdx = relation_open(partIdxId, AccessExclusiveLock);
21654
21655 /* we already hold locks on both tables, so this is safe: */
21656 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21657 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21658
21659 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21660
21661 /* Silently do nothing if already in the right state */
21662 currParent = partIdx->rd_rel->relispartition ?
21663 get_partition_parent(partIdxId, false) : InvalidOid;
21664 if (currParent != RelationGetRelid(parentIdx))
21665 {
21666 IndexInfo *childInfo;
21667 IndexInfo *parentInfo;
21668 AttrMap *attmap;
21669 bool found;
21670 int i;
21671 PartitionDesc partDesc;
21672 Oid constraintOid,
21673 cldConstrId = InvalidOid;
21674
21675 /*
21676 * If this partition already has an index attached, refuse the
21677 * operation.
21678 */
21679 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21680
21681 if (OidIsValid(currParent))
21682 ereport(ERROR,
21683 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21684 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21685 RelationGetRelationName(partIdx),
21686 RelationGetRelationName(parentIdx)),
21687 errdetail("Index \"%s\" is already attached to another index.",
21688 RelationGetRelationName(partIdx))));
21689
21690 /* Make sure it indexes a partition of the other index's table */
21691 partDesc = RelationGetPartitionDesc(parentTbl, true);
21692 found = false;
21693 for (i = 0; i < partDesc->nparts; i++)
21694 {
21695 if (partDesc->oids[i] == state.partitionOid)
21696 {
21697 found = true;
21698 break;
21699 }
21700 }
21701 if (!found)
21702 ereport(ERROR,
21703 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21704 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21705 RelationGetRelationName(partIdx),
21706 RelationGetRelationName(parentIdx)),
21707 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21708 RelationGetRelationName(partIdx),
21709 RelationGetRelationName(parentTbl))));
21710
21711 /* Ensure the indexes are compatible */
21712 childInfo = BuildIndexInfo(partIdx);
21713 parentInfo = BuildIndexInfo(parentIdx);
21714 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21715 RelationGetDescr(parentTbl),
21716 false);
21717 if (!CompareIndexInfo(childInfo, parentInfo,
21718 partIdx->rd_indcollation,
21719 parentIdx->rd_indcollation,
21720 partIdx->rd_opfamily,
21721 parentIdx->rd_opfamily,
21722 attmap))
21723 ereport(ERROR,
21724 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21725 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21726 RelationGetRelationName(partIdx),
21727 RelationGetRelationName(parentIdx)),
21728 errdetail("The index definitions do not match.")));
21729
21730 /*
21731 * If there is a constraint in the parent, make sure there is one in
21732 * the child too.
21733 */
21734 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21735 RelationGetRelid(parentIdx));
21736
21737 if (OidIsValid(constraintOid))
21738 {
21740 partIdxId);
21741 if (!OidIsValid(cldConstrId))
21742 ereport(ERROR,
21743 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21744 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21745 RelationGetRelationName(partIdx),
21746 RelationGetRelationName(parentIdx)),
21747 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21748 RelationGetRelationName(parentIdx),
21749 RelationGetRelationName(parentTbl),
21750 RelationGetRelationName(partIdx))));
21751 }
21752
21753 /*
21754 * If it's a primary key, make sure the columns in the partition are
21755 * NOT NULL.
21756 */
21757 if (parentIdx->rd_index->indisprimary)
21758 verifyPartitionIndexNotNull(childInfo, partTbl);
21759
21760 /* All good -- do it */
21761 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21762 if (OidIsValid(constraintOid))
21763 ConstraintSetParentConstraint(cldConstrId, constraintOid,
21764 RelationGetRelid(partTbl));
21765
21766 free_attrmap(attmap);
21767
21768 validatePartitionedIndex(parentIdx, parentTbl);
21769 }
21770
21771 relation_close(parentTbl, AccessShareLock);
21772 /* keep these locks till commit */
21773 relation_close(partTbl, NoLock);
21774 relation_close(partIdx, NoLock);
21775
21776 return address;
21777}
bool CompareIndexInfo(const IndexInfo *info1, const IndexInfo *info2, const Oid *collations1, const Oid *collations2, const Oid *opfamilies1, const Oid *opfamilies2, const AttrMap *attmap)
Definition: index.c:2537
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition: indexcmds.c:4445
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition: partition.c:53
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
Form_pg_index rd_index
Definition: rel.h:192
Oid * rd_opfamily
Definition: rel.h:207
Oid * rd_indcollation
Definition: rel.h:217
Definition: regguts.h:323
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:21568
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
Definition: tablecmds.c:21808
static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
Definition: tablecmds.c:21910
static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
Definition: tablecmds.c:21784
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 16100 of file tablecmds.c.

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

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

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

◆ ATExecClusterOn()

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

Definition at line 16475 of file tablecmds.c.

16476{
16477 Oid indexOid;
16478 ObjectAddress address;
16479
16480 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16481
16482 if (!OidIsValid(indexOid))
16483 ereport(ERROR,
16484 (errcode(ERRCODE_UNDEFINED_OBJECT),
16485 errmsg("index \"%s\" for table \"%s\" does not exist",
16486 indexName, RelationGetRelationName(rel))));
16487
16488 /* Check index is valid to cluster on */
16489 check_index_is_clusterable(rel, indexOid, lockmode);
16490
16491 /* And do the work */
16492 mark_index_clustered(rel, indexOid, false);
16493
16494 ObjectAddressSet(address,
16495 RelationRelationId, indexOid);
16496
16497 return address;
16498}
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 5384 of file tablecmds.c.

5387{
5389 Relation rel = tab->rel;
5390
5391 switch (cmd->subtype)
5392 {
5393 case AT_AddColumn: /* ADD COLUMN */
5394 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5395 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5396 cmd->recurse, false,
5397 lockmode, cur_pass, context);
5398 break;
5399 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5400 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5401 break;
5402 case AT_CookedColumnDefault: /* add a pre-cooked default */
5403 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5404 break;
5405 case AT_AddIdentity:
5406 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5407 cur_pass, context);
5408 Assert(cmd != NULL);
5409 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5410 break;
5411 case AT_SetIdentity:
5412 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5413 cur_pass, context);
5414 Assert(cmd != NULL);
5415 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5416 break;
5417 case AT_DropIdentity:
5418 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5419 break;
5420 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5421 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5422 break;
5423 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5424 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5425 cmd->recurse, false, lockmode);
5426 break;
5427 case AT_SetExpression:
5428 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5429 break;
5430 case AT_DropExpression:
5431 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5432 break;
5433 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5434 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5435 break;
5436 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5437 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5438 break;
5439 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5440 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5441 break;
5442 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5443 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5444 break;
5445 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5446 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5447 lockmode);
5448 break;
5449 case AT_DropColumn: /* DROP COLUMN */
5450 address = ATExecDropColumn(wqueue, rel, cmd->name,
5451 cmd->behavior, cmd->recurse, false,
5452 cmd->missing_ok, lockmode,
5453 NULL);
5454 break;
5455 case AT_AddIndex: /* ADD INDEX */
5456 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5457 lockmode);
5458 break;
5459 case AT_ReAddIndex: /* ADD INDEX */
5460 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5461 lockmode);
5462 break;
5463 case AT_ReAddStatistics: /* ADD STATISTICS */
5464 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5465 true, lockmode);
5466 break;
5467 case AT_AddConstraint: /* ADD CONSTRAINT */
5468 /* Transform the command only during initial examination */
5469 if (cur_pass == AT_PASS_ADD_CONSTR)
5470 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5471 cmd->recurse, lockmode,
5472 cur_pass, context);
5473 /* Depending on constraint type, might be no more work to do now */
5474 if (cmd != NULL)
5475 address =
5476 ATExecAddConstraint(wqueue, tab, rel,
5477 (Constraint *) cmd->def,
5478 cmd->recurse, false, lockmode);
5479 break;
5480 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5481 address =
5482 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5483 true, true, lockmode);
5484 break;
5485 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5486 * constraint */
5487 address =
5488 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5489 ((AlterDomainStmt *) cmd->def)->def,
5490 NULL);
5491 break;
5492 case AT_ReAddComment: /* Re-add existing comment */
5493 address = CommentObject((CommentStmt *) cmd->def);
5494 break;
5495 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5496 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5497 lockmode);
5498 break;
5499 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5500 address = ATExecAlterConstraint(wqueue, rel,
5502 cmd->recurse, lockmode);
5503 break;
5504 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5505 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5506 false, lockmode);
5507 break;
5508 case AT_DropConstraint: /* DROP CONSTRAINT */
5509 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5510 cmd->recurse,
5511 cmd->missing_ok, lockmode);
5512 break;
5513 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5514 /* parse transformation was done earlier */
5515 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5516 break;
5517 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5518 address =
5520 (List *) cmd->def, lockmode);
5521 break;
5522 case AT_ChangeOwner: /* ALTER OWNER */
5524 get_rolespec_oid(cmd->newowner, false),
5525 false, lockmode);
5526 break;
5527 case AT_ClusterOn: /* CLUSTER ON */
5528 address = ATExecClusterOn(rel, cmd->name, lockmode);
5529 break;
5530 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5531 ATExecDropCluster(rel, lockmode);
5532 break;
5533 case AT_SetLogged: /* SET LOGGED */
5534 case AT_SetUnLogged: /* SET UNLOGGED */
5535 break;
5536 case AT_DropOids: /* SET WITHOUT OIDS */
5537 /* nothing to do here, oid columns don't exist anymore */
5538 break;
5539 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5540
5541 /*
5542 * Only do this for partitioned tables, for which this is just a
5543 * catalog change. Tables with storage are handled by Phase 3.
5544 */
5545 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5546 tab->chgAccessMethod)
5548 break;
5549 case AT_SetTableSpace: /* SET TABLESPACE */
5550
5551 /*
5552 * Only do this for partitioned tables and indexes, for which this
5553 * is just a catalog change. Other relation types which have
5554 * storage are handled by Phase 3.
5555 */
5556 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5557 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5559
5560 break;
5561 case AT_SetRelOptions: /* SET (...) */
5562 case AT_ResetRelOptions: /* RESET (...) */
5563 case AT_ReplaceRelOptions: /* replace entire option list */
5564 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5565 break;
5566 case AT_EnableTrig: /* ENABLE TRIGGER name */
5569 cmd->recurse,
5570 lockmode);
5571 break;
5572 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5574 TRIGGER_FIRES_ALWAYS, false,
5575 cmd->recurse,
5576 lockmode);
5577 break;
5578 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5581 cmd->recurse,
5582 lockmode);
5583 break;
5584 case AT_DisableTrig: /* DISABLE TRIGGER name */
5586 TRIGGER_DISABLED, false,
5587 cmd->recurse,
5588 lockmode);
5589 break;
5590 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5593 cmd->recurse,
5594 lockmode);
5595 break;
5596 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5598 TRIGGER_DISABLED, false,
5599 cmd->recurse,
5600 lockmode);
5601 break;
5602 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5605 cmd->recurse,
5606 lockmode);
5607 break;
5608 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5610 TRIGGER_DISABLED, true,
5611 cmd->recurse,
5612 lockmode);
5613 break;
5614
5615 case AT_EnableRule: /* ENABLE RULE name */
5616 ATExecEnableDisableRule(rel, cmd->name,
5617 RULE_FIRES_ON_ORIGIN, lockmode);
5618 break;
5619 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5620 ATExecEnableDisableRule(rel, cmd->name,
5621 RULE_FIRES_ALWAYS, lockmode);
5622 break;
5623 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5624 ATExecEnableDisableRule(rel, cmd->name,
5625 RULE_FIRES_ON_REPLICA, lockmode);
5626 break;
5627 case AT_DisableRule: /* DISABLE RULE name */
5628 ATExecEnableDisableRule(rel, cmd->name,
5629 RULE_DISABLED, lockmode);
5630 break;
5631
5632 case AT_AddInherit:
5633 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5634 break;
5635 case AT_DropInherit:
5636 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5637 break;
5638 case AT_AddOf:
5639 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5640 break;
5641 case AT_DropOf:
5642 ATExecDropOf(rel, lockmode);
5643 break;
5644 case AT_ReplicaIdentity:
5645 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5646 break;
5648 ATExecSetRowSecurity(rel, true);
5649 break;
5651 ATExecSetRowSecurity(rel, false);
5652 break;
5655 break;
5658 break;
5659 case AT_GenericOptions:
5660 ATExecGenericOptions(rel, (List *) cmd->def);
5661 break;
5662 case AT_AttachPartition:
5663 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5664 cur_pass, context);
5665 Assert(cmd != NULL);
5666 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5667 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5668 context);
5669 else
5670 address = ATExecAttachPartitionIdx(wqueue, rel,
5671 ((PartitionCmd *) cmd->def)->name);
5672 break;
5673 case AT_DetachPartition:
5674 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5675 cur_pass, context);
5676 Assert(cmd != NULL);
5677 /* ATPrepCmd ensures it must be a table */
5678 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5679 address = ATExecDetachPartition(wqueue, tab, rel,
5680 ((PartitionCmd *) cmd->def)->name,
5681 ((PartitionCmd *) cmd->def)->concurrent);
5682 break;
5684 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5685 break;
5686 case AT_MergePartitions:
5687 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5688 cur_pass, context);
5689 Assert(cmd != NULL);
5690 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5691 ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5692 context);
5693 break;
5694 case AT_SplitPartition:
5695 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5696 cur_pass, context);
5697 Assert(cmd != NULL);
5698 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5699 ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5700 context);
5701 break;
5702 default: /* oops */
5703 elog(ERROR, "unrecognized alter table type: %d",
5704 (int) cmd->subtype);
5705 break;
5706 }
5707
5708 /*
5709 * Report the subcommand to interested event triggers.
5710 */
5711 if (cmd)
5712 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5713
5714 /*
5715 * Bump the command counter to ensure the next subcommand in the sequence
5716 * can see the changes so far
5717 */
5719}
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:2521
DropBehavior behavior
Definition: parsenodes.h:2524
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:15983
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17289
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:18384
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12226
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:8830
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition: tablecmds.c:8154
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8516
static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:12936
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:7770
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9824
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
Definition: tablecmds.c:20954
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:18660
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:17232
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
Definition: tablecmds.c:9079
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:9312
static void ATExecSetRowSecurity(Relation rel, bool rls)
Definition: tablecmds.c:18630
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition: tablecmds.c:8239
static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:22743
static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
Definition: tablecmds.c:16673
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8399
static void ATExecGenericOptions(Relation rel, List *options)
Definition: tablecmds.c:18689
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8934
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode)
Definition: tablecmds.c:14754
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:16507
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
Definition: tablecmds.c:8630
static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
Definition: tablecmds.c:18242
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17851
static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:9732
static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:18516
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:21461
static ObjectAddress ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:9221
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:20310
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
Definition: tablecmds.c:16553
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14040
static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9648
static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:18770
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:16974
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:21622
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:23197
static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9711
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
Definition: tablecmds.c:16475
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:17250
#define TRIGGER_DISABLED
Definition: trigger.h:152
#define TRIGGER_FIRES_ON_REPLICA
Definition: trigger.h:151
#define TRIGGER_FIRES_ALWAYS
Definition: trigger.h:150
ObjectAddress AlterDomainAddConstraint(List *names, Node *newConstraint, ObjectAddress *constrAddr)
Definition: typecmds.c:2938

References AlterDomainAddConstraint(), Assert(), AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AddInherit, AT_AddOf, AT_AlterColumnGenericOptions, AT_AlterColumnType, AT_AlterConstraint, AT_AttachPartition, AT_ChangeOwner, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_MergePartitions, 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_SplitPartition, 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(), ATExecMergePartitions(), ATExecReplicaIdentity(), ATExecSetAccessMethodNoStorage(), ATExecSetCompression(), ATExecSetExpression(), ATExecSetIdentity(), ATExecSetNotNull(), ATExecSetOptions(), ATExecSetRelOptions(), ATExecSetRowSecurity(), ATExecSetStatistics(), ATExecSetStorage(), ATExecSetTableSpaceNoStorage(), ATExecSplitPartition(), 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 8154 of file tablecmds.c.

8156{
8157 TupleDesc tupdesc = RelationGetDescr(rel);
8159 ObjectAddress address;
8160
8161 /*
8162 * get the number of the attribute
8163 */
8164 attnum = get_attnum(RelationGetRelid(rel), colName);
8166 ereport(ERROR,
8167 (errcode(ERRCODE_UNDEFINED_COLUMN),
8168 errmsg("column \"%s\" of relation \"%s\" does not exist",
8169 colName, RelationGetRelationName(rel))));
8170
8171 /* Prevent them from altering a system attribute */
8172 if (attnum <= 0)
8173 ereport(ERROR,
8174 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8175 errmsg("cannot alter system column \"%s\"",
8176 colName)));
8177
8178 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8179 ereport(ERROR,
8180 (errcode(ERRCODE_SYNTAX_ERROR),
8181 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8182 colName, RelationGetRelationName(rel)),
8183 /* translator: %s is an SQL ALTER command */
8184 newDefault ? 0 : errhint("Use %s instead.",
8185 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8186
8187 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8188 ereport(ERROR,
8189 (errcode(ERRCODE_SYNTAX_ERROR),
8190 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8191 colName, RelationGetRelationName(rel)),
8192 newDefault ?
8193 /* translator: %s is an SQL ALTER command */
8194 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8195 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8196 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8197
8198 /*
8199 * Remove any old default for the column. We use RESTRICT here for
8200 * safety, but at present we do not expect anything to depend on the
8201 * default.
8202 *
8203 * We treat removing the existing default as an internal operation when it
8204 * is preparatory to adding a new default, but as a user-initiated
8205 * operation when the user asked for a drop.
8206 */
8208 newDefault != NULL);
8209
8210 if (newDefault)
8211 {
8212 /* SET DEFAULT */
8213 RawColumnDefault *rawEnt;
8214
8216 rawEnt->attnum = attnum;
8217 rawEnt->raw_default = newDefault;
8218 rawEnt->generated = '\0';
8219
8220 /*
8221 * This function is intended for CREATE TABLE, so it processes a
8222 * _list_ of defaults, but we just do one.
8223 */
8225 false, true, false, NULL);
8226 }
8227
8228 ObjectAddressSubSet(address, RelationRelationId,
8229 RelationGetRelid(rel), attnum);
8230 return address;
8231}
#define InvalidAttrNumber
Definition: attnum.h:23
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:949

References AddRelationNewConstraints(), RawColumnDefault::attnum, attnum, DROP_RESTRICT, ereport, errcode(), errhint(), errmsg(), ERROR, RawColumnDefault::generated, get_attnum(), InvalidAttrNumber, list_make1, NIL, ObjectAddressSubSet, palloc_object, 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 8239 of file tablecmds.c.

8241{
8242 ObjectAddress address;
8243
8244 /* We assume no checking is required */
8245
8246 /*
8247 * Remove any old default for the column. We use RESTRICT here for
8248 * safety, but at present we do not expect anything to depend on the
8249 * default. (In ordinary cases, there could not be a default in place
8250 * anyway, but it's possible when combining LIKE with inheritance.)
8251 */
8253 true);
8254
8255 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8256
8257 ObjectAddressSubSet(address, RelationRelationId,
8258 RelationGetRelid(rel), attnum);
8259 return address;
8260}

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

20956{
20957 Relation partRel;
20958 ObjectAddress address;
20959 Oid defaultPartOid;
20960
20961 /*
20962 * We must lock the default partition, because detaching this partition
20963 * will change its partition constraint.
20964 */
20965 defaultPartOid =
20967 if (OidIsValid(defaultPartOid))
20968 {
20969 /*
20970 * Concurrent detaching when a default partition exists is not
20971 * supported. The main problem is that the default partition
20972 * constraint would change. And there's a definitional problem: what
20973 * should happen to the tuples that are being inserted that belong to
20974 * the partition being detached? Putting them on the partition being
20975 * detached would be wrong, since they'd become "lost" after the
20976 * detaching completes but we cannot put them in the default partition
20977 * either until we alter its partition constraint.
20978 *
20979 * I think we could solve this problem if we effected the constraint
20980 * change before committing the first transaction. But the lock would
20981 * have to remain AEL and it would cause concurrent query planning to
20982 * be blocked, so changing it that way would be even worse.
20983 */
20984 if (concurrent)
20985 ereport(ERROR,
20986 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20987 errmsg("cannot detach partitions concurrently when a default partition exists")));
20988 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20989 }
20990
20991 /*
20992 * In concurrent mode, the partition is locked with share-update-exclusive
20993 * in the first transaction. This allows concurrent transactions to be
20994 * doing DML to the partition.
20995 */
20996 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20998
20999 /*
21000 * Check inheritance conditions and either delete the pg_inherits row (in
21001 * non-concurrent mode) or just set the inhdetachpending flag.
21002 */
21003 if (!concurrent)
21004 RemoveInheritance(partRel, rel, false);
21005 else
21006 MarkInheritDetached(partRel, rel);
21007
21008 /*
21009 * Ensure that foreign keys still hold after this detach. This keeps
21010 * locks on the referencing tables, which prevents concurrent transactions
21011 * from adding rows that we wouldn't see. For this to work in concurrent
21012 * mode, it is critical that the partition appears as no longer attached
21013 * for the RI queries as soon as the first transaction commits.
21014 */
21016
21017 /*
21018 * Concurrent mode has to work harder; first we add a new constraint to
21019 * the partition that matches the partition constraint. Then we close our
21020 * existing transaction, and in a new one wait for all processes to catch
21021 * up on the catalog updates we've done so far; at that point we can
21022 * complete the operation.
21023 */
21024 if (concurrent)
21025 {
21026 Oid partrelid,
21027 parentrelid;
21028 LOCKTAG tag;
21029 char *parentrelname;
21030 char *partrelname;
21031
21032 /*
21033 * We're almost done now; the only traces that remain are the
21034 * pg_inherits tuple and the partition's relpartbounds. Before we can
21035 * remove those, we need to wait until all transactions that know that
21036 * this is a partition are gone.
21037 */
21038
21039 /*
21040 * Remember relation OIDs to re-acquire them later; and relation names
21041 * too, for error messages if something is dropped in between.
21042 */
21043 partrelid = RelationGetRelid(partRel);
21044 parentrelid = RelationGetRelid(rel);
21045 parentrelname = MemoryContextStrdup(PortalContext,
21047 partrelname = MemoryContextStrdup(PortalContext,
21048 RelationGetRelationName(partRel));
21049
21050 /* Invalidate relcache entries for the parent -- must be before close */
21052
21053 table_close(partRel, NoLock);
21054 table_close(rel, NoLock);
21055 tab->rel = NULL;
21056
21057 /* Make updated catalog entry visible */
21060
21062
21063 /*
21064 * Now wait. This ensures that all queries that were planned
21065 * including the partition are finished before we remove the rest of
21066 * catalog entries. We don't need or indeed want to acquire this
21067 * lock, though -- that would block later queries.
21068 *
21069 * We don't need to concern ourselves with waiting for a lock on the
21070 * partition itself, since we will acquire AccessExclusiveLock below.
21071 */
21072 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
21074
21075 /*
21076 * Now acquire locks in both relations again. Note they may have been
21077 * removed in the meantime, so care is required.
21078 */
21079 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
21080 partRel = try_relation_open(partrelid, AccessExclusiveLock);
21081
21082 /* If the relations aren't there, something bad happened; bail out */
21083 if (rel == NULL)
21084 {
21085 if (partRel != NULL) /* shouldn't happen */
21086 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21087 partrelname);
21088 ereport(ERROR,
21089 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21090 errmsg("partitioned table \"%s\" was removed concurrently",
21091 parentrelname)));
21092 }
21093 if (partRel == NULL)
21094 ereport(ERROR,
21095 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21096 errmsg("partition \"%s\" was removed concurrently", partrelname)));
21097
21098 tab->rel = rel;
21099 }
21100
21101 /*
21102 * Detaching the partition might involve TOAST table access, so ensure we
21103 * have a valid snapshot.
21104 */
21106
21107 /* Do the final part of detaching */
21108 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21109
21111
21112 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21113
21114 /* keep our lock until commit */
21115 table_close(partRel, NoLock);
21116
21117 return address;
21118}
Oid MyDatabaseId
Definition: globals.c:94
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1631
void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
Definition: lmgr.c:911
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition: lock.h:183
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:1746
MemoryContext PortalContext
Definition: mcxt.c:175
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:272
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:682
void PopActiveSnapshot(void)
Definition: snapmgr.c:775
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:88
Definition: lock.h:167
static void MarkInheritDetached(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17893
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:17976
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:21127
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21985
void StartTransactionCommand(void)
Definition: xact.c:3077
void CommitTransactionCommand(void)
Definition: xact.c:3175

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

Referenced by ATExecCmd().

◆ ATExecDetachPartitionFinalize()

static ObjectAddress ATExecDetachPartitionFinalize ( Relation  rel,
RangeVar name 
)
static

Definition at line 21461 of file tablecmds.c.

21462{
21463 Relation partRel;
21464 ObjectAddress address;
21465 Snapshot snap = GetActiveSnapshot();
21466
21468
21469 /*
21470 * Wait until existing snapshots are gone. This is important if the
21471 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21472 * user could immediately run DETACH FINALIZE without actually waiting for
21473 * existing transactions. We must not complete the detach action until
21474 * all such queries are complete (otherwise we would present them with an
21475 * inconsistent view of catalogs).
21476 */
21477 WaitForOlderSnapshots(snap->xmin, false);
21478
21479 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21480
21481 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21482
21483 table_close(partRel, NoLock);
21484
21485 return address;
21486}
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:434
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:800
TransactionId xmin
Definition: snapshot.h:153

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

Referenced by ATExecCmd().

◆ ATExecDropCluster()

static void ATExecDropCluster ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 16507 of file tablecmds.c.

16508{
16509 mark_index_clustered(rel, InvalidOid, false);
16510}

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

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

14043{
14044 Relation conrel;
14045 SysScanDesc scan;
14046 ScanKeyData skey[3];
14047 HeapTuple tuple;
14048 bool found = false;
14049
14050 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14051
14052 /*
14053 * Find and drop the target constraint
14054 */
14055 ScanKeyInit(&skey[0],
14056 Anum_pg_constraint_conrelid,
14057 BTEqualStrategyNumber, F_OIDEQ,
14059 ScanKeyInit(&skey[1],
14060 Anum_pg_constraint_contypid,
14061 BTEqualStrategyNumber, F_OIDEQ,
14063 ScanKeyInit(&skey[2],
14064 Anum_pg_constraint_conname,
14065 BTEqualStrategyNumber, F_NAMEEQ,
14066 CStringGetDatum(constrName));
14067 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14068 true, NULL, 3, skey);
14069
14070 /* There can be at most one matching row */
14071 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14072 {
14073 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14074 missing_ok, lockmode);
14075 found = true;
14076 }
14077
14078 systable_endscan(scan);
14079
14080 if (!found)
14081 {
14082 if (!missing_ok)
14083 ereport(ERROR,
14084 errcode(ERRCODE_UNDEFINED_OBJECT),
14085 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14086 constrName, RelationGetRelationName(rel)));
14087 else
14089 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14090 constrName, RelationGetRelationName(rel)));
14091 }
14092
14094}
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14105

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

8831{
8832 HeapTuple tuple;
8833 Form_pg_attribute attTup;
8835 Relation attrelation;
8836 Oid attrdefoid;
8837 ObjectAddress address;
8838
8839 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8840 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8841 if (!HeapTupleIsValid(tuple))
8842 ereport(ERROR,
8843 (errcode(ERRCODE_UNDEFINED_COLUMN),
8844 errmsg("column \"%s\" of relation \"%s\" does not exist",
8845 colName, RelationGetRelationName(rel))));
8846
8847 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8848 attnum = attTup->attnum;
8849
8850 if (attnum <= 0)
8851 ereport(ERROR,
8852 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8853 errmsg("cannot alter system column \"%s\"",
8854 colName)));
8855
8856 /*
8857 * TODO: This could be done, but it would need a table rewrite to
8858 * materialize the generated values. Note that for the time being, we
8859 * still error with missing_ok, so that we don't silently leave the column
8860 * as generated.
8861 */
8862 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8863 ereport(ERROR,
8864 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8865 errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8866 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8867 colName, RelationGetRelationName(rel))));
8868
8869 if (!attTup->attgenerated)
8870 {
8871 if (!missing_ok)
8872 ereport(ERROR,
8873 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8874 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8875 colName, RelationGetRelationName(rel))));
8876 else
8877 {
8879 (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8880 colName, RelationGetRelationName(rel))));
8881 heap_freetuple(tuple);
8882 table_close(attrelation, RowExclusiveLock);
8883 return InvalidObjectAddress;
8884 }
8885 }
8886
8887 /*
8888 * Mark the column as no longer generated. (The atthasdef flag needs to
8889 * get cleared too, but RemoveAttrDefault will handle that.)
8890 */
8891 attTup->attgenerated = '\0';
8892 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8893
8894 InvokeObjectPostAlterHook(RelationRelationId,
8895 RelationGetRelid(rel),
8896 attnum);
8897 heap_freetuple(tuple);
8898
8899 table_close(attrelation, RowExclusiveLock);
8900
8901 /*
8902 * Drop the dependency records of the GENERATED expression, in particular
8903 * its INTERNAL dependency on the column, which would otherwise cause
8904 * dependency.c to refuse to perform the deletion.
8905 */
8906 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8907 if (!OidIsValid(attrdefoid))
8908 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8909 RelationGetRelid(rel), attnum);
8910 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8911
8912 /* Make above changes visible */
8914
8915 /*
8916 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8917 * safety, but at present we do not expect anything to depend on the
8918 * default.
8919 */
8921 false, false);
8922
8923 ObjectAddressSubSet(address, RelationRelationId,
8924 RelationGetRelid(rel), attnum);
8925 return address;
8926}

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

8518{
8519 HeapTuple tuple;
8520 Form_pg_attribute attTup;
8522 Relation attrelation;
8523 ObjectAddress address;
8524 Oid seqid;
8525 ObjectAddress seqaddress;
8526 bool ispartitioned;
8527
8528 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8529 if (ispartitioned && !recurse)
8530 ereport(ERROR,
8531 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8532 errmsg("cannot drop identity from a column of only the partitioned table"),
8533 errhint("Do not specify the ONLY keyword.")));
8534
8535 if (rel->rd_rel->relispartition && !recursing)
8536 ereport(ERROR,
8537 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8538 errmsg("cannot drop identity from a column of a partition"));
8539
8540 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8541 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8542 if (!HeapTupleIsValid(tuple))
8543 ereport(ERROR,
8544 (errcode(ERRCODE_UNDEFINED_COLUMN),
8545 errmsg("column \"%s\" of relation \"%s\" does not exist",
8546 colName, RelationGetRelationName(rel))));
8547
8548 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8549 attnum = attTup->attnum;
8550
8551 if (attnum <= 0)
8552 ereport(ERROR,
8553 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8554 errmsg("cannot alter system column \"%s\"",
8555 colName)));
8556
8557 if (!attTup->attidentity)
8558 {
8559 if (!missing_ok)
8560 ereport(ERROR,
8561 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8562 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8563 colName, RelationGetRelationName(rel))));
8564 else
8565 {
8567 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8568 colName, RelationGetRelationName(rel))));
8569 heap_freetuple(tuple);
8570 table_close(attrelation, RowExclusiveLock);
8571 return InvalidObjectAddress;
8572 }
8573 }
8574
8575 attTup->attidentity = '\0';
8576 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8577
8578 InvokeObjectPostAlterHook(RelationRelationId,
8579 RelationGetRelid(rel),
8580 attTup->attnum);
8581 ObjectAddressSubSet(address, RelationRelationId,
8582 RelationGetRelid(rel), attnum);
8583 heap_freetuple(tuple);
8584
8585 table_close(attrelation, RowExclusiveLock);
8586
8587 /*
8588 * Recurse to drop the identity from column in partitions. Identity is
8589 * not inherited in regular inheritance children so ignore them.
8590 */
8591 if (recurse && ispartitioned)
8592 {
8593 List *children;
8594 ListCell *lc;
8595
8596 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8597
8598 foreach(lc, children)
8599 {
8600 Relation childrel;
8601
8602 childrel = table_open(lfirst_oid(lc), NoLock);
8603 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8604 table_close(childrel, NoLock);
8605 }
8606 }
8607
8608 if (!recursing)
8609 {
8610 /* drop the internal sequence */
8611 seqid = getIdentitySequence(rel, attnum, false);
8612 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8613 RelationRelationId, DEPENDENCY_INTERNAL);
8615 seqaddress.classId = RelationRelationId;
8616 seqaddress.objectId = seqid;
8617 seqaddress.objectSubId = 0;
8619 }
8620
8621 return address;
8622}
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:274
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:92
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:351
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:945

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

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

◆ ATExecDropInherit()

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

Definition at line 17851 of file tablecmds.c.

17852{
17853 ObjectAddress address;
17854 Relation parent_rel;
17855
17856 if (rel->rd_rel->relispartition)
17857 ereport(ERROR,
17858 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17859 errmsg("cannot change inheritance of a partition")));
17860
17861 /*
17862 * AccessShareLock on the parent is probably enough, seeing that DROP
17863 * TABLE doesn't lock parent tables at all. We need some lock since we'll
17864 * be inspecting the parent's schema.
17865 */
17866 parent_rel = table_openrv(parent, AccessShareLock);
17867
17868 /*
17869 * We don't bother to check ownership of the parent table --- ownership of
17870 * the child is presumed enough rights.
17871 */
17872
17873 /* Off to RemoveInheritance() where most of the work happens */
17874 RemoveInheritance(rel, parent_rel, false);
17875
17876 ObjectAddressSet(address, RelationRelationId,
17877 RelationGetRelid(parent_rel));
17878
17879 /* keep our lock on the parent relation until commit */
17880 table_close(parent_rel, NoLock);
17881
17882 return address;
17883}

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

7772{
7773 HeapTuple tuple;
7774 HeapTuple conTup;
7775 Form_pg_attribute attTup;
7777 Relation attr_rel;
7778 ObjectAddress address;
7779
7780 /*
7781 * lookup the attribute
7782 */
7783 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7784
7785 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7786 if (!HeapTupleIsValid(tuple))
7787 ereport(ERROR,
7788 (errcode(ERRCODE_UNDEFINED_COLUMN),
7789 errmsg("column \"%s\" of relation \"%s\" does not exist",
7790 colName, RelationGetRelationName(rel))));
7791 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7792 attnum = attTup->attnum;
7793 ObjectAddressSubSet(address, RelationRelationId,
7794 RelationGetRelid(rel), attnum);
7795
7796 /* If the column is already nullable there's nothing to do. */
7797 if (!attTup->attnotnull)
7798 {
7799 table_close(attr_rel, RowExclusiveLock);
7800 return InvalidObjectAddress;
7801 }
7802
7803 /* Prevent them from altering a system attribute */
7804 if (attnum <= 0)
7805 ereport(ERROR,
7806 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7807 errmsg("cannot alter system column \"%s\"",
7808 colName)));
7809
7810 if (attTup->attidentity)
7811 ereport(ERROR,
7812 (errcode(ERRCODE_SYNTAX_ERROR),
7813 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7814 colName, RelationGetRelationName(rel))));
7815
7816 /*
7817 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7818 */
7819 if (rel->rd_rel->relispartition)
7820 {
7821 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7822 Relation parent = table_open(parentId, AccessShareLock);
7823 TupleDesc tupDesc = RelationGetDescr(parent);
7824 AttrNumber parent_attnum;
7825
7826 parent_attnum = get_attnum(parentId, colName);
7827 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7828 ereport(ERROR,
7829 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7830 errmsg("column \"%s\" is marked NOT NULL in parent table",
7831 colName)));
7833 }
7834
7835 /*
7836 * Find the constraint that makes this column NOT NULL, and drop it.
7837 * dropconstraint_internal() resets attnotnull.
7838 */
7840 if (conTup == NULL)
7841 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7842 colName, RelationGetRelationName(rel));
7843
7844 /* The normal case: we have a pg_constraint row, remove it */
7845 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7846 false, lockmode);
7847 heap_freetuple(conTup);
7848
7849 InvokeObjectPostAlterHook(RelationRelationId,
7850 RelationGetRelid(rel), attnum);
7851
7852 table_close(attr_rel, RowExclusiveLock);
7853
7854 return address;
7855}
bool attnotnull
Definition: pg_attribute.h:123

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

Referenced by ATExecCmd().

◆ ATExecDropOf()

static void ATExecDropOf ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 18384 of file tablecmds.c.

18385{
18386 Oid relid = RelationGetRelid(rel);
18387 Relation relationRelation;
18388 HeapTuple tuple;
18389
18390 if (!OidIsValid(rel->rd_rel->reloftype))
18391 ereport(ERROR,
18392 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18393 errmsg("\"%s\" is not a typed table",
18395
18396 /*
18397 * We don't bother to check ownership of the type --- ownership of the
18398 * table is presumed enough rights. No lock required on the type, either.
18399 */
18400
18401 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18403
18404 /* Clear pg_class.reloftype */
18405 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18406 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18407 if (!HeapTupleIsValid(tuple))
18408 elog(ERROR, "cache lookup failed for relation %u", relid);
18409 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18410 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18411
18412 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18413
18414 heap_freetuple(tuple);
18415 table_close(relationRelation, RowExclusiveLock);
18416}

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

17252{
17253 EnableDisableRule(rel, rulename, fires_when);
17254
17255 InvokeObjectPostAlterHook(RelationRelationId,
17256 RelationGetRelid(rel), 0);
17257}
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 17232 of file tablecmds.c.

17235{
17236 EnableDisableTrigger(rel, trigname, InvalidOid,
17237 fires_when, skip_system, recurse,
17238 lockmode);
17239
17240 InvokeObjectPostAlterHook(RelationRelationId,
17241 RelationGetRelid(rel), 0);
17242}
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 18660 of file tablecmds.c.

18661{
18662 Relation pg_class;
18663 Oid relid;
18664 HeapTuple tuple;
18665
18666 relid = RelationGetRelid(rel);
18667
18668 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18669
18670 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18671
18672 if (!HeapTupleIsValid(tuple))
18673 elog(ERROR, "cache lookup failed for relation %u", relid);
18674
18675 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18676 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18677
18678 InvokeObjectPostAlterHook(RelationRelationId,
18679 RelationGetRelid(rel), 0);
18680
18681 table_close(pg_class, RowExclusiveLock);
18682 heap_freetuple(tuple);
18683}

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

18690{
18691 Relation ftrel;
18692 ForeignServer *server;
18693 ForeignDataWrapper *fdw;
18694 HeapTuple tuple;
18695 bool isnull;
18696 Datum repl_val[Natts_pg_foreign_table];
18697 bool repl_null[Natts_pg_foreign_table];
18698 bool repl_repl[Natts_pg_foreign_table];
18699 Datum datum;
18700 Form_pg_foreign_table tableform;
18701
18702 if (options == NIL)
18703 return;
18704
18705 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18706
18707 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18708 ObjectIdGetDatum(rel->rd_id));
18709 if (!HeapTupleIsValid(tuple))
18710 ereport(ERROR,
18711 (errcode(ERRCODE_UNDEFINED_OBJECT),
18712 errmsg("foreign table \"%s\" does not exist",
18714 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18715 server = GetForeignServer(tableform->ftserver);
18716 fdw = GetForeignDataWrapper(server->fdwid);
18717
18718 memset(repl_val, 0, sizeof(repl_val));
18719 memset(repl_null, false, sizeof(repl_null));
18720 memset(repl_repl, false, sizeof(repl_repl));
18721
18722 /* Extract the current options */
18723 datum = SysCacheGetAttr(FOREIGNTABLEREL,
18724 tuple,
18725 Anum_pg_foreign_table_ftoptions,
18726 &isnull);
18727 if (isnull)
18728 datum = PointerGetDatum(NULL);
18729
18730 /* Transform the options */
18731 datum = transformGenericOptions(ForeignTableRelationId,
18732 datum,
18733 options,
18734 fdw->fdwvalidator);
18735
18736 if (DatumGetPointer(datum) != NULL)
18737 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18738 else
18739 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18740
18741 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18742
18743 /* Everything looks good - update the tuple */
18744
18745 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18746 repl_val, repl_null, repl_repl);
18747
18748 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18749
18750 /*
18751 * Invalidate relcache so that all sessions will refresh any cached plans
18752 * that might depend on the old options.
18753 */
18755
18756 InvokeObjectPostAlterHook(ForeignTableRelationId,
18757 RelationGetRelid(rel), 0);
18758
18760
18761 heap_freetuple(tuple);
18762}

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

◆ ATExecMergePartitions()

static void ATExecMergePartitions ( List **  wqueue,
AlteredTableInfo tab,
Relation  rel,
PartitionCmd cmd,
AlterTableUtilityContext context 
)
static

Definition at line 22743 of file tablecmds.c.

22745{
22746 Relation newPartRel;
22747 List *mergingPartitions = NIL;
22748 Oid defaultPartOid;
22749 Oid existingRelid;
22750 Oid ownerId = InvalidOid;
22751 Oid save_userid;
22752 int save_sec_context;
22753 int save_nestlevel;
22754
22755 /*
22756 * Check ownership of merged partitions - partitions with different owners
22757 * cannot be merged. Also, collect the OIDs of these partitions during the
22758 * check.
22759 */
22761 {
22762 Relation mergingPartition;
22763
22764 /*
22765 * We are going to detach and remove this partition. We already took
22766 * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
22767 * NoLock is fine.
22768 */
22769 mergingPartition = table_openrv_extended(name, NoLock, false);
22770 Assert(CheckRelationLockedByMe(mergingPartition, AccessExclusiveLock, false));
22771
22772 if (OidIsValid(ownerId))
22773 {
22774 /* Do the partitions being merged have different owners? */
22775 if (ownerId != mergingPartition->rd_rel->relowner)
22776 ereport(ERROR,
22777 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22778 errmsg("partitions being merged have different owners"));
22779 }
22780 else
22781 ownerId = mergingPartition->rd_rel->relowner;
22782
22783 /* Store the next merging partition into the list. */
22784 mergingPartitions = lappend_oid(mergingPartitions,
22785 RelationGetRelid(mergingPartition));
22786
22787 table_close(mergingPartition, NoLock);
22788 }
22789
22790 /* Look up the existing relation by the new partition name. */
22791 RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, &existingRelid);
22792
22793 /*
22794 * Check if this name is already taken. This helps us to detect the
22795 * situation when one of the merging partitions has the same name as the
22796 * new partition. Otherwise, this would fail later on anyway, but
22797 * catching this here allows us to emit a nicer error message.
22798 */
22799 if (OidIsValid(existingRelid))
22800 {
22801 if (list_member_oid(mergingPartitions, existingRelid))
22802 {
22803 /*
22804 * The new partition has the same name as one of the merging
22805 * partitions.
22806 */
22807 char tmpRelName[NAMEDATALEN];
22808
22809 /* Generate a temporary name. */
22810 sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
22811
22812 /*
22813 * Rename the existing partition with a temporary name, leaving it
22814 * free for the new partition. We don't need to care about this
22815 * in the future because we're going to eventually drop the
22816 * existing partition anyway.
22817 */
22818 RenameRelationInternal(existingRelid, tmpRelName, true, false);
22819
22820 /*
22821 * We must bump the command counter to make the new partition
22822 * tuple visible for rename.
22823 */
22825 }
22826 else
22827 {
22828 ereport(ERROR,
22829 errcode(ERRCODE_DUPLICATE_TABLE),
22830 errmsg("relation \"%s\" already exists", cmd->name->relname));
22831 }
22832 }
22833
22834 defaultPartOid =
22836
22837 /* Detach all merging partitions. */
22838 foreach_oid(mergingPartitionOid, mergingPartitions)
22839 {
22840 Relation child_rel;
22841
22842 child_rel = table_open(mergingPartitionOid, NoLock);
22843
22844 detachPartitionTable(rel, child_rel, defaultPartOid);
22845
22846 table_close(child_rel, NoLock);
22847 }
22848
22849 /*
22850 * Perform a preliminary check to determine whether it's safe to drop all
22851 * merging partitions before we actually do so later. After merging rows
22852 * into the new partitions via MergePartitionsMoveRows, all old partitions
22853 * need to be dropped. However, since the drop behavior is DROP_RESTRICT
22854 * and the merge process (MergePartitionsMoveRows) can be time-consuming,
22855 * performing an early check on the drop eligibility of old partitions is
22856 * preferable.
22857 */
22858 foreach_oid(mergingPartitionOid, mergingPartitions)
22859 {
22860 ObjectAddress object;
22861
22862 /* Get oid of the later to be dropped relation. */
22863 object.objectId = mergingPartitionOid;
22864 object.classId = RelationRelationId;
22865 object.objectSubId = 0;
22866
22868 }
22869
22870 /*
22871 * Create a table for the new partition, using the partitioned table as a
22872 * model.
22873 */
22874 Assert(OidIsValid(ownerId));
22875 newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
22876
22877 /*
22878 * Switch to the table owner's userid, so that any index functions are run
22879 * as that user. Also, lockdown security-restricted operations and
22880 * arrange to make GUC variable changes local to this command.
22881 *
22882 * Need to do it after determining the namespace in the
22883 * createPartitionTable() call.
22884 */
22885 GetUserIdAndSecContext(&save_userid, &save_sec_context);
22886 SetUserIdAndSecContext(ownerId,
22887 save_sec_context | SECURITY_RESTRICTED_OPERATION);
22888 save_nestlevel = NewGUCNestLevel();
22890
22891 /* Copy data from merged partitions to the new partition. */
22892 MergePartitionsMoveRows(wqueue, mergingPartitions, newPartRel);
22893
22894 /* Drop the current partitions before attaching the new one. */
22895 foreach_oid(mergingPartitionOid, mergingPartitions)
22896 {
22897 ObjectAddress object;
22898
22899 object.objectId = mergingPartitionOid;
22900 object.classId = RelationRelationId;
22901 object.objectSubId = 0;
22902
22903 performDeletion(&object, DROP_RESTRICT, 0);
22904 }
22905
22906 list_free(mergingPartitions);
22907
22908 /*
22909 * Attach a new partition to the partitioned table. wqueue = NULL:
22910 * verification for each cloned constraint is not needed.
22911 */
22912 attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
22913
22914 /* Keep the lock until commit. */
22915 table_close(newPartRel, NoLock);
22916
22917 /* Roll back any GUC changes executed by index functions. */
22918 AtEOXact_GUC(false, save_nestlevel);
22919
22920 /* Restore the userid and security context. */
22921 SetUserIdAndSecContext(save_userid, save_sec_context);
22922}
void performDeletionCheck(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:333
int MyProcPid
Definition: globals.c:47
int NewGUCNestLevel(void)
Definition: guc.c:2110
void RestrictSearchPath(void)
Definition: guc.c:2121
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition: guc.c:2137
#define SECURITY_RESTRICTED_OPERATION
Definition: miscadmin.h:319
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition: miscinit.c:612
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition: miscinit.c:619
#define sprintf
Definition: port.h:262
List * partlist
Definition: parsenodes.h:1000
Relation table_openrv_extended(const RangeVar *relation, LOCKMODE lockmode, bool missing_ok)
Definition: table.c:103
static void MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
Definition: tablecmds.c:22568
static Relation createPartitionTable(List **wqueue, RangeVar *newPartName, Relation parent_rel, Oid ownerId)
Definition: tablecmds.c:22455
static void detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
Definition: tablecmds.c:22722

References AccessExclusiveLock, Assert(), AtEOXact_GUC(), attachPartitionTable(), PartitionCmd::bound, CheckRelationLockedByMe(), CommandCounterIncrement(), createPartitionTable(), detachPartitionTable(), DROP_RESTRICT, ereport, errcode(), errmsg(), ERROR, foreach_node, foreach_oid, get_default_oid_from_partdesc(), GetUserIdAndSecContext(), InvalidOid, lappend_oid(), list_free(), list_member_oid(), MergePartitionsMoveRows(), MyProcPid, name, PartitionCmd::name, NAMEDATALEN, NewGUCNestLevel(), NIL, NoLock, ObjectAddress::objectId, OidIsValid, PartitionCmd::partlist, PERFORM_DELETION_INTERNAL, performDeletion(), performDeletionCheck(), RangeVarGetAndCheckCreationNamespace(), RelationData::rd_rel, RelationGetPartitionDesc(), RelationGetRelid, RangeVar::relname, RenameRelationInternal(), RestrictSearchPath(), SECURITY_RESTRICTED_OPERATION, SetUserIdAndSecContext(), sprintf, table_close(), table_open(), and table_openrv_extended().

Referenced by ATExecCmd().

◆ ATExecReplicaIdentity()

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

Definition at line 18516 of file tablecmds.c.

18517{
18518 Oid indexOid;
18519 Relation indexRel;
18520 int key;
18521
18522 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18523 {
18524 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18525 return;
18526 }
18527 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18528 {
18529 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18530 return;
18531 }
18532 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18533 {
18534 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18535 return;
18536 }
18537 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18538 {
18539 /* fallthrough */ ;
18540 }
18541 else
18542 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18543
18544 /* Check that the index exists */
18545 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18546 if (!OidIsValid(indexOid))
18547 ereport(ERROR,
18548 (errcode(ERRCODE_UNDEFINED_OBJECT),
18549 errmsg("index \"%s\" for table \"%s\" does not exist",
18550 stmt->name, RelationGetRelationName(rel))));
18551
18552 indexRel = index_open(indexOid, ShareLock);
18553
18554 /* Check that the index is on the relation we're altering. */
18555 if (indexRel->rd_index == NULL ||
18556 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18557 ereport(ERROR,
18558 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18559 errmsg("\"%s\" is not an index for table \"%s\"",
18560 RelationGetRelationName(indexRel),
18562
18563 /*
18564 * The AM must support uniqueness, and the index must in fact be unique.
18565 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18566 * exclusion), we can use that too.
18567 */
18568 if ((!indexRel->rd_indam->amcanunique ||
18569 !indexRel->rd_index->indisunique) &&
18570 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18571 ereport(ERROR,
18572 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18573 errmsg("cannot use non-unique index \"%s\" as replica identity",
18574 RelationGetRelationName(indexRel))));
18575 /* Deferred indexes are not guaranteed to be always unique. */
18576 if (!indexRel->rd_index->indimmediate)
18577 ereport(ERROR,
18578 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18579 errmsg("cannot use non-immediate index \"%s\" as replica identity",
18580 RelationGetRelationName(indexRel))));
18581 /* Expression indexes aren't supported. */
18582 if (RelationGetIndexExpressions(indexRel) != NIL)
18583 ereport(ERROR,
18584 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18585 errmsg("cannot use expression index \"%s\" as replica identity",
18586 RelationGetRelationName(indexRel))));
18587 /* Predicate indexes aren't supported. */
18588 if (RelationGetIndexPredicate(indexRel) != NIL)
18589 ereport(ERROR,
18590 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18591 errmsg("cannot use partial index \"%s\" as replica identity",
18592 RelationGetRelationName(indexRel))));
18593
18594 /* Check index for nullable columns. */
18595 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18596 {
18597 int16 attno = indexRel->rd_index->indkey.values[key];
18598 Form_pg_attribute attr;
18599
18600 /*
18601 * Reject any other system columns. (Going forward, we'll disallow
18602 * indexes containing such columns in the first place, but they might
18603 * exist in older branches.)
18604 */
18605 if (attno <= 0)
18606 ereport(ERROR,
18607 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18608 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18609 RelationGetRelationName(indexRel), attno)));
18610
18611 attr = TupleDescAttr(rel->rd_att, attno - 1);
18612 if (!attr->attnotnull)
18613 ereport(ERROR,
18614 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18615 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18616 RelationGetRelationName(indexRel),
18617 NameStr(attr->attname))));
18618 }
18619
18620 /* This index is suitable for use as a replica identity. Mark it. */
18621 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18622
18623 index_close(indexRel, NoLock);
18624}
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:534
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5210
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5097
bool amcanunique
Definition: amapi.h:258
struct IndexAmRoutine * rd_indam
Definition: rel.h:206
TupleDesc rd_att
Definition: rel.h:112
static void relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, bool is_internal)
Definition: tablecmds.c:18428

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

16554{
16555 Relation pg_class;
16556 Oid oldAccessMethodId;
16557 HeapTuple tuple;
16558 Form_pg_class rd_rel;
16559 Oid reloid = RelationGetRelid(rel);
16560
16561 /*
16562 * Shouldn't be called on relations having storage; these are processed in
16563 * phase 3.
16564 */
16565 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16566
16567 /* Get a modifiable copy of the relation's pg_class row. */
16568 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16569
16570 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16571 if (!HeapTupleIsValid(tuple))
16572 elog(ERROR, "cache lookup failed for relation %u", reloid);
16573 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16574
16575 /* Update the pg_class row. */
16576 oldAccessMethodId = rd_rel->relam;
16577 rd_rel->relam = newAccessMethodId;
16578
16579 /* Leave if no update required */
16580 if (rd_rel->relam == oldAccessMethodId)
16581 {
16582 heap_freetuple(tuple);
16583 table_close(pg_class, RowExclusiveLock);
16584 return;
16585 }
16586
16587 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16588
16589 /*
16590 * Update the dependency on the new access method. No dependency is added
16591 * if the new access method is InvalidOid (default case). Be very careful
16592 * that this has to compare the previous value stored in pg_class with the
16593 * new one.
16594 */
16595 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16596 {
16597 ObjectAddress relobj,
16598 referenced;
16599
16600 /*
16601 * New access method is defined and there was no dependency
16602 * previously, so record a new one.
16603 */
16604 ObjectAddressSet(relobj, RelationRelationId, reloid);
16605 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16606 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16607 }
16608 else if (OidIsValid(oldAccessMethodId) &&
16609 !OidIsValid(rd_rel->relam))
16610 {
16611 /*
16612 * There was an access method defined, and no new one, so just remove
16613 * the existing dependency.
16614 */
16615 deleteDependencyRecordsForClass(RelationRelationId, reloid,
16616 AccessMethodRelationId,
16618 }
16619 else
16620 {
16621 Assert(OidIsValid(oldAccessMethodId) &&
16622 OidIsValid(rd_rel->relam));
16623
16624 /* Both are valid, so update the dependency */
16625 changeDependencyFor(RelationRelationId, reloid,
16626 AccessMethodRelationId,
16627 oldAccessMethodId, rd_rel->relam);
16628 }
16629
16630 /* make the relam and dependency changes visible */
16632
16633 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16634
16635 heap_freetuple(tuple);
16636 table_close(pg_class, RowExclusiveLock);
16637}

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

18774{
18775 Relation attrel;
18776 HeapTuple tuple;
18777 Form_pg_attribute atttableform;
18779 char *compression;
18780 char cmethod;
18781 ObjectAddress address;
18782
18783 compression = strVal(newValue);
18784
18785 attrel = table_open(AttributeRelationId, RowExclusiveLock);
18786
18787 /* copy the cache entry so we can scribble on it below */
18788 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18789 if (!HeapTupleIsValid(tuple))
18790 ereport(ERROR,
18791 (errcode(ERRCODE_UNDEFINED_COLUMN),
18792 errmsg("column \"%s\" of relation \"%s\" does not exist",
18793 column, RelationGetRelationName(rel))));
18794
18795 /* prevent them from altering a system attribute */
18796 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18797 attnum = atttableform->attnum;
18798 if (attnum <= 0)
18799 ereport(ERROR,
18800 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18801 errmsg("cannot alter system column \"%s\"", column)));
18802
18803 /*
18804 * Check that column type is compressible, then get the attribute
18805 * compression method code
18806 */
18807 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18808
18809 /* update pg_attribute entry */
18810 atttableform->attcompression = cmethod;
18811 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18812
18813 InvokeObjectPostAlterHook(RelationRelationId,
18814 RelationGetRelid(rel),
18815 attnum);
18816
18817 /*
18818 * Apply the change to indexes as well (only for simple index columns,
18819 * matching behavior of index.c ConstructTupleDescriptor()).
18820 */
18821 SetIndexStorageProperties(rel, attrel, attnum,
18822 false, 0,
18823 true, cmethod,
18824 lockmode);
18825
18826 heap_freetuple(tuple);
18827
18829
18830 /* make changes visible */
18832
18833 ObjectAddressSubSet(address, RelationRelationId,
18834 RelationGetRelid(rel), attnum);
18835 return address;
18836}
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:22034
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:9158

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

8632{
8633 HeapTuple tuple;
8634 Form_pg_attribute attTup;
8636 char attgenerated;
8637 bool rewrite;
8638 Oid attrdefoid;
8639 ObjectAddress address;
8640 Expr *defval;
8642 RawColumnDefault *rawEnt;
8643
8644 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8645 if (!HeapTupleIsValid(tuple))
8646 ereport(ERROR,
8647 (errcode(ERRCODE_UNDEFINED_COLUMN),
8648 errmsg("column \"%s\" of relation \"%s\" does not exist",
8649 colName, RelationGetRelationName(rel))));
8650
8651 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8652
8653 attnum = attTup->attnum;
8654 if (attnum <= 0)
8655 ereport(ERROR,
8656 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8657 errmsg("cannot alter system column \"%s\"",
8658 colName)));
8659
8660 attgenerated = attTup->attgenerated;
8661 if (!attgenerated)
8662 ereport(ERROR,
8663 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8664 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8665 colName, RelationGetRelationName(rel))));
8666
8667 /*
8668 * TODO: This could be done, just need to recheck any constraints
8669 * afterwards.
8670 */
8671 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8672 rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8673 ereport(ERROR,
8674 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8675 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables with check constraints"),
8676 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8677 colName, RelationGetRelationName(rel))));
8678
8679 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8680 tab->verify_new_notnull = true;
8681
8682 /*
8683 * We need to prevent this because a change of expression could affect a
8684 * row filter and inject expressions that are not permitted in a row
8685 * filter. XXX We could try to have a more precise check to catch only
8686 * publications with row filters, or even re-verify the row filter
8687 * expressions.
8688 */
8689 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8691 ereport(ERROR,
8692 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8693 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8694 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8695 colName, RelationGetRelationName(rel))));
8696
8697 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8698
8699 ReleaseSysCache(tuple);
8700
8701 if (rewrite)
8702 {
8703 /*
8704 * Clear all the missing values if we're rewriting the table, since
8705 * this renders them pointless.
8706 */
8708
8709 /* make sure we don't conflict with later attribute modifications */
8711
8712 /*
8713 * Find everything that depends on the column (constraints, indexes,
8714 * etc), and record enough information to let us recreate the objects
8715 * after rewrite.
8716 */
8718 }
8719
8720 /*
8721 * Drop the dependency records of the GENERATED expression, in particular
8722 * its INTERNAL dependency on the column, which would otherwise cause
8723 * dependency.c to refuse to perform the deletion.
8724 */
8725 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8726 if (!OidIsValid(attrdefoid))
8727 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8728 RelationGetRelid(rel), attnum);
8729 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8730
8731 /* Make above changes visible */
8733
8734 /*
8735 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8736 * safety, but at present we do not expect anything to depend on the
8737 * expression.
8738 */
8740 false, false);
8741
8742 /* Prepare to store the new expression, in the catalogs */
8744 rawEnt->attnum = attnum;
8745 rawEnt->raw_default = newExpr;
8746 rawEnt->generated = attgenerated;
8747
8748 /* Store the generated expression */
8750 false, true, false, NULL);
8751
8752 /* Make above new expression visible */
8754
8755 if (rewrite)
8756 {
8757 /* Prepare for table rewrite */
8758 defval = (Expr *) build_column_default(rel, attnum);
8759
8761 newval->attnum = attnum;
8762 newval->expr = expression_planner(defval);
8763 newval->is_generated = true;
8764
8765 tab->newvals = lappend(tab->newvals, newval);
8767 }
8768
8769 /* Drop any pg_statistic entry for the column */
8771
8772 InvokeObjectPostAlterHook(RelationRelationId,
8773 RelationGetRelid(rel), attnum);
8774
8775 ObjectAddressSubSet(address, RelationRelationId,
8776 RelationGetRelid(rel), attnum);
8777 return address;
8778}
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, palloc0_object, palloc_object, 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 8399 of file tablecmds.c.

8401{
8403 DefElem *generatedEl = NULL;
8404 HeapTuple tuple;
8405 Form_pg_attribute attTup;
8407 Relation attrelation;
8408 ObjectAddress address;
8409 bool ispartitioned;
8410
8411 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8412 if (ispartitioned && !recurse)
8413 ereport(ERROR,
8414 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8415 errmsg("cannot change identity column of only the partitioned table"),
8416 errhint("Do not specify the ONLY keyword.")));
8417
8418 if (rel->rd_rel->relispartition && !recursing)
8419 ereport(ERROR,
8420 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8421 errmsg("cannot change identity column of a partition"));
8422
8423 foreach(option, castNode(List, def))
8424 {
8425 DefElem *defel = lfirst_node(DefElem, option);
8426
8427 if (strcmp(defel->defname, "generated") == 0)
8428 {
8429 if (generatedEl)
8430 ereport(ERROR,
8431 (errcode(ERRCODE_SYNTAX_ERROR),
8432 errmsg("conflicting or redundant options")));
8433 generatedEl = defel;
8434 }
8435 else
8436 elog(ERROR, "option \"%s\" not recognized",
8437 defel->defname);
8438 }
8439
8440 /*
8441 * Even if there is nothing to change here, we run all the checks. There
8442 * will be a subsequent ALTER SEQUENCE that relies on everything being
8443 * there.
8444 */
8445
8446 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8447 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8448 if (!HeapTupleIsValid(tuple))
8449 ereport(ERROR,
8450 (errcode(ERRCODE_UNDEFINED_COLUMN),
8451 errmsg("column \"%s\" of relation \"%s\" does not exist",
8452 colName, RelationGetRelationName(rel))));
8453
8454 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8455 attnum = attTup->attnum;
8456
8457 if (attnum <= 0)
8458 ereport(ERROR,
8459 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8460 errmsg("cannot alter system column \"%s\"",
8461 colName)));
8462
8463 if (!attTup->attidentity)
8464 ereport(ERROR,
8465 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8466 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8467 colName, RelationGetRelationName(rel))));
8468
8469 if (generatedEl)
8470 {
8471 attTup->attidentity = defGetInt32(generatedEl);
8472 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8473
8474 InvokeObjectPostAlterHook(RelationRelationId,
8475 RelationGetRelid(rel),
8476 attTup->attnum);
8477 ObjectAddressSubSet(address, RelationRelationId,
8478 RelationGetRelid(rel), attnum);
8479 }
8480 else
8481 address = InvalidObjectAddress;
8482
8483 heap_freetuple(tuple);
8484 table_close(attrelation, RowExclusiveLock);
8485
8486 /*
8487 * Recurse to propagate the identity change to partitions. Identity is not
8488 * inherited in regular inheritance children.
8489 */
8490 if (generatedEl && recurse && ispartitioned)
8491 {
8492 List *children;
8493 ListCell *lc;
8494
8495 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8496
8497 foreach(lc, children)
8498 {
8499 Relation childrel;
8500
8501 childrel = table_open(lfirst_oid(lc), NoLock);
8502 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8503 table_close(childrel, NoLock);
8504 }
8505 }
8506
8507 return address;
8508}
int32 defGetInt32(DefElem *def)
Definition: define.c:149
#define lfirst_node(type, lc)
Definition: pg_list.h:176
char * defname
Definition: parsenodes.h:843

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

Referenced by ATExecCmd(), and ATExecSetIdentity().

◆ ATExecSetNotNull()

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

Definition at line 7941 of file tablecmds.c.

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

9081{
9082 Relation attrelation;
9083 HeapTuple tuple,
9084 newtuple;
9085 Form_pg_attribute attrtuple;
9087 Datum datum,
9088 newOptions;
9089 bool isnull;
9090 ObjectAddress address;
9091 Datum repl_val[Natts_pg_attribute];
9092 bool repl_null[Natts_pg_attribute];
9093 bool repl_repl[Natts_pg_attribute];
9094
9095 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9096
9097 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9098
9099 if (!HeapTupleIsValid(tuple))
9100 ereport(ERROR,
9101 (errcode(ERRCODE_UNDEFINED_COLUMN),
9102 errmsg("column \"%s\" of relation \"%s\" does not exist",
9103 colName, RelationGetRelationName(rel))));
9104 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9105
9106 attnum = attrtuple->attnum;
9107 if (attnum <= 0)
9108 ereport(ERROR,
9109 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9110 errmsg("cannot alter system column \"%s\"",
9111 colName)));
9112
9113 /* Generate new proposed attoptions (text array) */
9114 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9115 &isnull);
9116 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9117 castNode(List, options), NULL, NULL,
9118 false, isReset);
9119 /* Validate new options */
9120 (void) attribute_reloptions(newOptions, true);
9121
9122 /* Build new tuple. */
9123 memset(repl_null, false, sizeof(repl_null));
9124 memset(repl_repl, false, sizeof(repl_repl));
9125 if (newOptions != (Datum) 0)
9126 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9127 else
9128 repl_null[Anum_pg_attribute_attoptions - 1] = true;
9129 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9130 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9131 repl_val, repl_null, repl_repl);
9132
9133 /* Update system catalog. */
9134 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9135
9136 InvokeObjectPostAlterHook(RelationRelationId,
9137 RelationGetRelid(rel),
9138 attrtuple->attnum);
9139 ObjectAddressSubSet(address, RelationRelationId,
9140 RelationGetRelid(rel), attnum);
9141
9142 heap_freetuple(newtuple);
9143
9144 ReleaseSysCache(tuple);
9145
9146 table_close(attrelation, RowExclusiveLock);
9147
9148 return address;
9149}
Datum transformRelOptions(Datum oldOptions, List *defList, const char *nameSpace, const char *const validnsps[], bool acceptOidsOff, bool isReset)
Definition: reloptions.c:1176
bytea * attribute_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2116

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

Referenced by ATExecCmd().

◆ ATExecSetRelOptions()

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

Definition at line 16673 of file tablecmds.c.

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

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

Referenced by ATExecCmd().

◆ ATExecSetRowSecurity()

static void ATExecSetRowSecurity ( Relation  rel,
bool  rls 
)
static

Definition at line 18630 of file tablecmds.c.

18631{
18632 Relation pg_class;
18633 Oid relid;
18634 HeapTuple tuple;
18635
18636 relid = RelationGetRelid(rel);
18637
18638 /* Pull the record for this relation and update it */
18639 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18640
18641 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18642
18643 if (!HeapTupleIsValid(tuple))
18644 elog(ERROR, "cache lookup failed for relation %u", relid);
18645
18646 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18647 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18648
18649 InvokeObjectPostAlterHook(RelationRelationId,
18650 RelationGetRelid(rel), 0);
18651
18652 table_close(pg_class, RowExclusiveLock);
18653 heap_freetuple(tuple);
18654}

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

8935{
8936 int newtarget = 0;
8937 bool newtarget_default;
8938 Relation attrelation;
8939 HeapTuple tuple,
8940 newtuple;
8941 Form_pg_attribute attrtuple;
8943 ObjectAddress address;
8944 Datum repl_val[Natts_pg_attribute];
8945 bool repl_null[Natts_pg_attribute];
8946 bool repl_repl[Natts_pg_attribute];
8947
8948 /*
8949 * We allow referencing columns by numbers only for indexes, since table
8950 * column numbers could contain gaps if columns are later dropped.
8951 */
8952 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8953 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8954 !colName)
8955 ereport(ERROR,
8956 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8957 errmsg("cannot refer to non-index column by number")));
8958
8959 /* -1 was used in previous versions for the default setting */
8960 if (newValue && intVal(newValue) != -1)
8961 {
8962 newtarget = intVal(newValue);
8963 newtarget_default = false;
8964 }
8965 else
8966 newtarget_default = true;
8967
8968 if (!newtarget_default)
8969 {
8970 /*
8971 * Limit target to a sane range
8972 */
8973 if (newtarget < 0)
8974 {
8975 ereport(ERROR,
8976 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8977 errmsg("statistics target %d is too low",
8978 newtarget)));
8979 }
8980 else if (newtarget > MAX_STATISTICS_TARGET)
8981 {
8982 newtarget = MAX_STATISTICS_TARGET;
8984 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8985 errmsg("lowering statistics target to %d",
8986 newtarget)));
8987 }
8988 }
8989
8990 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8991
8992 if (colName)
8993 {
8994 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8995
8996 if (!HeapTupleIsValid(tuple))
8997 ereport(ERROR,
8998 (errcode(ERRCODE_UNDEFINED_COLUMN),
8999 errmsg("column \"%s\" of relation \"%s\" does not exist",
9000 colName, RelationGetRelationName(rel))));
9001 }
9002 else
9003 {
9004 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
9005
9006 if (!HeapTupleIsValid(tuple))
9007 ereport(ERROR,
9008 (errcode(ERRCODE_UNDEFINED_COLUMN),
9009 errmsg("column number %d of relation \"%s\" does not exist",
9010 colNum, RelationGetRelationName(rel))));
9011 }
9012
9013 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9014
9015 attnum = attrtuple->attnum;
9016 if (attnum <= 0)
9017 ereport(ERROR,
9018 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9019 errmsg("cannot alter system column \"%s\"",
9020 colName)));
9021
9022 /*
9023 * Prevent this as long as the ANALYZE code skips virtual generated
9024 * columns.
9025 */
9026 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
9027 ereport(ERROR,
9028 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9029 errmsg("cannot alter statistics on virtual generated column \"%s\"",
9030 colName)));
9031
9032 if (rel->rd_rel->relkind == RELKIND_INDEX ||
9033 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
9034 {
9035 if (attnum > rel->rd_index->indnkeyatts)
9036 ereport(ERROR,
9037 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9038 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9039 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9040 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9041 ereport(ERROR,
9042 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9043 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9044 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9045 errhint("Alter statistics on table column instead.")));
9046 }
9047
9048 /* Build new tuple. */
9049 memset(repl_null, false, sizeof(repl_null));
9050 memset(repl_repl, false, sizeof(repl_repl));
9051 if (!newtarget_default)
9052 repl_val[Anum_pg_attribute_attstattarget - 1] = Int16GetDatum(newtarget);
9053 else
9054 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
9055 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
9056 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9057 repl_val, repl_null, repl_repl);
9058 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9059
9060 InvokeObjectPostAlterHook(RelationRelationId,
9061 RelationGetRelid(rel),
9062 attrtuple->attnum);
9063 ObjectAddressSubSet(address, RelationRelationId,
9064 RelationGetRelid(rel), attnum);
9065
9066 heap_freetuple(newtuple);
9067
9068 ReleaseSysCache(tuple);
9069
9070 table_close(attrelation, RowExclusiveLock);
9071
9072 return address;
9073}
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:182
HeapTuple SearchSysCacheAttNum(Oid relid, int16 attnum)
Definition: syscache.c:538
#define MAX_STATISTICS_TARGET
Definition: vacuum.h:329
#define intVal(v)
Definition: value.h:79

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

Referenced by ATExecCmd().

◆ ATExecSetStorage()

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

Definition at line 9221 of file tablecmds.c.

9222{
9223 Relation attrelation;
9224 HeapTuple tuple;
9225 Form_pg_attribute attrtuple;
9227 ObjectAddress address;
9228
9229 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9230
9231 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9232
9233 if (!HeapTupleIsValid(tuple))
9234 ereport(ERROR,
9235 (errcode(ERRCODE_UNDEFINED_COLUMN),
9236 errmsg("column \"%s\" of relation \"%s\" does not exist",
9237 colName, RelationGetRelationName(rel))));
9238 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9239
9240 attnum = attrtuple->attnum;
9241 if (attnum <= 0)
9242 ereport(ERROR,
9243 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9244 errmsg("cannot alter system column \"%s\"",
9245 colName)));
9246
9247 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9248
9249 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9250
9251 InvokeObjectPostAlterHook(RelationRelationId,
9252 RelationGetRelid(rel),
9253 attrtuple->attnum);
9254
9255 /*
9256 * Apply the change to indexes as well (only for simple index columns,
9257 * matching behavior of index.c ConstructTupleDescriptor()).
9258 */
9259 SetIndexStorageProperties(rel, attrelation, attnum,
9260 true, attrtuple->attstorage,
9261 false, 0,
9262 lockmode);
9263
9264 heap_freetuple(tuple);
9265
9266 table_close(attrelation, RowExclusiveLock);
9267
9268 ObjectAddressSubSet(address, RelationRelationId,
9269 RelationGetRelid(rel), attnum);
9270 return address;
9271}
static char GetAttributeStorage(Oid atttypid, const char *storagemode)
Definition: tablecmds.c:22072

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

16882{
16883 Relation rel;
16884 Oid reltoastrelid;
16885 RelFileNumber newrelfilenumber;
16886 RelFileLocator newrlocator;
16887 List *reltoastidxids = NIL;
16888 ListCell *lc;
16889
16890 /*
16891 * Need lock here in case we are recursing to toast table or index
16892 */
16893 rel = relation_open(tableOid, lockmode);
16894
16895 /* Check first if relation can be moved to new tablespace */
16896 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16897 {
16898 InvokeObjectPostAlterHook(RelationRelationId,
16899 RelationGetRelid(rel), 0);
16900 relation_close(rel, NoLock);
16901 return;
16902 }
16903
16904 reltoastrelid = rel->rd_rel->reltoastrelid;
16905 /* Fetch the list of indexes on toast relation if necessary */
16906 if (OidIsValid(reltoastrelid))
16907 {
16908 Relation toastRel = relation_open(reltoastrelid, lockmode);
16909
16910 reltoastidxids = RelationGetIndexList(toastRel);
16911 relation_close(toastRel, lockmode);
16912 }
16913
16914 /*
16915 * Relfilenumbers are not unique in databases across tablespaces, so we
16916 * need to allocate a new one in the new tablespace.
16917 */
16918 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16919 rel->rd_rel->relpersistence);
16920
16921 /* Open old and new relation */
16922 newrlocator = rel->rd_locator;
16923 newrlocator.relNumber = newrelfilenumber;
16924 newrlocator.spcOid = newTableSpace;
16925
16926 /* hand off to AM to actually create new rel storage and copy the data */
16927 if (rel->rd_rel->relkind == RELKIND_INDEX)
16928 {
16929 index_copy_data(rel, newrlocator);
16930 }
16931 else
16932 {
16933 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16934 table_relation_copy_data(rel, &newrlocator);
16935 }
16936
16937 /*
16938 * Update the pg_class row.
16939 *
16940 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16941 * executed on pg_class or its indexes (the above copy wouldn't contain
16942 * the updated pg_class entry), but that's forbidden with
16943 * CheckRelationTableSpaceMove().
16944 */
16945 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16946
16947 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16948
16950
16951 relation_close(rel, NoLock);
16952
16953 /* Make sure the reltablespace change is visible */
16955
16956 /* Move associated toast relation and/or indexes, too */
16957 if (OidIsValid(reltoastrelid))
16958 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16959 foreach(lc, reltoastidxids)
16960 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16961
16962 /* Clean up */
16963 list_free(reltoastidxids);
16964}
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition: catalog.c:557
void RelationAssumeNewRelfilelocator(Relation relation)
Definition: relcache.c:3976
Oid RelFileNumber
Definition: relpath.h:25
RelFileNumber relNumber
static void table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
Definition: tableam.h:1630
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:16881
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3690
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3747
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:17175

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

16975{
16976 /*
16977 * Shouldn't be called on relations having storage; these are processed in
16978 * phase 3.
16979 */
16980 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16981
16982 /* check if relation can be moved to its new tablespace */
16983 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16984 {
16985 InvokeObjectPostAlterHook(RelationRelationId,
16986 RelationGetRelid(rel),
16987 0);
16988 return;
16989 }
16990
16991 /* Update can be done, so change reltablespace */
16992 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16993
16994 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16995
16996 /* Make sure the reltablespace change is visible */
16998}

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

Referenced by ATExecCmd().

◆ ATExecSplitPartition()

static void ATExecSplitPartition ( List **  wqueue,
AlteredTableInfo tab,
Relation  rel,
PartitionCmd cmd,
AlterTableUtilityContext context 
)
static

Definition at line 23197 of file tablecmds.c.

23199{
23200 Relation splitRel;
23201 Oid splitRelOid;
23202 ListCell *listptr,
23203 *listptr2;
23204 bool isSameName = false;
23205 char tmpRelName[NAMEDATALEN];
23206 List *newPartRels = NIL;
23207 ObjectAddress object;
23208 Oid defaultPartOid;
23209 Oid save_userid;
23210 int save_sec_context;
23211 int save_nestlevel;
23212
23213 defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
23214
23215 /*
23216 * Partition is already locked in the transformPartitionCmdForSplit
23217 * function.
23218 */
23219 splitRel = table_openrv(cmd->name, NoLock);
23220
23221 splitRelOid = RelationGetRelid(splitRel);
23222
23223 /* Check descriptions of new partitions. */
23225 {
23226 Oid existingRelid;
23227
23228 /* Look up the existing relation by the new partition name. */
23229 RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, &existingRelid);
23230
23231 /*
23232 * This would fail later on anyway if the relation already exists. But
23233 * by catching it here, we can emit a nicer error message.
23234 */
23235 if (existingRelid == splitRelOid && !isSameName)
23236 /* One new partition can have the same name as a split partition. */
23237 isSameName = true;
23238 else if (OidIsValid(existingRelid))
23239 ereport(ERROR,
23240 errcode(ERRCODE_DUPLICATE_TABLE),
23241 errmsg("relation \"%s\" already exists", sps->name->relname));
23242 }
23243
23244 /* Detach the split partition. */
23245 detachPartitionTable(rel, splitRel, defaultPartOid);
23246
23247 /*
23248 * Perform a preliminary check to determine whether it's safe to drop the
23249 * split partition before we actually do so later. After merging rows into
23250 * the new partitions via SplitPartitionMoveRows, all old partitions need
23251 * to be dropped. However, since the drop behavior is DROP_RESTRICT and
23252 * the merge process (SplitPartitionMoveRows) can be time-consuming,
23253 * performing an early check on the drop eligibility of old partitions is
23254 * preferable.
23255 */
23256 object.objectId = splitRelOid;
23257 object.classId = RelationRelationId;
23258 object.objectSubId = 0;
23260
23261 /*
23262 * If a new partition has the same name as the split partition, then we
23263 * should rename the split partition to reuse its name.
23264 */
23265 if (isSameName)
23266 {
23267 /*
23268 * We must bump the command counter to make the split partition tuple
23269 * visible for renaming.
23270 */
23272 /* Rename partition. */
23273 sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23274 RenameRelationInternal(splitRelOid, tmpRelName, true, false);
23275
23276 /*
23277 * We must bump the command counter to make the split partition tuple
23278 * visible after renaming.
23279 */
23281 }
23282
23283 /* Create new partitions (like a split partition), without indexes. */
23285 {
23286 Relation newPartRel;
23287
23288 newPartRel = createPartitionTable(wqueue, sps->name, rel,
23289 splitRel->rd_rel->relowner);
23290 newPartRels = lappend(newPartRels, newPartRel);
23291 }
23292
23293 /*
23294 * Switch to the table owner's userid, so that any index functions are run
23295 * as that user. Also, lockdown security-restricted operations and
23296 * arrange to make GUC variable changes local to this command.
23297 *
23298 * Need to do it after determining the namespace in the
23299 * createPartitionTable() call.
23300 */
23301 GetUserIdAndSecContext(&save_userid, &save_sec_context);
23302 SetUserIdAndSecContext(splitRel->rd_rel->relowner,
23303 save_sec_context | SECURITY_RESTRICTED_OPERATION);
23304 save_nestlevel = NewGUCNestLevel();
23306
23307 /* Copy data from the split partition to the new partitions. */
23308 SplitPartitionMoveRows(wqueue, rel, splitRel, cmd->partlist, newPartRels);
23309 /* Keep the lock until commit. */
23310 table_close(splitRel, NoLock);
23311
23312 /* Attach new partitions to the partitioned table. */
23313 forboth(listptr, cmd->partlist, listptr2, newPartRels)
23314 {
23316 Relation newPartRel = (Relation) lfirst(listptr2);
23317
23318 /*
23319 * wqueue = NULL: verification for each cloned constraint is not
23320 * needed.
23321 */
23322 attachPartitionTable(NULL, rel, newPartRel, sps->bound);
23323 /* Keep the lock until commit. */
23324 table_close(newPartRel, NoLock);
23325 }
23326
23327 /* Drop the split partition. */
23328 object.classId = RelationRelationId;
23329 object.objectId = splitRelOid;
23330 object.objectSubId = 0;
23331 /* Probably DROP_CASCADE is not needed. */
23332 performDeletion(&object, DROP_RESTRICT, 0);
23333
23334 /* Roll back any GUC changes executed by index functions. */
23335 AtEOXact_GUC(false, save_nestlevel);
23336
23337 /* Restore the userid and security context. */
23338 SetUserIdAndSecContext(save_userid, save_sec_context);
23339}
struct RelationData * Relation
Definition: genam.h:30
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
PartitionBoundSpec * bound
Definition: parsenodes.h:977
static void SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel, List *partlist, List *newPartRels)
Definition: tablecmds.c:23004

References AtEOXact_GUC(), attachPartitionTable(), SinglePartitionSpec::bound, CommandCounterIncrement(), createPartitionTable(), detachPartitionTable(), DROP_RESTRICT, ereport, errcode(), errmsg(), ERROR, forboth, foreach_node, get_default_oid_from_partdesc(), GetUserIdAndSecContext(), lappend(), lfirst, MyProcPid, PartitionCmd::name, NAMEDATALEN, NewGUCNestLevel(), NIL, NoLock, OidIsValid, PartitionCmd::partlist, PERFORM_DELETION_INTERNAL, performDeletion(), performDeletionCheck(), RangeVarGetAndCheckCreationNamespace(), RelationData::rd_rel, RelationGetPartitionDesc(), RelationGetRelid, RenameRelationInternal(), RestrictSearchPath(), SECURITY_RESTRICTED_OPERATION, SetUserIdAndSecContext(), SplitPartitionMoveRows(), sprintf, table_close(), and table_openrv().

Referenced by ATExecCmd().

◆ ATExecValidateConstraint()

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

Definition at line 12936 of file tablecmds.c.

12938{
12939 Relation conrel;
12940 SysScanDesc scan;
12941 ScanKeyData skey[3];
12942 HeapTuple tuple;
12944 ObjectAddress address;
12945
12946 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12947
12948 /*
12949 * Find and check the target constraint
12950 */
12951 ScanKeyInit(&skey[0],
12952 Anum_pg_constraint_conrelid,
12953 BTEqualStrategyNumber, F_OIDEQ,
12955 ScanKeyInit(&skey[1],
12956 Anum_pg_constraint_contypid,
12957 BTEqualStrategyNumber, F_OIDEQ,
12959 ScanKeyInit(&skey[2],
12960 Anum_pg_constraint_conname,
12961 BTEqualStrategyNumber, F_NAMEEQ,
12962 CStringGetDatum(constrName));
12963 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12964 true, NULL, 3, skey);
12965
12966 /* There can be at most one matching row */
12967 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12968 ereport(ERROR,
12969 (errcode(ERRCODE_UNDEFINED_OBJECT),
12970 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12971 constrName, RelationGetRelationName(rel))));
12972
12973 con = (Form_pg_constraint) GETSTRUCT(tuple);
12974 if (con->contype != CONSTRAINT_FOREIGN &&
12975 con->contype != CONSTRAINT_CHECK &&
12976 con->contype != CONSTRAINT_NOTNULL)
12977 ereport(ERROR,
12978 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12979 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
12980 constrName, RelationGetRelationName(rel)),
12981 errdetail("This operation is not supported for this type of constraint."));
12982
12983 if (!con->conenforced)
12984 ereport(ERROR,
12985 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12986 errmsg("cannot validate NOT ENFORCED constraint")));
12987
12988 if (!con->convalidated)
12989 {
12990 if (con->contype == CONSTRAINT_FOREIGN)
12991 {
12992 QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12993 tuple, lockmode);
12994 }
12995 else if (con->contype == CONSTRAINT_CHECK)
12996 {
12997 QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12998 tuple, recurse, recursing, lockmode);
12999 }
13000 else if (con->contype == CONSTRAINT_NOTNULL)
13001 {
13002 QueueNNConstraintValidation(wqueue, conrel, rel,
13003 tuple, recurse, recursing, lockmode);
13004 }
13005
13006 ObjectAddressSet(address, ConstraintRelationId, con->oid);
13007 }
13008 else
13009 address = InvalidObjectAddress; /* already validated */
13010
13011 systable_endscan(scan);
13012
13014
13015 return address;
13016}
static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:13026
static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13248
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13145

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

6587{
6588 Oid relid = RelationGetRelid(rel);
6589 AlteredTableInfo *tab;
6590 ListCell *ltab;
6591
6592 foreach(ltab, *wqueue)
6593 {
6594 tab = (AlteredTableInfo *) lfirst(ltab);
6595 if (tab->relid == relid)
6596 return tab;
6597 }
6598
6599 /*
6600 * Not there, so add it. Note that we make a copy of the relation's
6601 * existing descriptor before anything interesting can happen to it.
6602 */
6604 tab->relid = relid;
6605 tab->rel = NULL; /* set later */
6606 tab->relkind = rel->rd_rel->relkind;
6609 tab->chgAccessMethod = false;
6611 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6612 tab->chgPersistence = false;
6613
6614 *wqueue = lappend(*wqueue, tab);
6615
6616 return tab;
6617}
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_object, RelationData::rd_rel, AlteredTableInfo::rel, RelationGetDescr, RelationGetRelid, AlteredTableInfo::relid, and AlteredTableInfo::relkind.

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

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

5738{
5739 AlterTableCmd *newcmd = NULL;
5741 List *beforeStmts;
5742 List *afterStmts;
5743 ListCell *lc;
5744
5745 /* Gin up an AlterTableStmt with just this subcommand and this table */
5746 atstmt->relation =
5749 -1);
5750 atstmt->relation->inh = recurse;
5751 atstmt->cmds = list_make1(cmd);
5752 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5753 atstmt->missing_ok = false;
5754
5755 /* Transform the AlterTableStmt */
5757 atstmt,
5758 context->queryString,
5759 &beforeStmts,
5760 &afterStmts);
5761
5762 /* Execute any statements that should happen before these subcommand(s) */
5763 foreach(lc, beforeStmts)
5764 {
5765 Node *stmt = (Node *) lfirst(lc);
5766
5769 }
5770
5771 /* Examine the transformed subcommands and schedule them appropriately */
5772 foreach(lc, atstmt->cmds)
5773 {
5775 AlterTablePass pass;
5776
5777 /*
5778 * This switch need only cover the subcommand types that can be added
5779 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5780 * executing the subcommand immediately, as a substitute for the
5781 * original subcommand. (Note, however, that this does cause
5782 * AT_AddConstraint subcommands to be rescheduled into later passes,
5783 * which is important for index and foreign key constraints.)
5784 *
5785 * We assume we needn't do any phase-1 checks for added subcommands.
5786 */
5787 switch (cmd2->subtype)
5788 {
5789 case AT_AddIndex:
5790 pass = AT_PASS_ADD_INDEX;
5791 break;
5794 break;
5795 case AT_AddConstraint:
5796 /* Recursion occurs during execution phase */
5797 if (recurse)
5798 cmd2->recurse = true;
5799 switch (castNode(Constraint, cmd2->def)->contype)
5800 {
5801 case CONSTR_NOTNULL:
5802 pass = AT_PASS_COL_ATTRS;
5803 break;
5804 case CONSTR_PRIMARY:
5805 case CONSTR_UNIQUE:
5806 case CONSTR_EXCLUSION:
5808 break;
5809 default:
5811 break;
5812 }
5813 break;
5815 /* This command never recurses */
5816 /* No command-specific prep needed */
5817 pass = AT_PASS_MISC;
5818 break;
5819 default:
5820 pass = cur_pass;
5821 break;
5822 }
5823
5824 if (pass < cur_pass)
5825 {
5826 /* Cannot schedule into a pass we already finished */
5827 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5828 pass);
5829 }
5830 else if (pass > cur_pass)
5831 {
5832 /* OK, queue it up for later */
5833 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5834 }
5835 else
5836 {
5837 /*
5838 * We should see at most one subcommand for the current pass,
5839 * which is the transformed version of the original subcommand.
5840 */
5841 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5842 {
5843 /* Found the transformed version of our subcommand */
5844 newcmd = cmd2;
5845 }
5846 else
5847 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5848 pass);
5849 }
5850 }
5851
5852 /* Queue up any after-statements to happen at the end */
5853 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5854
5855 return newcmd;
5856}
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:2435
ObjectType objtype
Definition: parsenodes.h:2437
List * afterStmts
Definition: tablecmds.c:190
List * subcmds[AT_NUM_PASSES]
Definition: tablecmds.c:186
bool inh
Definition: primnodes.h:86
void ProcessUtilityForAlterTable(Node *stmt, AlterTableUtilityContext *context)
Definition: utility.c:1958

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

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

◆ ATPostAlterTypeCleanup()

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

Definition at line 15464 of file tablecmds.c.

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

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

15658{
15659 List *raw_parsetree_list;
15660 List *querytree_list;
15661 ListCell *list_item;
15662 Relation rel;
15663
15664 /*
15665 * We expect that we will get only ALTER TABLE and CREATE INDEX
15666 * statements. Hence, there is no need to pass them through
15667 * parse_analyze_*() or the rewriter, but instead we need to pass them
15668 * through parse_utilcmd.c to make them ready for execution.
15669 */
15670 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15671 querytree_list = NIL;
15672 foreach(list_item, raw_parsetree_list)
15673 {
15674 RawStmt *rs = lfirst_node(RawStmt, list_item);
15675 Node *stmt = rs->stmt;
15676
15677 if (IsA(stmt, IndexStmt))
15678 querytree_list = lappend(querytree_list,
15679 transformIndexStmt(oldRelId,
15680 (IndexStmt *) stmt,
15681 cmd));
15682 else if (IsA(stmt, AlterTableStmt))
15683 {
15684 List *beforeStmts;
15685 List *afterStmts;
15686
15687 stmt = (Node *) transformAlterTableStmt(oldRelId,
15688 (AlterTableStmt *) stmt,
15689 cmd,
15690 &beforeStmts,
15691 &afterStmts);
15692 querytree_list = list_concat(querytree_list, beforeStmts);
15693 querytree_list = lappend(querytree_list, stmt);
15694 querytree_list = list_concat(querytree_list, afterStmts);
15695 }
15696 else if (IsA(stmt, CreateStatsStmt))
15697 querytree_list = lappend(querytree_list,
15698 transformStatsStmt(oldRelId,
15700 cmd));
15701 else
15702 querytree_list = lappend(querytree_list, stmt);
15703 }
15704
15705 /* Caller should already have acquired whatever lock we need. */
15706 rel = relation_open(oldRelId, NoLock);
15707
15708 /*
15709 * Attach each generated command to the proper place in the work queue.
15710 * Note this could result in creation of entirely new work-queue entries.
15711 *
15712 * Also note that we have to tweak the command subtypes, because it turns
15713 * out that re-creation of indexes and constraints has to act a bit
15714 * differently from initial creation.
15715 */
15716 foreach(list_item, querytree_list)
15717 {
15718 Node *stm = (Node *) lfirst(list_item);
15719 AlteredTableInfo *tab;
15720
15721 tab = ATGetQueueEntry(wqueue, rel);
15722
15723 if (IsA(stm, IndexStmt))
15724 {
15725 IndexStmt *stmt = (IndexStmt *) stm;
15726 AlterTableCmd *newcmd;
15727
15728 if (!rewrite)
15729 TryReuseIndex(oldId, stmt);
15730 stmt->reset_default_tblspc = true;
15731 /* keep the index's comment */
15732 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15733
15734 newcmd = makeNode(AlterTableCmd);
15735 newcmd->subtype = AT_ReAddIndex;
15736 newcmd->def = (Node *) stmt;
15738 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15739 }
15740 else if (IsA(stm, AlterTableStmt))
15741 {
15743 ListCell *lcmd;
15744
15745 foreach(lcmd, stmt->cmds)
15746 {
15748
15749 if (cmd->subtype == AT_AddIndex)
15750 {
15751 IndexStmt *indstmt;
15752 Oid indoid;
15753
15754 indstmt = castNode(IndexStmt, cmd->def);
15755 indoid = get_constraint_index(oldId);
15756
15757 if (!rewrite)
15758 TryReuseIndex(indoid, indstmt);
15759 /* keep any comment on the index */
15760 indstmt->idxcomment = GetComment(indoid,
15761 RelationRelationId, 0);
15762 indstmt->reset_default_tblspc = true;
15763
15764 cmd->subtype = AT_ReAddIndex;
15766 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15767
15768 /* recreate any comment on the constraint */
15771 oldId,
15772 rel,
15773 NIL,
15774 indstmt->idxname);
15775 }
15776 else if (cmd->subtype == AT_AddConstraint)
15777 {
15778 Constraint *con = castNode(Constraint, cmd->def);
15779
15780 con->old_pktable_oid = refRelId;
15781 /* rewriting neither side of a FK */
15782 if (con->contype == CONSTR_FOREIGN &&
15783 !rewrite && tab->rewrite == 0)
15784 TryReuseForeignKey(oldId, con);
15785 con->reset_default_tblspc = true;
15789
15790 /*
15791 * Recreate any comment on the constraint. If we have
15792 * recreated a primary key, then transformTableConstraint
15793 * has added an unnamed not-null constraint here; skip
15794 * this in that case.
15795 */
15796 if (con->conname)
15799 oldId,
15800 rel,
15801 NIL,
15802 con->conname);
15803 else
15804 Assert(con->contype == CONSTR_NOTNULL);
15805 }
15806 else
15807 elog(ERROR, "unexpected statement subtype: %d",
15808 (int) cmd->subtype);
15809 }
15810 }
15811 else if (IsA(stm, AlterDomainStmt))
15812 {
15814
15815 if (stmt->subtype == AD_AddConstraint)
15816 {
15817 Constraint *con = castNode(Constraint, stmt->def);
15819
15821 cmd->def = (Node *) stmt;
15824
15825 /* recreate any comment on the constraint */
15828 oldId,
15829 NULL,
15830 stmt->typeName,
15831 con->conname);
15832 }
15833 else
15834 elog(ERROR, "unexpected statement subtype: %d",
15835 (int) stmt->subtype);
15836 }
15837 else if (IsA(stm, CreateStatsStmt))
15838 {
15840 AlterTableCmd *newcmd;
15841
15842 /* keep the statistics object's comment */
15843 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15844
15845 newcmd = makeNode(AlterTableCmd);
15846 newcmd->subtype = AT_ReAddStatistics;
15847 newcmd->def = (Node *) stmt;
15848 tab->subcmds[AT_PASS_MISC] =
15849 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15850 }
15851 else
15852 elog(ERROR, "unexpected statement type: %d",
15853 (int) nodeTag(stm));
15854 }
15855
15856 relation_close(rel, NoLock);
15857}
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:1204
#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:2575
@ RAW_PARSE_DEFAULT
Definition: parser.h:39
bool reset_default_tblspc
Definition: parsenodes.h:2886
bool reset_default_tblspc
Definition: parsenodes.h:3539
char * idxname
Definition: parsenodes.h:3513
char * idxcomment
Definition: parsenodes.h:3523
Node * stmt
Definition: parsenodes.h:2114
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:15914
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:15943
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:15870

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

7224{
7225 if (rel->rd_rel->reloftype && !recursing)
7226 ereport(ERROR,
7227 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7228 errmsg("cannot add column to typed table")));
7229
7230 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7231 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7232
7233 if (recurse && !is_view)
7234 cmd->recurse = true;
7235}
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6919

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

17268{
17269 if (child_rel->rd_rel->reloftype)
17270 ereport(ERROR,
17271 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17272 errmsg("cannot change inheritance of typed table")));
17273
17274 if (child_rel->rd_rel->relispartition)
17275 ereport(ERROR,
17276 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17277 errmsg("cannot change inheritance of a partition")));
17278
17279 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17280 ereport(ERROR,
17281 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17282 errmsg("cannot change inheritance of partitioned table")));
17283}

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

9530{
9531 Constraint *pkconstr;
9532 List *children = NIL;
9533 bool got_children = false;
9534
9535 pkconstr = castNode(Constraint, cmd->def);
9536 if (pkconstr->contype != CONSTR_PRIMARY)
9537 return;
9538
9539 /* Verify that columns are not-null, or request that they be made so */
9540 foreach_node(String, column, pkconstr->keys)
9541 {
9542 AlterTableCmd *newcmd;
9543 Constraint *nnconstr;
9544 HeapTuple tuple;
9545
9546 /*
9547 * First check if a suitable constraint exists. If it does, we don't
9548 * need to request another one. We do need to bail out if it's not
9549 * valid, though.
9550 */
9551 tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9552 if (tuple != NULL)
9553 {
9554 verifyNotNullPKCompatible(tuple, strVal(column));
9555
9556 /* All good with this one; don't request another */
9557 heap_freetuple(tuple);
9558 continue;
9559 }
9560 else if (!recurse)
9561 {
9562 /*
9563 * No constraint on this column. Asked not to recurse, we won't
9564 * create one here, but verify that all children have one.
9565 */
9566 if (!got_children)
9567 {
9569 lockmode);
9570 /* only search for children on the first time through */
9571 got_children = true;
9572 }
9573
9574 foreach_oid(childrelid, children)
9575 {
9576 HeapTuple tup;
9577
9578 tup = findNotNullConstraint(childrelid, strVal(column));
9579 if (!tup)
9580 ereport(ERROR,
9581 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9582 strVal(column), get_rel_name(childrelid)));
9583 /* verify it's good enough */
9584 verifyNotNullPKCompatible(tup, strVal(column));
9585 }
9586 }
9587
9588 /* This column is not already not-null, so add it to the queue */
9589 nnconstr = makeNotNullConstraint(column);
9590
9591 newcmd = makeNode(AlterTableCmd);
9592 newcmd->subtype = AT_AddConstraint;
9593 /* note we force recurse=true here; see above */
9594 newcmd->recurse = true;
9595 newcmd->def = (Node *) nnconstr;
9596
9597 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9598 }
9599}
List * keys
Definition: parsenodes.h:2876
Definition: value.h:64
static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
Definition: tablecmds.c:9606

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

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

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

18848{
18849 Relation pg_constraint;
18850 HeapTuple tuple;
18851 SysScanDesc scan;
18852 ScanKeyData skey[1];
18853
18854 /*
18855 * Disallow changing status for a temp table. Also verify whether we can
18856 * get away with doing nothing; in such cases we don't need to run the
18857 * checks below, either.
18858 */
18859 switch (rel->rd_rel->relpersistence)
18860 {
18861 case RELPERSISTENCE_TEMP:
18862 ereport(ERROR,
18863 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18864 errmsg("cannot change logged status of table \"%s\" because it is temporary",
18866 errtable(rel)));
18867 break;
18868 case RELPERSISTENCE_PERMANENT:
18869 if (toLogged)
18870 /* nothing to do */
18871 return;
18872 break;
18873 case RELPERSISTENCE_UNLOGGED:
18874 if (!toLogged)
18875 /* nothing to do */
18876 return;
18877 break;
18878 }
18879
18880 /*
18881 * Check that the table is not part of any publication when changing to
18882 * UNLOGGED, as UNLOGGED tables can't be published.
18883 */
18884 if (!toLogged &&
18886 ereport(ERROR,
18887 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18888 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18890 errdetail("Unlogged relations cannot be replicated.")));
18891
18892 /*
18893 * Check existing foreign key constraints to preserve the invariant that
18894 * permanent tables cannot reference unlogged ones. Self-referencing
18895 * foreign keys can safely be ignored.
18896 */
18897 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18898
18899 /*
18900 * Scan conrelid if changing to permanent, else confrelid. This also
18901 * determines whether a useful index exists.
18902 */
18903 ScanKeyInit(&skey[0],
18904 toLogged ? Anum_pg_constraint_conrelid :
18905 Anum_pg_constraint_confrelid,
18906 BTEqualStrategyNumber, F_OIDEQ,
18908 scan = systable_beginscan(pg_constraint,
18909 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18910 true, NULL, 1, skey);
18911
18912 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18913 {
18915
18916 if (con->contype == CONSTRAINT_FOREIGN)
18917 {
18918 Oid foreignrelid;
18919 Relation foreignrel;
18920
18921 /* the opposite end of what we used as scankey */
18922 foreignrelid = toLogged ? con->confrelid : con->conrelid;
18923
18924 /* ignore if self-referencing */
18925 if (RelationGetRelid(rel) == foreignrelid)
18926 continue;
18927
18928 foreignrel = relation_open(foreignrelid, AccessShareLock);
18929
18930 if (toLogged)
18931 {
18932 if (!RelationIsPermanent(foreignrel))
18933 ereport(ERROR,
18934 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18935 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18937 RelationGetRelationName(foreignrel)),
18938 errtableconstraint(rel, NameStr(con->conname))));
18939 }
18940 else
18941 {
18942 if (RelationIsPermanent(foreignrel))
18943 ereport(ERROR,
18944 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18945 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18947 RelationGetRelationName(foreignrel)),
18948 errtableconstraint(rel, NameStr(con->conname))));
18949 }
18950
18951 relation_close(foreignrel, AccessShareLock);
18952 }
18953 }
18954
18955 systable_endscan(scan);
18956
18957 table_close(pg_constraint, AccessShareLock);
18958
18959 /* force rewrite if necessary; see comment in ATRewriteTables */
18961 if (toLogged)
18962 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18963 else
18964 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18965 tab->chgPersistence = true;
18966}
#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 4907 of file tablecmds.c.

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

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_MergePartitions, 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_SplitPartition, 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 9284 of file tablecmds.c.

9287{
9288 if (rel->rd_rel->reloftype && !recursing)
9289 ereport(ERROR,
9290 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9291 errmsg("cannot drop column from typed table")));
9292
9293 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9294 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9295
9296 if (recurse)
9297 cmd->recurse = true;
9298}

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

8785{
8786 /*
8787 * Reject ONLY if there are child tables. We could implement this, but it
8788 * is a bit complicated. GENERATED clauses must be attached to the column
8789 * definition and cannot be added later like DEFAULT, so if a child table
8790 * has a generation expression that the parent does not have, the child
8791 * column will necessarily be an attislocal column. So to implement ONLY
8792 * here, we'd need extra code to update attislocal of the direct child
8793 * tables, somewhat similar to how DROP COLUMN does it, so that the
8794 * resulting state can be properly dumped and restored.
8795 */
8796 if (!recurse &&
8798 ereport(ERROR,
8799 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8800 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8801
8802 /*
8803 * Cannot drop generation expression from inherited columns.
8804 */
8805 if (!recursing)
8806 {
8807 HeapTuple tuple;
8808 Form_pg_attribute attTup;
8809
8811 if (!HeapTupleIsValid(tuple))
8812 ereport(ERROR,
8813 (errcode(ERRCODE_UNDEFINED_COLUMN),
8814 errmsg("column \"%s\" of relation \"%s\" does not exist",
8815 cmd->name, RelationGetRelationName(rel))));
8816
8817 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8818
8819 if (attTup->attinhcount > 0)
8820 ereport(ERROR,
8821 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8822 errmsg("cannot drop generation expression from inherited column")));
8823 }
8824}

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

16520{
16521 Oid amoid;
16522
16523 /*
16524 * Look up the access method name and check that it differs from the
16525 * table's current AM. If DEFAULT was specified for a partitioned table
16526 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16527 */
16528 if (amname != NULL)
16529 amoid = get_table_am_oid(amname, false);
16530 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16531 amoid = InvalidOid;
16532 else
16534
16535 /* if it's a match, phase 3 doesn't need to do anything */
16536 if (rel->rd_rel->relam == amoid)
16537 return;
16538
16539 /* Save info for Phase 3 to do the real work */
16541 tab->newAccessMethod = amoid;
16542 tab->chgAccessMethod = true;
16543}
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 16643 of file tablecmds.c.

16644{
16645 Oid tablespaceId;
16646
16647 /* Check that the tablespace exists */
16648 tablespaceId = get_tablespace_oid(tablespacename, false);
16649
16650 /* Check permissions except when moving to database's default */
16651 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16652 {
16653 AclResult aclresult;
16654
16655 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16656 if (aclresult != ACLCHECK_OK)
16657 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16658 }
16659
16660 /* Save info for Phase 3 to do the real work */
16661 if (OidIsValid(tab->newTableSpace))
16662 ereport(ERROR,
16663 (errcode(ERRCODE_SYNTAX_ERROR),
16664 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16665
16666 tab->newTableSpace = tablespaceId;
16667}

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

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

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

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

Referenced by ATRewriteTables().

◆ ATRewriteTables()

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

Definition at line 5862 of file tablecmds.c.

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

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

6768{
6769 int actual_target;
6770
6771 switch (rel->rd_rel->relkind)
6772 {
6773 case RELKIND_RELATION:
6774 actual_target = ATT_TABLE;
6775 break;
6776 case RELKIND_PARTITIONED_TABLE:
6777 actual_target = ATT_PARTITIONED_TABLE;
6778 break;
6779 case RELKIND_VIEW:
6780 actual_target = ATT_VIEW;
6781 break;
6782 case RELKIND_MATVIEW:
6783 actual_target = ATT_MATVIEW;
6784 break;
6785 case RELKIND_INDEX:
6786 actual_target = ATT_INDEX;
6787 break;
6788 case RELKIND_PARTITIONED_INDEX:
6789 actual_target = ATT_PARTITIONED_INDEX;
6790 break;
6791 case RELKIND_COMPOSITE_TYPE:
6792 actual_target = ATT_COMPOSITE_TYPE;
6793 break;
6794 case RELKIND_FOREIGN_TABLE:
6795 actual_target = ATT_FOREIGN_TABLE;
6796 break;
6797 case RELKIND_SEQUENCE:
6798 actual_target = ATT_SEQUENCE;
6799 break;
6800 default:
6801 actual_target = 0;
6802 break;
6803 }
6804
6805 /* Wrong target type? */
6806 if ((actual_target & allowed_targets) == 0)
6807 {
6808 const char *action_str = alter_table_type_to_string(cmdtype);
6809
6810 if (action_str)
6811 ereport(ERROR,
6812 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6813 /* translator: %s is a group of some SQL keywords */
6814 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6815 action_str, RelationGetRelationName(rel)),
6816 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6817 else
6818 /* internal error? */
6819 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6821 }
6822
6823 /* Permissions checks */
6824 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6827
6829 ereport(ERROR,
6830 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6831 errmsg("permission denied: \"%s\" is a system catalog",
6833}
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition: tablecmds.c:6620

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

6847{
6848 /*
6849 * Propagate to children, if desired and if there are (or might be) any
6850 * children.
6851 */
6852 if (recurse && rel->rd_rel->relhassubclass)
6853 {
6854 Oid relid = RelationGetRelid(rel);
6855 ListCell *child;
6856 List *children;
6857
6858 children = find_all_inheritors(relid, lockmode, NULL);
6859
6860 /*
6861 * find_all_inheritors does the recursive search of the inheritance
6862 * hierarchy, so all we have to do is process all of the relids in the
6863 * list that it returns.
6864 */
6865 foreach(child, children)
6866 {
6867 Oid childrelid = lfirst_oid(child);
6868 Relation childrel;
6869
6870 if (childrelid == relid)
6871 continue;
6872 /* find_all_inheritors already got lock */
6873 childrel = relation_open(childrelid, NoLock);
6874 CheckAlterTableIsSafe(childrel);
6875 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6876 relation_close(childrel, NoLock);
6877 }
6878 }
6879}

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

20616{
20617 List *idxes;
20618 List *attachRelIdxs;
20619 Relation *attachrelIdxRels;
20620 IndexInfo **attachInfos;
20621 ListCell *cell;
20622 MemoryContext cxt;
20623 MemoryContext oldcxt;
20624
20626 "AttachPartitionEnsureIndexes",
20628 oldcxt = MemoryContextSwitchTo(cxt);
20629
20630 idxes = RelationGetIndexList(rel);
20631 attachRelIdxs = RelationGetIndexList(attachrel);
20632 attachrelIdxRels = palloc_array(Relation, list_length(attachRelIdxs));
20633 attachInfos = palloc_array(IndexInfo *, list_length(attachRelIdxs));
20634
20635 /* Build arrays of all existing indexes and their IndexInfos */
20636 foreach_oid(cldIdxId, attachRelIdxs)
20637 {
20638 int i = foreach_current_index(cldIdxId);
20639
20640 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20641 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20642 }
20643
20644 /*
20645 * If we're attaching a foreign table, we must fail if any of the indexes
20646 * is a constraint index; otherwise, there's nothing to do here. Do this
20647 * before starting work, to avoid wasting the effort of building a few
20648 * non-unique indexes before coming across a unique one.
20649 */
20650 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20651 {
20652 foreach(cell, idxes)
20653 {
20654 Oid idx = lfirst_oid(cell);
20656
20657 if (idxRel->rd_index->indisunique ||
20658 idxRel->rd_index->indisprimary)
20659 ereport(ERROR,
20660 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20661 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20662 RelationGetRelationName(attachrel),
20664 errdetail("Partitioned table \"%s\" contains unique indexes.",
20667 }
20668
20669 goto out;
20670 }
20671
20672 /*
20673 * For each index on the partitioned table, find a matching one in the
20674 * partition-to-be; if one is not found, create one.
20675 */
20676 foreach(cell, idxes)
20677 {
20678 Oid idx = lfirst_oid(cell);
20680 IndexInfo *info;
20681 AttrMap *attmap;
20682 bool found = false;
20683 Oid constraintOid;
20684
20685 /*
20686 * Ignore indexes in the partitioned table other than partitioned
20687 * indexes.
20688 */
20689 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20690 {
20692 continue;
20693 }
20694
20695 /* construct an indexinfo to compare existing indexes against */
20696 info = BuildIndexInfo(idxRel);
20697 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20698 RelationGetDescr(rel),
20699 false);
20701
20702 /*
20703 * Scan the list of existing indexes in the partition-to-be, and mark
20704 * the first matching, valid, unattached one we find, if any, as
20705 * partition of the parent index. If we find one, we're done.
20706 */
20707 for (int i = 0; i < list_length(attachRelIdxs); i++)
20708 {
20709 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20710 Oid cldConstrOid = InvalidOid;
20711
20712 /* does this index have a parent? if so, can't use it */
20713 if (attachrelIdxRels[i]->rd_rel->relispartition)
20714 continue;
20715
20716 /* If this index is invalid, can't use it */
20717 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20718 continue;
20719
20720 if (CompareIndexInfo(attachInfos[i], info,
20721 attachrelIdxRels[i]->rd_indcollation,
20722 idxRel->rd_indcollation,
20723 attachrelIdxRels[i]->rd_opfamily,
20724 idxRel->rd_opfamily,
20725 attmap))
20726 {
20727 /*
20728 * If this index is being created in the parent because of a
20729 * constraint, then the child needs to have a constraint also,
20730 * so look for one. If there is no such constraint, this
20731 * index is no good, so keep looking.
20732 */
20733 if (OidIsValid(constraintOid))
20734 {
20735 cldConstrOid =
20737 cldIdxId);
20738 /* no dice */
20739 if (!OidIsValid(cldConstrOid))
20740 continue;
20741
20742 /* Ensure they're both the same type of constraint */
20743 if (get_constraint_type(constraintOid) !=
20744 get_constraint_type(cldConstrOid))
20745 continue;
20746 }
20747
20748 /* bingo. */
20749 IndexSetParentIndex(attachrelIdxRels[i], idx);
20750 if (OidIsValid(constraintOid))
20751 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20752 RelationGetRelid(attachrel));
20753 found = true;
20754
20756 break;
20757 }
20758 }
20759
20760 /*
20761 * If no suitable index was found in the partition-to-be, create one
20762 * now. Note that if this is a PK, not-null constraints must already
20763 * exist.
20764 */
20765 if (!found)
20766 {
20767 IndexStmt *stmt;
20768 Oid conOid;
20769
20771 idxRel, attmap,
20772 &conOid);
20774 RelationGetRelid(idxRel),
20775 conOid,
20776 -1,
20777 true, false, false, false, false);
20778 }
20779
20781 }
20782
20783out:
20784 /* Clean up. */
20785 for (int i = 0; i < list_length(attachRelIdxs); i++)
20786 index_close(attachrelIdxRels[i], AccessShareLock);
20787 MemoryContextSwitchTo(oldcxt);
20789}
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:262
char get_constraint_type(Oid conoid)
Definition: lsyscache.c:1234
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_array, RelationData::rd_indcollation, RelationData::rd_index, RelationData::rd_opfamily, RelationData::rd_rel, RelationGetDescr, RelationGetIndexList(), RelationGetRelationName, RelationGetRelid, and stmt.

Referenced by attachPartitionTable().

◆ AttachPartitionForeignKey()

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

Definition at line 11818 of file tablecmds.c.

11825{
11826 HeapTuple parentConstrTup;
11827 Form_pg_constraint parentConstr;
11828 HeapTuple partcontup;
11829 Form_pg_constraint partConstr;
11830 bool queueValidation;
11831 Oid partConstrFrelid;
11832 Oid partConstrRelid;
11833 bool parentConstrIsEnforced;
11834
11835 /* Fetch the parent constraint tuple */
11836 parentConstrTup = SearchSysCache1(CONSTROID,
11837 ObjectIdGetDatum(parentConstrOid));
11838 if (!HeapTupleIsValid(parentConstrTup))
11839 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11840 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11841 parentConstrIsEnforced = parentConstr->conenforced;
11842
11843 /* Fetch the child constraint tuple */
11844 partcontup = SearchSysCache1(CONSTROID,
11845 ObjectIdGetDatum(partConstrOid));
11846 if (!HeapTupleIsValid(partcontup))
11847 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11848 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11849 partConstrFrelid = partConstr->confrelid;
11850 partConstrRelid = partConstr->conrelid;
11851
11852 /*
11853 * If the referenced table is partitioned, then the partition we're
11854 * attaching now has extra pg_constraint rows and action triggers that are
11855 * no longer needed. Remove those.
11856 */
11857 if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11858 {
11859 Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11860
11861 RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11862 partConstrRelid);
11863
11864 table_close(pg_constraint, RowShareLock);
11865 }
11866
11867 /*
11868 * Will we need to validate this constraint? A valid parent constraint
11869 * implies that all child constraints have been validated, so if this one
11870 * isn't, we must trigger phase 3 validation.
11871 */
11872 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11873
11874 ReleaseSysCache(partcontup);
11875 ReleaseSysCache(parentConstrTup);
11876
11877 /*
11878 * The action triggers in the new partition become redundant -- the parent
11879 * table already has equivalent ones, and those will be able to reach the
11880 * partition. Remove the ones in the partition. We identify them because
11881 * they have our constraint OID, as well as being on the referenced rel.
11882 */
11883 DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11884 partConstrRelid);
11885
11886 ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11887 RelationGetRelid(partition));
11888
11889 /*
11890 * Like the constraint, attach partition's "check" triggers to the
11891 * corresponding parent triggers if the constraint is ENFORCED. NOT
11892 * ENFORCED constraints do not have these triggers.
11893 */
11894 if (parentConstrIsEnforced)
11895 {
11896 Oid insertTriggerOid,
11897 updateTriggerOid;
11898
11900 partConstrOid, partConstrFrelid, partConstrRelid,
11901 &insertTriggerOid, &updateTriggerOid);
11902 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11903 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11904 RelationGetRelid(partition));
11905 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11906 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11907 RelationGetRelid(partition));
11908 }
11909
11910 /*
11911 * We updated this pg_constraint row above to set its parent; validating
11912 * it will cause its convalidated flag to change, so we need CCI here. In
11913 * addition, we need it unconditionally for the rare case where the parent
11914 * table has *two* identical constraints; when reaching this function for
11915 * the second one, we must have made our changes visible, otherwise we
11916 * would try to attach both to this one.
11917 */
11919
11920 /* If validation is needed, put it in the queue now. */
11921 if (queueValidation)
11922 {
11923 Relation conrel;
11924 Oid confrelid;
11925
11926 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11927
11928 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11929 if (!HeapTupleIsValid(partcontup))
11930 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11931
11932 confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11933
11934 /* Use the same lock as for AT_ValidateConstraint */
11935 QueueFKConstraintValidation(wqueue, conrel, partition, confrelid,
11936 partcontup, ShareUpdateExclusiveLock);
11937 ReleaseSysCache(partcontup);
11939 }
11940}
static void RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
Definition: tablecmds.c:11949
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12160
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().

◆ attachPartitionTable()

static void attachPartitionTable ( List **  wqueue,
Relation  rel,
Relation  attachrel,
PartitionBoundSpec bound 
)
static

Definition at line 20280 of file tablecmds.c.

20281{
20282 /*
20283 * Create an inheritance; the relevant checks are performed inside the
20284 * function.
20285 */
20286 CreateInheritance(attachrel, rel, true);
20287
20288 /* Update the pg_class entry. */
20289 StorePartitionBound(attachrel, rel, bound);
20290
20291 /* Ensure there exists a correct set of indexes in the partition. */
20292 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20293
20294 /* and triggers */
20295 CloneRowTriggersToPartition(rel, attachrel);
20296
20297 /*
20298 * Clone foreign key constraints. Callee is responsible for setting up
20299 * for phase 3 constraint verification.
20300 */
20301 CloneForeignKeyConstraints(wqueue, rel, attachrel);
20302}
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition: heap.c:4050
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
Definition: tablecmds.c:20797
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:20615
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11245

References AttachPartitionEnsureIndexes(), CloneForeignKeyConstraints(), CloneRowTriggersToPartition(), CreateInheritance(), and StorePartitionBound().

Referenced by ATExecAttachPartition(), ATExecMergePartitions(), and ATExecSplitPartition().

◆ ATTypedTableRecursion()

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

Definition at line 6919 of file tablecmds.c.

6921{
6922 ListCell *child;
6923 List *children;
6924
6925 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6926
6927 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6929 cmd->behavior);
6930
6931 foreach(child, children)
6932 {
6933 Oid childrelid = lfirst_oid(child);
6934 Relation childrel;
6935
6936 childrel = relation_open(childrelid, lockmode);
6937 CheckAlterTableIsSafe(childrel);
6938 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6939 relation_close(childrel, NoLock);
6940 }
6941}
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition: tablecmds.c:7122

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

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

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

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

◆ buildExpressionExecutionStates()

static void buildExpressionExecutionStates ( AlteredTableInfo tab,
Relation  newPartRel,
EState estate 
)
static

Definition at line 22112 of file tablecmds.c.

22113{
22114 /*
22115 * Build the needed expression execution states. Here, we expect only NOT
22116 * NULL and CHECK constraint.
22117 */
22119 {
22120 switch (con->contype)
22121 {
22122 case CONSTR_CHECK:
22123
22124 /*
22125 * We already expanded virtual expression in
22126 * createTableConstraints.
22127 */
22128 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
22129 break;
22130 case CONSTR_NOTNULL:
22131 /* Nothing to do here. */
22132 break;
22133 default:
22134 elog(ERROR, "unrecognized constraint type: %d",
22135 (int) con->contype);
22136 }
22137 }
22138
22139 /* Expression already planned in createTableConstraints */
22141 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
22142}
#define foreach_ptr(type, var, lst)
Definition: pg_list.h:469

References CONSTR_CHECK, CONSTR_NOTNULL, AlteredTableInfo::constraints, elog, ERROR, ExecInitExpr(), ExecPrepareExpr(), foreach_ptr, and AlteredTableInfo::newvals.

Referenced by MergePartitionsMoveRows(), and SplitPartitionMoveRows().

◆ change_owner_fix_column_acls()

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

Definition at line 16341 of file tablecmds.c.

16342{
16343 Relation attRelation;
16344 SysScanDesc scan;
16345 ScanKeyData key[1];
16346 HeapTuple attributeTuple;
16347
16348 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16349 ScanKeyInit(&key[0],
16350 Anum_pg_attribute_attrelid,
16351 BTEqualStrategyNumber, F_OIDEQ,
16352 ObjectIdGetDatum(relationOid));
16353 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16354 true, NULL, 1, key);
16355 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16356 {
16357 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16358 Datum repl_val[Natts_pg_attribute];
16359 bool repl_null[Natts_pg_attribute];
16360 bool repl_repl[Natts_pg_attribute];
16361 Acl *newAcl;
16362 Datum aclDatum;
16363 bool isNull;
16364 HeapTuple newtuple;
16365
16366 /* Ignore dropped columns */
16367 if (att->attisdropped)
16368 continue;
16369
16370 aclDatum = heap_getattr(attributeTuple,
16371 Anum_pg_attribute_attacl,
16372 RelationGetDescr(attRelation),
16373 &isNull);
16374 /* Null ACLs do not require changes */
16375 if (isNull)
16376 continue;
16377
16378 memset(repl_null, false, sizeof(repl_null));
16379 memset(repl_repl, false, sizeof(repl_repl));
16380
16381 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16382 oldOwnerId, newOwnerId);
16383 repl_repl[Anum_pg_attribute_attacl - 1] = true;
16384 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16385
16386 newtuple = heap_modify_tuple(attributeTuple,
16387 RelationGetDescr(attRelation),
16388 repl_val, repl_null, repl_repl);
16389
16390 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16391
16392 heap_freetuple(newtuple);
16393 }
16394 systable_endscan(scan);
16395 table_close(attRelation, RowExclusiveLock);
16396}

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

16407{
16408 Relation depRel;
16409 SysScanDesc scan;
16410 ScanKeyData key[2];
16411 HeapTuple tup;
16412
16413 /*
16414 * SERIAL sequences are those having an auto dependency on one of the
16415 * table's columns (we don't care *which* column, exactly).
16416 */
16417 depRel = table_open(DependRelationId, AccessShareLock);
16418
16419 ScanKeyInit(&key[0],
16420 Anum_pg_depend_refclassid,
16421 BTEqualStrategyNumber, F_OIDEQ,
16422 ObjectIdGetDatum(RelationRelationId));
16423 ScanKeyInit(&key[1],
16424 Anum_pg_depend_refobjid,
16425 BTEqualStrategyNumber, F_OIDEQ,
16426 ObjectIdGetDatum(relationOid));
16427 /* we leave refobjsubid unspecified */
16428
16429 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16430 NULL, 2, key);
16431
16432 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16433 {
16434 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16435 Relation seqRel;
16436
16437 /* skip dependencies other than auto dependencies on columns */
16438 if (depForm->refobjsubid == 0 ||
16439 depForm->classid != RelationRelationId ||
16440 depForm->objsubid != 0 ||
16441 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16442 continue;
16443
16444 /* Use relation_open just in case it's an index */
16445 seqRel = relation_open(depForm->objid, lockmode);
16446
16447 /* skip non-sequence relations */
16448 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16449 {
16450 /* No need to keep the lock */
16451 relation_close(seqRel, lockmode);
16452 continue;
16453 }
16454
16455 /* We don't need to close the sequence while we alter it. */
16456 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16457
16458 /* Now we can close it. Keep the lock till end of transaction. */
16459 relation_close(seqRel, NoLock);
16460 }
16461
16462 systable_endscan(scan);
16463
16465}

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

7676{
7677 HeapTuple attTuple;
7678 int attnum;
7679
7680 /*
7681 * this test is deliberately not attisdropped-aware, since if one tries to
7682 * add a column matching a dropped column name, it's gonna fail anyway.
7683 */
7684 attTuple = SearchSysCache2(ATTNAME,
7686 PointerGetDatum(colname));
7687 if (!HeapTupleIsValid(attTuple))
7688 return true;
7689
7690 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7691 ReleaseSysCache(attTuple);
7692
7693 /*
7694 * We throw a different error message for conflicts with system column
7695 * names, since they are normally not shown and the user might otherwise
7696 * be confused about the reason for the conflict.
7697 */
7698 if (attnum <= 0)
7699 ereport(ERROR,
7700 (errcode(ERRCODE_DUPLICATE_COLUMN),
7701 errmsg("column name \"%s\" conflicts with a system column name",
7702 colname)));
7703 else
7704 {
7705 if (if_not_exists)
7706 {
7708 (errcode(ERRCODE_DUPLICATE_COLUMN),
7709 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7710 colname, RelationGetRelationName(rel))));
7711 return false;
7712 }
7713
7714 ereport(ERROR,
7715 (errcode(ERRCODE_DUPLICATE_COLUMN),
7716 errmsg("column \"%s\" of relation \"%s\" already exists",
7717 colname, RelationGetRelationName(rel))));
7718 }
7719
7720 return true;
7721}
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 7171 of file tablecmds.c.

7172{
7173 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7174 bool typeOk = false;
7175
7176 if (typ->typtype == TYPTYPE_COMPOSITE)
7177 {
7178 Relation typeRelation;
7179
7180 Assert(OidIsValid(typ->typrelid));
7181 typeRelation = relation_open(typ->typrelid, AccessShareLock);
7182 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7183
7184 /*
7185 * Close the parent rel, but keep our AccessShareLock on it until xact
7186 * commit. That will prevent someone else from deleting or ALTERing
7187 * the type before the typed table creation/conversion commits.
7188 */
7189 relation_close(typeRelation, NoLock);
7190
7191 if (!typeOk)
7192 ereport(ERROR,
7193 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7194 errmsg("type %s is the row type of another table",
7195 format_type_be(typ->oid)),
7196 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7197 }
7198 else
7199 ereport(ERROR,
7200 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7201 errmsg("type %s is not a composite type",
7202 format_type_be(typ->oid))));
7203}

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

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

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

13694{
13695 Oid roleid = GetUserId();
13696 AclResult aclresult;
13697 int i;
13698
13699 /* Okay if we have relation-level REFERENCES permission */
13700 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13702 if (aclresult == ACLCHECK_OK)
13703 return;
13704 /* Else we must have REFERENCES on each column */
13705 for (i = 0; i < natts; i++)
13706 {
13707 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13708 roleid, ACL_REFERENCES);
13709 if (aclresult != ACLCHECK_OK)
13710 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13712 }
13713}
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3868
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4039
#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 3690 of file tablecmds.c.

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

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

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

◆ CheckTableNotInUse()

void CheckTableNotInUse ( Relation  rel,
const char *  stmt 
)

Definition at line 4413 of file tablecmds.c.

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

9899{
9900 char buf[NAMEDATALEN * 2];
9901 int buflen = 0;
9902 ListCell *lc;
9903
9904 buf[0] = '\0';
9905 foreach(lc, colnames)
9906 {
9907 const char *name = strVal(lfirst(lc));
9908
9909 if (buflen > 0)
9910 buf[buflen++] = '_'; /* insert _ between names */
9911
9912 /*
9913 * At this point we have buflen <= NAMEDATALEN. name should be less
9914 * than NAMEDATALEN already, but use strlcpy for paranoia.
9915 */
9916 strlcpy(buf + buflen, name, NAMEDATALEN);
9917 buflen += strlen(buf + buflen);
9918 if (buflen >= NAMEDATALEN)
9919 break;
9920 }
9921 return pstrdup(buf);
9922}
static char buf[DEFAULT_XLOG_SEG_SIZE]
Definition: pg_test_fsync.c:71
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 11274 of file tablecmds.c.

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

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

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

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

11247{
11248 /* This only works for declarative partitioning */
11249 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11250
11251 /*
11252 * First, clone constraints where the parent is on the referencing side.
11253 */
11254 CloneFkReferencing(wqueue, parentRel, partitionRel);
11255
11256 /*
11257 * Clone constraints for which the parent is on the referenced side.
11258 */
11259 CloneFkReferenced(parentRel, partitionRel);
11260}
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11475
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11274

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

Referenced by attachPartitionTable(), and DefineRelation().

◆ CloneRowTriggersToPartition()

static void CloneRowTriggersToPartition ( Relation  parent,
Relation  partition 
)
static

Definition at line 20797 of file tablecmds.c.

20798{
20799 Relation pg_trigger;
20801 SysScanDesc scan;
20802 HeapTuple tuple;
20803 MemoryContext perTupCxt;
20804
20805 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20806 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20807 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20808 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20809 true, NULL, 1, &key);
20810
20812 "clone trig", ALLOCSET_SMALL_SIZES);
20813
20814 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20815 {
20816 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20817 CreateTrigStmt *trigStmt;
20818 Node *qual = NULL;
20819 Datum value;
20820 bool isnull;
20821 List *cols = NIL;
20822 List *trigargs = NIL;
20823 MemoryContext oldcxt;
20824
20825 /*
20826 * Ignore statement-level triggers; those are not cloned.
20827 */
20828 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20829 continue;
20830
20831 /*
20832 * Don't clone internal triggers, because the constraint cloning code
20833 * will.
20834 */
20835 if (trigForm->tgisinternal)
20836 continue;
20837
20838 /*
20839 * Complain if we find an unexpected trigger type.
20840 */
20841 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20842 !TRIGGER_FOR_AFTER(trigForm->tgtype))
20843 elog(ERROR, "unexpected trigger \"%s\" found",
20844 NameStr(trigForm->tgname));
20845
20846 /* Use short-lived context for CREATE TRIGGER */
20847 oldcxt = MemoryContextSwitchTo(perTupCxt);
20848
20849 /*
20850 * If there is a WHEN clause, generate a 'cooked' version of it that's
20851 * appropriate for the partition.
20852 */
20853 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20854 RelationGetDescr(pg_trigger), &isnull);
20855 if (!isnull)
20856 {
20858 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20859 partition, parent);
20860 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20861 partition, parent);
20862 }
20863
20864 /*
20865 * If there is a column list, transform it to a list of column names.
20866 * Note we don't need to map this list in any way ...
20867 */
20868 if (trigForm->tgattr.dim1 > 0)
20869 {
20870 int i;
20871
20872 for (i = 0; i < trigForm->tgattr.dim1; i++)
20873 {
20875
20876 col = TupleDescAttr(parent->rd_att,
20877 trigForm->tgattr.values[i] - 1);
20878 cols = lappend(cols,
20879 makeString(pstrdup(NameStr(col->attname))));
20880 }
20881 }
20882
20883 /* Reconstruct trigger arguments list. */
20884 if (trigForm->tgnargs > 0)
20885 {
20886 char *p;
20887
20888 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20889 RelationGetDescr(pg_trigger), &isnull);
20890 if (isnull)
20891 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20892 NameStr(trigForm->tgname), RelationGetRelationName(partition));
20893
20894 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20895
20896 for (int i = 0; i < trigForm->tgnargs; i++)
20897 {
20898 trigargs = lappend(trigargs, makeString(pstrdup(p)));
20899 p += strlen(p) + 1;
20900 }
20901 }
20902
20903 trigStmt = makeNode(CreateTrigStmt);
20904 trigStmt->replace = false;
20905 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20906 trigStmt->trigname = NameStr(trigForm->tgname);
20907 trigStmt->relation = NULL;
20908 trigStmt->funcname = NULL; /* passed separately */
20909 trigStmt->args = trigargs;
20910 trigStmt->row = true;
20911 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20912 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20913 trigStmt->columns = cols;
20914 trigStmt->whenClause = NULL; /* passed separately */
20915 trigStmt->transitionRels = NIL; /* not supported at present */
20916 trigStmt->deferrable = trigForm->tgdeferrable;
20917 trigStmt->initdeferred = trigForm->tginitdeferred;
20918 trigStmt->constrrel = NULL; /* passed separately */
20919
20920 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20921 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20922 trigForm->tgfoid, trigForm->oid, qual,
20923 false, true, trigForm->tgenabled);
20924
20925 MemoryContextSwitchTo(oldcxt);
20926 MemoryContextReset(perTupCxt);
20927 }
20928
20929 MemoryContextDelete(perTupCxt);
20930
20931 systable_endscan(scan);
20932 table_close(pg_trigger, RowExclusiveLock);
20933}
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define DatumGetByteaPP(X)
Definition: fmgr.h:291
static struct @171 value
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:400
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
#define PRS2_OLD_VARNO
Definition: primnodes.h:250
#define PRS2_NEW_VARNO
Definition: primnodes.h:251
void * stringToNode(const char *str)
Definition: read.c:90
Node * whenClause
Definition: parsenodes.h:3149
List * transitionRels
Definition: parsenodes.h:3151
RangeVar * constrrel
Definition: parsenodes.h:3155
RangeVar * relation
Definition: parsenodes.h:3140
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 attachPartitionTable(), 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 19811 of file tablecmds.c.

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

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

Referenced by DefineRelation().

◆ ConstraintImpliedByRelConstraint()

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

Definition at line 20140 of file tablecmds.c.

20141{
20142 List *existConstraint = list_copy(provenConstraint);
20143 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20144 int num_check,
20145 i;
20146
20147 num_check = (constr != NULL) ? constr->num_check : 0;
20148 for (i = 0; i < num_check; i++)
20149 {
20150 Node *cexpr;
20151
20152 /*
20153 * If this constraint hasn't been fully validated yet, we must ignore
20154 * it here.
20155 */
20156 if (!constr->check[i].ccvalid)
20157 continue;
20158
20159 /*
20160 * NOT ENFORCED constraints are always marked as invalid, which should
20161 * have been ignored.
20162 */
20163 Assert(constr->check[i].ccenforced);
20164
20165 cexpr = stringToNode(constr->check[i].ccbin);
20166
20167 /*
20168 * Run each expression through const-simplification and
20169 * canonicalization. It is necessary, because we will be comparing it
20170 * to similarly-processed partition constraint expressions, and may
20171 * fail to detect valid matches without this.
20172 */
20173 cexpr = eval_const_expressions(NULL, cexpr);
20174 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20175
20176 existConstraint = list_concat(existConstraint,
20177 make_ands_implicit((Expr *) cexpr));
20178 }
20179
20180 /*
20181 * Try to make the proof. Since we are comparing CHECK constraints, we
20182 * need to use weak implication, i.e., we assume existConstraint is
20183 * not-false and try to prove the same for testConstraint.
20184 *
20185 * Note that predicate_implied_by assumes its first argument is known
20186 * immutable. That should always be true for both NOT NULL and partition
20187 * constraints, so we don't test it here.
20188 */
20189 return predicate_implied_by(testConstraint, existConstraint, true);
20190}
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 17497 of file tablecmds.c.

17498{
17501
17502 if (acon->condeferrable != bcon->condeferrable ||
17503 acon->condeferred != bcon->condeferred ||
17504 strcmp(decompile_conbin(a, tupleDesc),
17505 decompile_conbin(b, tupleDesc)) != 0)
17506 return false;
17507 else
17508 return true;
17509}
int b
Definition: isn.c:74
int a
Definition: isn.c:73
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:17469

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

13825{
13826 ObjectAddress trigAddress;
13827 CreateTrigStmt *fk_trigger;
13828
13829 /*
13830 * Note: for a self-referential FK (referencing and referenced tables are
13831 * the same), it is important that the ON UPDATE action fires before the
13832 * CHECK action, since both triggers will fire on the same row during an
13833 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13834 * state of the row. Triggers fire in name order, so we ensure this by
13835 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13836 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13837 */
13838 fk_trigger = makeNode(CreateTrigStmt);
13839 fk_trigger->replace = false;
13840 fk_trigger->isconstraint = true;
13841 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13842 fk_trigger->relation = NULL;
13843
13844 /* Either ON INSERT or ON UPDATE */
13845 if (on_insert)
13846 {
13847 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13848 fk_trigger->events = TRIGGER_TYPE_INSERT;
13849 }
13850 else
13851 {
13852 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13853 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13854 }
13855
13856 fk_trigger->args = NIL;
13857 fk_trigger->row = true;
13858 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13859 fk_trigger->columns = NIL;
13860 fk_trigger->whenClause = NULL;
13861 fk_trigger->transitionRels = NIL;
13862 fk_trigger->deferrable = fkconstraint->deferrable;
13863 fk_trigger->initdeferred = fkconstraint->initdeferred;
13864 fk_trigger->constrrel = NULL;
13865
13866 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13867 constraintOid, indexOid, InvalidOid,
13868 parentTrigOid, NULL, true, false);
13869
13870 /* Make changes-so-far visible */
13872
13873 return trigAddress.objectId;
13874}
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 13885 of file tablecmds.c.

13889{
13890 CreateTrigStmt *fk_trigger;
13891 ObjectAddress trigAddress;
13892
13893 /*
13894 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13895 * DELETE 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_DELETE;
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_del_action)
13912 {
13914 fk_trigger->deferrable = fkconstraint->deferrable;
13915 fk_trigger->initdeferred = fkconstraint->initdeferred;
13916 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13917 break;
13919 fk_trigger->deferrable = false;
13920 fk_trigger->initdeferred = false;
13921 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13922 break;
13924 fk_trigger->deferrable = false;
13925 fk_trigger->initdeferred = false;
13926 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13927 break;
13929 fk_trigger->deferrable = false;
13930 fk_trigger->initdeferred = false;
13931 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13932 break;
13934 fk_trigger->deferrable = false;
13935 fk_trigger->initdeferred = false;
13936 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13937 break;
13938 default:
13939 elog(ERROR, "unrecognized FK action type: %d",
13940 (int) fkconstraint->fk_del_action);
13941 break;
13942 }
13943
13944 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13945 constraintOid, indexOid, InvalidOid,
13946 parentDelTrigger, NULL, true, false);
13947 if (deleteTrigOid)
13948 *deleteTrigOid = trigAddress.objectId;
13949
13950 /* Make changes-so-far visible */
13952
13953 /*
13954 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13955 * UPDATE action on the referenced table.
13956 */
13957 fk_trigger = makeNode(CreateTrigStmt);
13958 fk_trigger->replace = false;
13959 fk_trigger->isconstraint = true;
13960 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13961 fk_trigger->relation = NULL;
13962 fk_trigger->args = NIL;
13963 fk_trigger->row = true;
13964 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13965 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13966 fk_trigger->columns = NIL;
13967 fk_trigger->whenClause = NULL;
13968 fk_trigger->transitionRels = NIL;
13969 fk_trigger->constrrel = NULL;
13970
13971 switch (fkconstraint->fk_upd_action)
13972 {
13974 fk_trigger->deferrable = fkconstraint->deferrable;
13975 fk_trigger->initdeferred = fkconstraint->initdeferred;
13976 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13977 break;
13979 fk_trigger->deferrable = false;
13980 fk_trigger->initdeferred = false;
13981 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13982 break;
13984 fk_trigger->deferrable = false;
13985 fk_trigger->initdeferred = false;
13986 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13987 break;
13989 fk_trigger->deferrable = false;
13990 fk_trigger->initdeferred = false;
13991 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13992 break;
13994 fk_trigger->deferrable = false;
13995 fk_trigger->initdeferred = false;
13996 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13997 break;
13998 default:
13999 elog(ERROR, "unrecognized FK action type: %d",
14000 (int) fkconstraint->fk_upd_action);
14001 break;
14002 }
14003
14004 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
14005 constraintOid, indexOid, InvalidOid,
14006 parentUpdTrigger, NULL, true, false);
14007 if (updateTrigOid)
14008 *updateTrigOid = trigAddress.objectId;
14009}
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2847

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

14025{
14026 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
14027 constraintOid, indexOid,
14028 parentInsTrigger, true);
14029 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
14030 constraintOid, indexOid,
14031 parentUpdTrigger, false);
14032}
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:13822

References CreateFKCheckTrigger().

Referenced by addFkRecurseReferencing(), and ATExecAlterConstrEnforceability().

◆ CreateInheritance()

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

Definition at line 17400 of file tablecmds.c.

17401{
17402 Relation catalogRelation;
17403 SysScanDesc scan;
17405 HeapTuple inheritsTuple;
17406 int32 inhseqno;
17407
17408 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17409 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17410
17411 /*
17412 * Check for duplicates in the list of parents, and determine the highest
17413 * inhseqno already present; we'll use the next one for the new parent.
17414 * Also, if proposed child is a partition, it cannot already be
17415 * inheriting.
17416 *
17417 * Note: we do not reject the case where the child already inherits from
17418 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17419 */
17421 Anum_pg_inherits_inhrelid,
17422 BTEqualStrategyNumber, F_OIDEQ,
17424 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17425 true, NULL, 1, &key);
17426
17427 /* inhseqno sequences start at 1 */
17428 inhseqno = 0;
17429 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17430 {
17431 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17432
17433 if (inh->inhparent == RelationGetRelid(parent_rel))
17434 ereport(ERROR,
17435 (errcode(ERRCODE_DUPLICATE_TABLE),
17436 errmsg("relation \"%s\" would be inherited from more than once",
17437 RelationGetRelationName(parent_rel))));
17438
17439 if (inh->inhseqno > inhseqno)
17440 inhseqno = inh->inhseqno;
17441 }
17442 systable_endscan(scan);
17443
17444 /* Match up the columns and bump attinhcount as needed */
17445 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17446
17447 /* Match up the constraints and bump coninhcount as needed */
17448 MergeConstraintsIntoExisting(child_rel, parent_rel);
17449
17450 /*
17451 * OK, it looks valid. Make the catalog entries that show inheritance.
17452 */
17454 RelationGetRelid(parent_rel),
17455 inhseqno + 1,
17456 catalogRelation,
17457 parent_rel->rd_rel->relkind ==
17458 RELKIND_PARTITIONED_TABLE);
17459
17460 /* Now we're done with pg_inherits */
17461 table_close(catalogRelation, RowExclusiveLock);
17462}
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:17526
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3562
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17664

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

◆ createPartitionTable()

static Relation createPartitionTable ( List **  wqueue,
RangeVar newPartName,
Relation  parent_rel,
Oid  ownerId 
)
static

Definition at line 22455 of file tablecmds.c.

22457{
22458 Relation newRel;
22459 Oid newRelId;
22460 Oid existingRelid;
22462 List *colList = NIL;
22463 Oid relamId;
22464 Oid namespaceId;
22465 AlteredTableInfo *new_partrel_tab;
22466 Form_pg_class parent_relform = parent_rel->rd_rel;
22467
22468 /* If the existing rel is temp, it must belong to this session. */
22469 if (RELATION_IS_OTHER_TEMP(parent_rel))
22470 ereport(ERROR,
22471 errcode(ERRCODE_WRONG_OBJECT_TYPE),
22472 errmsg("cannot create as partition of temporary relation of another session"));
22473
22474 /* Look up inheritance ancestors and generate the relation schema. */
22475 colList = getAttributesList(parent_rel);
22476
22477 /* Create a tuple descriptor from the relation schema. */
22479
22480 /* Look up the access method for the new relation. */
22481 relamId = (parent_relform->relam != InvalidOid) ? parent_relform->relam : HEAP_TABLE_AM_OID;
22482
22483 /* Look up the namespace in which we are supposed to create the relation. */
22484 namespaceId =
22485 RangeVarGetAndCheckCreationNamespace(newPartName, NoLock, &existingRelid);
22486 if (OidIsValid(existingRelid))
22487 ereport(ERROR,
22488 errcode(ERRCODE_DUPLICATE_TABLE),
22489 errmsg("relation \"%s\" already exists", newPartName->relname));
22490
22491 /*
22492 * We intended to create the partition with the same persistence as the
22493 * parent table, but we still need to recheck because that might be
22494 * affected by the search_path. If the parent is permanent, so must be
22495 * all of its partitions.
22496 */
22497 if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
22498 newPartName->relpersistence == RELPERSISTENCE_TEMP)
22499 ereport(ERROR,
22500 errcode(ERRCODE_WRONG_OBJECT_TYPE),
22501 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
22502 RelationGetRelationName(parent_rel)));
22503
22504 /* Permanent rels cannot be partitions belonging to a temporary parent. */
22505 if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
22506 parent_relform->relpersistence == RELPERSISTENCE_TEMP)
22507 ereport(ERROR,
22508 errcode(ERRCODE_WRONG_OBJECT_TYPE),
22509 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
22510 RelationGetRelationName(parent_rel)));
22511
22512 /* Create the relation. */
22513 newRelId = heap_create_with_catalog(newPartName->relname,
22514 namespaceId,
22515 parent_relform->reltablespace,
22516 InvalidOid,
22517 InvalidOid,
22518 InvalidOid,
22519 ownerId,
22520 relamId,
22521 descriptor,
22522 NIL,
22523 RELKIND_RELATION,
22524 newPartName->relpersistence,
22525 false,
22526 false,
22528 (Datum) 0,
22529 true,
22531 true,
22532 InvalidOid,
22533 NULL);
22534
22535 /*
22536 * We must bump the command counter to make the newly-created relation
22537 * tuple visible for opening.
22538 */
22540
22541 /*
22542 * Open the new partition with no lock, because we already have an
22543 * AccessExclusiveLock placed there after creation.
22544 */
22545 newRel = table_open(newRelId, NoLock);
22546
22547 /* Find or create a work queue entry for the newly created table. */
22548 new_partrel_tab = ATGetQueueEntry(wqueue, newRel);
22549
22550 /* Create constraints, default values, and generated values. */
22551 createTableConstraints(wqueue, new_partrel_tab, parent_rel, newRel);
22552
22553 /*
22554 * Need to call CommandCounterIncrement, so a fresh relcache entry has
22555 * newly installed constraint info.
22556 */
22558
22559 return newRel;
22560}
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
@ ONCOMMIT_NOOP
Definition: primnodes.h:58
char relpersistence
Definition: primnodes.h:89
static void createTableConstraints(List **wqueue, AlteredTableInfo *tab, Relation parent_rel, Relation newRel)
Definition: tablecmds.c:22248
static List * getAttributesList(Relation parent_rel)
Definition: tablecmds.c:22195

References allowSystemTableMods, ATGetQueueEntry(), BuildDescForRelation(), CommandCounterIncrement(), createTableConstraints(), ereport, errcode(), errmsg(), ERROR, getAttributesList(), heap_create_with_catalog(), InvalidOid, NIL, NoLock, OidIsValid, ONCOMMIT_NOOP, RangeVarGetAndCheckCreationNamespace(), RelationData::rd_rel, RELATION_IS_OTHER_TEMP, RelationGetRelationName, RangeVar::relname, RangeVar::relpersistence, and table_open().

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ createSplitPartitionContext()

static SplitPartitionContext * createSplitPartitionContext ( Relation  partRel)
static

Definition at line 22943 of file tablecmds.c.

22944{
22946
22948 pc->partRel = partRel;
22949
22950 /*
22951 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
22952 * don't bother using it.
22953 */
22955
22956 /* Create a destination tuple slot for the new partition. */
22957 pc->dstslot = table_slot_create(pc->partRel, NULL);
22958
22959 return pc;
22960}
TupleTableSlot * dstslot
Definition: tablecmds.c:22933
BulkInsertState bistate
Definition: tablecmds.c:22932
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:92

References SplitPartitionContext::bistate, SplitPartitionContext::dstslot, GetBulkInsertState(), palloc0_object, SplitPartitionContext::partRel, and table_slot_create().

Referenced by SplitPartitionMoveRows().

◆ createTableConstraints()

static void createTableConstraints ( List **  wqueue,
AlteredTableInfo tab,
Relation  parent_rel,
Relation  newRel 
)
static

Definition at line 22248 of file tablecmds.c.

22250{
22251 TupleDesc tupleDesc;
22252 TupleConstr *constr;
22253 AttrMap *attmap;
22254 AttrNumber parent_attno;
22255 int ccnum;
22256 List *constraints = NIL;
22257 List *cookedConstraints = NIL;
22258
22259 tupleDesc = RelationGetDescr(parent_rel);
22260 constr = tupleDesc->constr;
22261
22262 if (!constr)
22263 return;
22264
22265 /*
22266 * Construct a map from the parent relation's attnos to the child rel's.
22267 * This re-checks type match, etc, although it shouldn't be possible to
22268 * have a failure since both tables are locked.
22269 */
22270 attmap = build_attrmap_by_name(RelationGetDescr(newRel),
22271 tupleDesc,
22272 false);
22273
22274 /* Cycle for default values. */
22275 for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
22276 {
22277 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
22278 parent_attno - 1);
22279
22280 /* Ignore dropped columns in the parent. */
22281 if (attribute->attisdropped)
22282 continue;
22283
22284 /* Copy the default, if present, and it should be copied. */
22285 if (attribute->atthasdef)
22286 {
22287 Node *this_default = NULL;
22288 bool found_whole_row;
22289 AttrNumber num;
22290 Node *def;
22292
22293 if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
22294 this_default = build_generation_expression(parent_rel, attribute->attnum);
22295 else
22296 {
22297 this_default = TupleDescGetDefault(tupleDesc, attribute->attnum);
22298 if (this_default == NULL)
22299 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
22300 attribute->attnum, RelationGetRelationName(parent_rel));
22301 }
22302
22303 num = attmap->attnums[parent_attno - 1];
22304 def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
22305
22306 if (found_whole_row && attribute->attgenerated != '\0')
22307 elog(ERROR, "cannot convert whole-row table reference");
22308
22309 /* Add a pre-cooked default expression. */
22310 StoreAttrDefault(newRel, num, def, true);
22311
22312 /*
22313 * Stored generated column expressions in parent_rel might
22314 * reference the tableoid. newRel, parent_rel tableoid clear is
22315 * not the same. If so, these stored generated columns require
22316 * recomputation for newRel within MergePartitionsMoveRows.
22317 */
22318 if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
22319 {
22321 newval->attnum = num;
22322 newval->expr = expression_planner((Expr *) def);
22323 newval->is_generated = (attribute->attgenerated != '\0');
22324 tab->newvals = lappend(tab->newvals, newval);
22325 }
22326 }
22327 }
22328
22329 /* Cycle for CHECK constraints. */
22330 for (ccnum = 0; ccnum < constr->num_check; ccnum++)
22331 {
22332 char *ccname = constr->check[ccnum].ccname;
22333 char *ccbin = constr->check[ccnum].ccbin;
22334 bool ccenforced = constr->check[ccnum].ccenforced;
22335 bool ccnoinherit = constr->check[ccnum].ccnoinherit;
22336 bool ccvalid = constr->check[ccnum].ccvalid;
22337 Node *ccbin_node;
22338 bool found_whole_row;
22339 Constraint *constr;
22340
22341 /*
22342 * The partitioned table can not have a NO INHERIT check constraint
22343 * (see StoreRelCheck function for details).
22344 */
22345 Assert(!ccnoinherit);
22346
22347 ccbin_node = map_variable_attnos(stringToNode(ccbin),
22348 1, 0,
22349 attmap,
22350 InvalidOid, &found_whole_row);
22351
22352 /*
22353 * For the moment we have to reject whole-row variables (as for CREATE
22354 * TABLE LIKE and inheritances).
22355 */
22356 if (found_whole_row)
22357 elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
22358 ccname,
22359 RelationGetRelationName(parent_rel));
22360
22361 constr = makeNode(Constraint);
22362 constr->contype = CONSTR_CHECK;
22363 constr->conname = pstrdup(ccname);
22364 constr->deferrable = false;
22365 constr->initdeferred = false;
22366 constr->is_enforced = ccenforced;
22367 constr->skip_validation = !ccvalid;
22368 constr->initially_valid = ccvalid;
22369 constr->is_no_inherit = ccnoinherit;
22370 constr->raw_expr = NULL;
22371 constr->cooked_expr = nodeToString(ccbin_node);
22372 constr->location = -1;
22373 constraints = lappend(constraints, constr);
22374 }
22375
22376 /* Install all CHECK constraints. */
22377 cookedConstraints = AddRelationNewConstraints(newRel, NIL, constraints,
22378 false, true, true, NULL);
22379
22380 /* Make the additional catalog changes visible. */
22382
22383 /*
22384 * parent_rel check constraint expression may reference tableoid, so later
22385 * in MergePartitionsMoveRows, we need to evaluate the check constraint
22386 * again for the newRel. We can check whether the check constraint
22387 * contains a tableoid reference via pull_varattnos.
22388 */
22389 foreach_ptr(CookedConstraint, ccon, cookedConstraints)
22390 {
22391 if (!ccon->skip_validation)
22392 {
22393 Node *qual;
22394 Bitmapset *attnums = NULL;
22395
22396 Assert(ccon->contype == CONSTR_CHECK);
22397 qual = expand_generated_columns_in_expr(ccon->expr, newRel, 1);
22398 pull_varattnos(qual, 1, &attnums);
22399
22400 /*
22401 * Add a check only if it contains a tableoid
22402 * (TableOidAttributeNumber).
22403 */
22405 attnums))
22406 {
22407 NewConstraint *newcon;
22408
22409 newcon = palloc0_object(NewConstraint);
22410 newcon->name = ccon->name;
22411 newcon->contype = CONSTR_CHECK;
22412 newcon->qual = qual;
22413
22414 tab->constraints = lappend(tab->constraints, newcon);
22415 }
22416 }
22417 }
22418
22419 /* Don't need the cookedConstraints anymore. */
22420 list_free_deep(cookedConstraints);
22421
22422 /* Reproduce not-null constraints. */
22423 if (constr->has_not_null)
22424 {
22425 List *nnconstraints;
22426
22427 /*
22428 * The "include_noinh" argument is false because a partitioned table
22429 * can't have NO INHERIT constraint.
22430 */
22431 nnconstraints = RelationGetNotNullConstraints(RelationGetRelid(parent_rel),
22432 false, false);
22433
22434 Assert(list_length(nnconstraints) > 0);
22435
22436 /*
22437 * We already set pg_attribute.attnotnull in createPartitionTable. No
22438 * need call set_attnotnull again.
22439 */
22440 AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, true, NULL);
22441 }
22442}
void list_free_deep(List *list)
Definition: list.c:1560
char * nodeToString(const void *obj)
Definition: outfuncs.c:802
List * RelationGetNotNullConstraints(Oid relid, bool cooked, bool include_noinh)
Node * build_generation_expression(Relation rel, int attrno)
char * ccname
Definition: tupdesc.h:30
bool ccnoinherit
Definition: tupdesc.h:34
char * cooked_expr
Definition: parsenodes.h:2871
Node * raw_expr
Definition: parsenodes.h:2869
#define TableOidAttributeNumber
Definition: sysattr.h:26
Node * TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
Definition: tupdesc.c:1092

References AddRelationNewConstraints(), Assert(), AttrMap::attnums, bms_is_member(), build_attrmap_by_name(), build_generation_expression(), ConstrCheck::ccbin, ConstrCheck::ccenforced, ConstrCheck::ccname, ConstrCheck::ccnoinherit, ConstrCheck::ccvalid, TupleConstr::check, CommandCounterIncrement(), Constraint::conname, TupleDescData::constr, CONSTR_CHECK, AlteredTableInfo::constraints, NewConstraint::contype, Constraint::contype, Constraint::cooked_expr, Constraint::deferrable, elog, ERROR, expand_generated_columns_in_expr(), expression_planner(), FirstLowInvalidHeapAttributeNumber, foreach_ptr, TupleConstr::has_not_null, Constraint::initdeferred, Constraint::initially_valid, InvalidOid, Constraint::is_enforced, Constraint::is_no_inherit, lappend(), list_free_deep(), list_length(), Constraint::location, makeNode, map_variable_attnos(), NewConstraint::name, TupleDescData::natts, newval, AlteredTableInfo::newvals, NIL, nodeToString(), TupleConstr::num_check, palloc0_object, pstrdup(), pull_varattnos(), NewConstraint::qual, Constraint::raw_expr, RelationGetDescr, RelationGetNotNullConstraints(), RelationGetRelationName, RelationGetRelid, Constraint::skip_validation, StoreAttrDefault(), stringToNode(), TableOidAttributeNumber, TupleDescAttr(), and TupleDescGetDefault().

Referenced by createPartitionTable().

◆ decompile_conbin()

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

Definition at line 17469 of file tablecmds.c.

17470{
17472 bool isnull;
17473 Datum attr;
17474 Datum expr;
17475
17476 con = (Form_pg_constraint) GETSTRUCT(contup);
17477 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17478 if (isnull)
17479 elog(ERROR, "null conbin for constraint %u", con->oid);
17480
17481 expr = DirectFunctionCall2(pg_get_expr, attr,
17482 ObjectIdGetDatum(con->conrelid));
17483 return TextDatumGetCString(expr);
17484}
#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 769 of file tablecmds.c.

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

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

◆ deleteSplitPartitionContext()

static void deleteSplitPartitionContext ( SplitPartitionContext pc,
List **  wqueue,
int  ti_options 
)
static

Definition at line 22966 of file tablecmds.c.

22967{
22968 ListCell *ltab;
22969
22972
22973 table_finish_bulk_insert(pc->partRel, ti_options);
22974
22975 /*
22976 * We don't need to process this pc->partRel so delete the ALTER TABLE
22977 * queue of it.
22978 */
22979 foreach(ltab, *wqueue)
22980 {
22981 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
22982
22983 if (tab->relid == RelationGetRelid(pc->partRel))
22984 {
22985 *wqueue = list_delete_cell(*wqueue, ltab);
22986 break;
22987 }
22988 }
22989
22990 pfree(pc);
22991}
List * list_delete_cell(List *list, ListCell *cell)
Definition: list.c:841

References SplitPartitionContext::bistate, SplitPartitionContext::dstslot, ExecDropSingleTupleTableSlot(), FreeBulkInsertState(), lfirst, list_delete_cell(), SplitPartitionContext::partRel, pfree(), RelationGetRelid, AlteredTableInfo::relid, and table_finish_bulk_insert().

Referenced by SplitPartitionMoveRows().

◆ DetachPartitionFinalize()

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

Definition at line 21127 of file tablecmds.c.

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

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

◆ detachPartitionTable()

static void detachPartitionTable ( Relation  parent_rel,
Relation  child_rel,
Oid  defaultPartOid 
)
static

Definition at line 22722 of file tablecmds.c.

22723{
22724 /* Remove the pg_inherits row first. */
22725 RemoveInheritance(child_rel, parent_rel, false);
22726
22727 /*
22728 * Detaching the partition might involve TOAST table access, so ensure we
22729 * have a valid snapshot.
22730 */
22732
22733 /* Do the final part of detaching. */
22734 DetachPartitionFinalize(parent_rel, child_rel, false, defaultPartOid);
22735
22737}

References DetachPartitionFinalize(), GetTransactionSnapshot(), PopActiveSnapshot(), PushActiveSnapshot(), and RemoveInheritance().

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ drop_parent_dependency()

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

Definition at line 18190 of file tablecmds.c.

18192{
18193 Relation catalogRelation;
18194 SysScanDesc scan;
18195 ScanKeyData key[3];
18196 HeapTuple depTuple;
18197
18198 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18199
18200 ScanKeyInit(&key[0],
18201 Anum_pg_depend_classid,
18202 BTEqualStrategyNumber, F_OIDEQ,
18203 ObjectIdGetDatum(RelationRelationId));
18204 ScanKeyInit(&key[1],
18205 Anum_pg_depend_objid,
18206 BTEqualStrategyNumber, F_OIDEQ,
18207 ObjectIdGetDatum(relid));
18208 ScanKeyInit(&key[2],
18209 Anum_pg_depend_objsubid,
18210 BTEqualStrategyNumber, F_INT4EQ,
18211 Int32GetDatum(0));
18212
18213 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18214 NULL, 3, key);
18215
18216 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18217 {
18218 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18219
18220 if (dep->refclassid == refclassid &&
18221 dep->refobjid == refobjid &&
18222 dep->refobjsubid == 0 &&
18223 dep->deptype == deptype)
18224 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18225 }
18226
18227 systable_endscan(scan);
18228 table_close(catalogRelation, RowExclusiveLock);
18229}

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

21496{
21497 ScanKeyData skey;
21498 SysScanDesc scan;
21499 HeapTuple trigtup;
21500 Relation tgrel;
21501 ObjectAddresses *objects;
21502
21503 objects = new_object_addresses();
21504
21505 /*
21506 * Scan pg_trigger to search for all triggers on this rel.
21507 */
21508 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21509 F_OIDEQ, ObjectIdGetDatum(partitionId));
21510 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21511 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21512 true, NULL, 1, &skey);
21513 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21514 {
21515 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21516 ObjectAddress trig;
21517
21518 /* Ignore triggers that weren't cloned */
21519 if (!OidIsValid(pg_trigger->tgparentid))
21520 continue;
21521
21522 /*
21523 * Ignore internal triggers that are implementation objects of foreign
21524 * keys, because these will be detached when the foreign keys
21525 * themselves are.
21526 */
21527 if (OidIsValid(pg_trigger->tgconstrrelid))
21528 continue;
21529
21530 /*
21531 * This is ugly, but necessary: remove the dependency markings on the
21532 * trigger so that it can be removed.
21533 */
21534 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21535 TriggerRelationId,
21537 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21538 RelationRelationId,
21540
21541 /* remember this trigger to remove it below */
21542 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21543 add_exact_object_address(&trig, objects);
21544 }
21545
21546 /* make the dependency removal visible to the deletion below */
21549
21550 /* done */
21551 free_object_addresses(objects);
21552 systable_endscan(scan);
21554}

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

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

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

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

◆ DropErrorMsgNonExistent()

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

Definition at line 1458 of file tablecmds.c.

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

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

12033{
12035 SysScanDesc scan;
12036 HeapTuple trigtup;
12037
12039 Anum_pg_trigger_tgconstraint,
12040 BTEqualStrategyNumber, F_OIDEQ,
12041 ObjectIdGetDatum(conoid));
12042 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12043 NULL, 1, &key);
12044 while ((trigtup = systable_getnext(scan)) != NULL)
12045 {
12046 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12047 ObjectAddress trigger;
12048
12049 /* Invalid if trigger is not for a referential integrity constraint */
12050 if (!OidIsValid(trgform->tgconstrrelid))
12051 continue;
12052 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12053 continue;
12054 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12055 continue;
12056
12057 /* We should be dropping trigger related to foreign key constraint */
12058 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12059 trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12060 trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12061 trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12062 trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12063 trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12064 trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12065 trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12066 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12067 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12068 trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12069 trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12070
12071 /*
12072 * The constraint is originally set up to contain this trigger as an
12073 * implementation object, so there's a dependency record that links
12074 * the two; however, since the trigger is no longer needed, we remove
12075 * the dependency link in order to be able to drop the trigger while
12076 * keeping the constraint intact.
12077 */
12078 deleteDependencyRecordsFor(TriggerRelationId,
12079 trgform->oid,
12080 false);
12081 /* make dependency deletion visible to performDeletion */
12083 ObjectAddressSet(trigger, TriggerRelationId,
12084 trgform->oid);
12085 performDeletion(&trigger, DROP_RESTRICT, 0);
12086 /* make trigger drop visible, in case the loop iterates */
12088 }
12089
12090 systable_endscan(scan);
12091}

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

◆ evaluateGeneratedExpressionsAndCheckConstraints()

static void evaluateGeneratedExpressionsAndCheckConstraints ( AlteredTableInfo tab,
Relation  newPartRel,
TupleTableSlot insertslot,
ExprContext econtext 
)
static

Definition at line 22150 of file tablecmds.c.

22154{
22155 econtext->ecxt_scantuple = insertslot;
22156
22158 {
22159 if (!ex->is_generated)
22160 continue;
22161
22162 insertslot->tts_values[ex->attnum - 1]
22163 = ExecEvalExpr(ex->exprstate,
22164 econtext,
22165 &insertslot->tts_isnull[ex->attnum - 1]);
22166 }
22167
22169 {
22170 switch (con->contype)
22171 {
22172 case CONSTR_CHECK:
22173 if (!ExecCheck(con->qualstate, econtext))
22174 ereport(ERROR,
22175 errcode(ERRCODE_CHECK_VIOLATION),
22176 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
22177 con->name, RelationGetRelationName(newPartRel)),
22178 errtableconstraint(newPartRel, con->name));
22179 break;
22180 case CONSTR_NOTNULL:
22181 case CONSTR_FOREIGN:
22182 /* Nothing to do here */
22183 break;
22184 default:
22185 elog(ERROR, "unrecognized constraint type: %d",
22186 (int) con->contype);
22187 }
22188 }
22189}

References CONSTR_CHECK, CONSTR_FOREIGN, CONSTR_NOTNULL, AlteredTableInfo::constraints, ExprContext::ecxt_scantuple, elog, ereport, errcode(), errmsg(), ERROR, errtableconstraint(), ExecCheck(), ExecEvalExpr(), foreach_ptr, AlteredTableInfo::newvals, RelationGetRelationName, TupleTableSlot::tts_isnull, and TupleTableSlot::tts_values.

Referenced by MergePartitionsMoveRows(), and SplitPartitionMoveRows().

◆ ExecuteTruncate()

void ExecuteTruncate ( TruncateStmt stmt)

Definition at line 1856 of file tablecmds.c.

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

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

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

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

Referenced by apply_handle_truncate(), and ExecuteTruncate().

◆ find_composite_type_dependencies()

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

Definition at line 6964 of file tablecmds.c.

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

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

7123{
7124 Relation classRel;
7125 ScanKeyData key[1];
7126 TableScanDesc scan;
7127 HeapTuple tuple;
7128 List *result = NIL;
7129
7130 classRel = table_open(RelationRelationId, AccessShareLock);
7131
7132 ScanKeyInit(&key[0],
7133 Anum_pg_class_reloftype,
7134 BTEqualStrategyNumber, F_OIDEQ,
7135 ObjectIdGetDatum(typeOid));
7136
7137 scan = table_beginscan_catalog(classRel, 1, key);
7138
7139 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7140 {
7141 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7142
7143 if (behavior == DROP_RESTRICT)
7144 ereport(ERROR,
7145 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7146 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7147 typeName),
7148 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7149 else
7150 result = lappend_oid(result, classform->oid);
7151 }
7152
7153 table_endscan(scan);
7154 table_close(classRel, AccessShareLock);
7155
7156 return result;
7157}

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

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

References i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 13664 of file tablecmds.c.

13665{
13666 CoercionPathType ret;
13667
13668 if (targetTypeId == sourceTypeId)
13669 {
13671 *funcid = InvalidOid;
13672 }
13673 else
13674 {
13675 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13676 COERCION_IMPLICIT, funcid);
13677 if (ret == COERCION_PATH_NONE)
13678 /* A previously-relied-upon cast is now gone. */
13679 elog(ERROR, "could not find cast from %u to %u",
13680 sourceTypeId, targetTypeId);
13681 }
13682
13683 return ret;
13684}
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 22034 of file tablecmds.c.

22035{
22036 char cmethod;
22037
22038 if (compression == NULL || strcmp(compression, "default") == 0)
22040
22041 /*
22042 * To specify a nondefault method, the column data type must be toastable.
22043 * Note this says nothing about whether the column's attstorage setting
22044 * permits compression; we intentionally allow attstorage and
22045 * attcompression to be independent. But with a non-toastable type,
22046 * attstorage could not be set to a value that would permit compression.
22047 *
22048 * We don't actually need to enforce this, since nothing bad would happen
22049 * if attcompression were non-default; it would never be consulted. But
22050 * it seems more user-friendly to complain about a certainly-useless
22051 * attempt to set the property.
22052 */
22053 if (!TypeIsToastable(atttypid))
22054 ereport(ERROR,
22055 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22056 errmsg("column data type %s does not support compression",
22057 format_type_be(atttypid))));
22058
22059 cmethod = CompressionNameToMethod(compression);
22060 if (!CompressionMethodIsValid(cmethod))
22061 ereport(ERROR,
22062 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22063 errmsg("invalid compression method \"%s\"", compression)));
22064
22065 return cmethod;
22066}
#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().

◆ getAttributesList()

static List * getAttributesList ( Relation  parent_rel)
static

Definition at line 22195 of file tablecmds.c.

22196{
22197 AttrNumber parent_attno;
22198 TupleDesc modelDesc;
22199 List *colList = NIL;
22200
22201 modelDesc = RelationGetDescr(parent_rel);
22202
22203 for (parent_attno = 1; parent_attno <= modelDesc->natts;
22204 parent_attno++)
22205 {
22206 Form_pg_attribute attribute = TupleDescAttr(modelDesc,
22207 parent_attno - 1);
22208 ColumnDef *def;
22209
22210 /* Ignore dropped columns in the parent. */
22211 if (attribute->attisdropped)
22212 continue;
22213
22214 def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
22215 attribute->atttypmod, attribute->attcollation);
22216
22217 def->is_not_null = attribute->attnotnull;
22218
22219 /* Copy identity. */
22220 def->identity = attribute->attidentity;
22221
22222 /* Copy attgenerated. */
22223 def->generated = attribute->attgenerated;
22224
22225 def->storage = attribute->attstorage;
22226
22227 /* Likewise, copy compression. */
22228 if (CompressionMethodIsValid(attribute->attcompression))
22229 def->compression =
22230 pstrdup(GetCompressionMethodName(attribute->attcompression));
22231 else
22232 def->compression = NULL;
22233
22234 /* Add to column list. */
22235 colList = lappend(colList, def);
22236 }
22237
22238 return colList;
22239}
ColumnDef * makeColumnDef(const char *colname, Oid typeOid, int32 typmod, Oid collOid)
Definition: makefuncs.c:565
const char * GetCompressionMethodName(char method)

References ColumnDef::compression, CompressionMethodIsValid, ColumnDef::generated, GetCompressionMethodName(), ColumnDef::identity, ColumnDef::is_not_null, lappend(), makeColumnDef(), NameStr, TupleDescData::natts, NIL, pstrdup(), RelationGetDescr, ColumnDef::storage, and TupleDescAttr().

Referenced by createPartitionTable().

◆ GetAttributeStorage()

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

Definition at line 22072 of file tablecmds.c.

22073{
22074 char cstorage = 0;
22075
22076 if (pg_strcasecmp(storagemode, "plain") == 0)
22077 cstorage = TYPSTORAGE_PLAIN;
22078 else if (pg_strcasecmp(storagemode, "external") == 0)
22079 cstorage = TYPSTORAGE_EXTERNAL;
22080 else if (pg_strcasecmp(storagemode, "extended") == 0)
22081 cstorage = TYPSTORAGE_EXTENDED;
22082 else if (pg_strcasecmp(storagemode, "main") == 0)
22083 cstorage = TYPSTORAGE_MAIN;
22084 else if (pg_strcasecmp(storagemode, "default") == 0)
22085 cstorage = get_typstorage(atttypid);
22086 else
22087 ereport(ERROR,
22088 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
22089 errmsg("invalid storage type \"%s\"",
22090 storagemode)));
22091
22092 /*
22093 * safety check: do not allow toasted storage modes unless column datatype
22094 * is TOAST-aware.
22095 */
22096 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22097 ereport(ERROR,
22098 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
22099 errmsg("column data type %s can only have storage PLAIN",
22100 format_type_be(atttypid))));
22101
22102 return cstorage;
22103}
char get_typstorage(Oid typid)
Definition: lsyscache.c:2584
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:32

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

Referenced by ATExecSetStorage(), and BuildDescForRelation().

◆ GetForeignKeyActionTriggers()

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

Definition at line 12099 of file tablecmds.c.

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

12164{
12166 SysScanDesc scan;
12167 HeapTuple trigtup;
12168
12169 *insertTriggerOid = *updateTriggerOid = InvalidOid;
12171 Anum_pg_trigger_tgconstraint,
12172 BTEqualStrategyNumber, F_OIDEQ,
12173 ObjectIdGetDatum(conoid));
12174
12175 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12176 NULL, 1, &key);
12177 while ((trigtup = systable_getnext(scan)) != NULL)
12178 {
12179 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12180
12181 if (trgform->tgconstrrelid != confrelid)
12182 continue;
12183 if (trgform->tgrelid != conrelid)
12184 continue;
12185 /* Only ever look at "check" triggers on the FK side. */
12186 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12187 continue;
12188 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12189 {
12190 Assert(*insertTriggerOid == InvalidOid);
12191 *insertTriggerOid = trgform->oid;
12192 }
12193 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12194 {
12195 Assert(*updateTriggerOid == InvalidOid);
12196 *updateTriggerOid = trgform->oid;
12197 }
12198#ifndef USE_ASSERT_CHECKING
12199 /* In an assert-enabled build, continue looking to find duplicates. */
12200 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12201 break;
12202#endif
12203 }
12204
12205 if (!OidIsValid(*insertTriggerOid))
12206 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12207 conoid);
12208 if (!OidIsValid(*updateTriggerOid))
12209 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12210 conoid);
12211
12212 systable_endscan(scan);
12213}
#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 21932 of file tablecmds.c.

21933{
21934 Relation pg_constraint;
21935 HeapTuple tuple;
21936 SysScanDesc scan;
21937 ScanKeyData key[2];
21938 List *constraints = NIL;
21939
21940 /*
21941 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21942 * scan.
21943 */
21944 if (RelationGetIndexList(partition) == NIL ||
21947 return NIL;
21948
21949 /* Search for constraints referencing this table */
21950 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21951 ScanKeyInit(&key[0],
21952 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21953 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21954 ScanKeyInit(&key[1],
21955 Anum_pg_constraint_contype, BTEqualStrategyNumber,
21956 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21957
21958 /* XXX This is a seqscan, as we don't have a usable index */
21959 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21960 while ((tuple = systable_getnext(scan)) != NULL)
21961 {
21962 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21963
21964 /*
21965 * We only need to process constraints that are part of larger ones.
21966 */
21967 if (!OidIsValid(constrForm->conparentid))
21968 continue;
21969
21970 constraints = lappend_oid(constraints, constrForm->oid);
21971 }
21972
21973 systable_endscan(scan);
21974 table_close(pg_constraint, AccessShareLock);
21975
21976 return constraints;
21977}
#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 17175 of file tablecmds.c.

17176{
17177 SMgrRelation dstrel;
17178
17179 /*
17180 * Since we copy the file directly without looking at the shared buffers,
17181 * we'd better first flush out any pages of the source relation that are
17182 * in shared buffers. We assume no new changes will be made while we are
17183 * holding exclusive lock on the rel.
17184 */
17186
17187 /*
17188 * Create and copy all forks of the relation, and schedule unlinking of
17189 * old physical files.
17190 *
17191 * NOTE: any conflict in relfilenumber value will be caught in
17192 * RelationCreateStorage().
17193 */
17194 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17195
17196 /* copy main fork */
17198 rel->rd_rel->relpersistence);
17199
17200 /* copy those extra forks that exist */
17201 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17202 forkNum <= MAX_FORKNUM; forkNum++)
17203 {
17204 if (smgrexists(RelationGetSmgr(rel), forkNum))
17205 {
17206 smgrcreate(dstrel, forkNum, false);
17207
17208 /*
17209 * WAL log creation if the relation is persistent, or this is the
17210 * init fork of an unlogged relation.
17211 */
17212 if (RelationIsPermanent(rel) ||
17213 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17214 forkNum == INIT_FORKNUM))
17215 log_smgrcreate(&newrlocator, forkNum);
17216 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17217 rel->rd_rel->relpersistence);
17218 }
17219 }
17220
17221 /* drop old relation, and close new one */
17223 smgrclose(dstrel);
17224}
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:4942
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:577
ForkNumber
Definition: relpath.h:56
@ MAIN_FORKNUM
Definition: relpath.h:58
@ INIT_FORKNUM
Definition: relpath.h:61
#define MAX_FORKNUM
Definition: relpath.h:70
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition: smgr.c:481
void smgrclose(SMgrRelation reln)
Definition: smgr.c:374
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:462
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition: storage.c:478
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition: storage.c:122
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition: storage.c:187
void RelationDropStorage(Relation rel)
Definition: storage.c:207

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

Referenced by ATExecSetTableSpace().

◆ MarkInheritDetached()

static void MarkInheritDetached ( Relation  child_rel,
Relation  parent_rel 
)
static

Definition at line 17893 of file tablecmds.c.

17894{
17895 Relation catalogRelation;
17896 SysScanDesc scan;
17898 HeapTuple inheritsTuple;
17899 bool found = false;
17900
17901 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17902
17903 /*
17904 * Find pg_inherits entries by inhparent. (We need to scan them all in
17905 * order to verify that no other partition is pending detach.)
17906 */
17907 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17909 Anum_pg_inherits_inhparent,
17910 BTEqualStrategyNumber, F_OIDEQ,
17911 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17912 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17913 true, NULL, 1, &key);
17914
17915 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17916 {
17917 Form_pg_inherits inhForm;
17918
17919 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17920 if (inhForm->inhdetachpending)
17921 ereport(ERROR,
17922 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17923 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17924 get_rel_name(inhForm->inhrelid),
17925 get_namespace_name(parent_rel->rd_rel->relnamespace),
17926 RelationGetRelationName(parent_rel)),
17927 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17928
17929 if (inhForm->inhrelid == RelationGetRelid(child_rel))
17930 {
17931 HeapTuple newtup;
17932
17933 newtup = heap_copytuple(inheritsTuple);
17934 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17935
17936 CatalogTupleUpdate(catalogRelation,
17937 &inheritsTuple->t_self,
17938 newtup);
17939 found = true;
17940 heap_freetuple(newtup);
17941 /* keep looking, to ensure we catch others pending detach */
17942 }
17943 }
17944
17945 /* Done */
17946 systable_endscan(scan);
17947 table_close(catalogRelation, RowExclusiveLock);
17948
17949 if (!found)
17950 ereport(ERROR,
17952 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17953 RelationGetRelationName(child_rel),
17954 RelationGetRelationName(parent_rel))));
17955}
#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 2544 of file tablecmds.c.

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

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

17527{
17528 Relation attrrel;
17529 TupleDesc parent_desc;
17530
17531 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17532 parent_desc = RelationGetDescr(parent_rel);
17533
17534 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17535 {
17536 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17537 char *parent_attname = NameStr(parent_att->attname);
17538 HeapTuple tuple;
17539
17540 /* Ignore dropped columns in the parent. */
17541 if (parent_att->attisdropped)
17542 continue;
17543
17544 /* Find same column in child (matching on column name). */
17545 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17546 if (HeapTupleIsValid(tuple))
17547 {
17548 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17549
17550 if (parent_att->atttypid != child_att->atttypid ||
17551 parent_att->atttypmod != child_att->atttypmod)
17552 ereport(ERROR,
17553 (errcode(ERRCODE_DATATYPE_MISMATCH),
17554 errmsg("child table \"%s\" has different type for column \"%s\"",
17555 RelationGetRelationName(child_rel), parent_attname)));
17556
17557 if (parent_att->attcollation != child_att->attcollation)
17558 ereport(ERROR,
17559 (errcode(ERRCODE_COLLATION_MISMATCH),
17560 errmsg("child table \"%s\" has different collation for column \"%s\"",
17561 RelationGetRelationName(child_rel), parent_attname)));
17562
17563 /*
17564 * If the parent has a not-null constraint that's not NO INHERIT,
17565 * make sure the child has one too.
17566 *
17567 * Other constraints are checked elsewhere.
17568 */
17569 if (parent_att->attnotnull && !child_att->attnotnull)
17570 {
17571 HeapTuple contup;
17572
17573 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17574 parent_att->attnum);
17575 if (HeapTupleIsValid(contup) &&
17576 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17577 ereport(ERROR,
17578 errcode(ERRCODE_DATATYPE_MISMATCH),
17579 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17580 parent_attname, RelationGetRelationName(child_rel)));
17581 }
17582
17583 /*
17584 * Child column must be generated if and only if parent column is.
17585 */
17586 if (parent_att->attgenerated && !child_att->attgenerated)
17587 ereport(ERROR,
17588 (errcode(ERRCODE_DATATYPE_MISMATCH),
17589 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17590 if (child_att->attgenerated && !parent_att->attgenerated)
17591 ereport(ERROR,
17592 (errcode(ERRCODE_DATATYPE_MISMATCH),
17593 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17594
17595 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17596 ereport(ERROR,
17597 (errcode(ERRCODE_DATATYPE_MISMATCH),
17598 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17599 errdetail("Parent column is %s, child column is %s.",
17600 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17601 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17602
17603 /*
17604 * Regular inheritance children are independent enough not to
17605 * inherit identity columns. But partitions are integral part of
17606 * a partitioned table and inherit identity column.
17607 */
17608 if (ispartition)
17609 child_att->attidentity = parent_att->attidentity;
17610
17611 /*
17612 * OK, bump the child column's inheritance count. (If we fail
17613 * later on, this change will just roll back.)
17614 */
17615 if (pg_add_s16_overflow(child_att->attinhcount, 1,
17616 &child_att->attinhcount))
17617 ereport(ERROR,
17618 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17619 errmsg("too many inheritance parents"));
17620
17621 /*
17622 * In case of partitions, we must enforce that value of attislocal
17623 * is same in all partitions. (Note: there are only inherited
17624 * attributes in partitions)
17625 */
17626 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17627 {
17628 Assert(child_att->attinhcount == 1);
17629 child_att->attislocal = false;
17630 }
17631
17632 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17633 heap_freetuple(tuple);
17634 }
17635 else
17636 {
17637 ereport(ERROR,
17638 (errcode(ERRCODE_DATATYPE_MISMATCH),
17639 errmsg("child table is missing column \"%s\"", parent_attname)));
17640 }
17641 }
17642
17643 table_close(attrrel, RowExclusiveLock);
17644}

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

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

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

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

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

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

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

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

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

◆ MergePartitionsMoveRows()

static void MergePartitionsMoveRows ( List **  wqueue,
List mergingPartitions,
Relation  newPartRel 
)
static

Definition at line 22568 of file tablecmds.c.

22569{
22570 CommandId mycid;
22571 EState *estate;
22572 AlteredTableInfo *tab;
22573 ListCell *ltab;
22574
22575 /* The FSM is empty, so don't bother using it. */
22576 int ti_options = TABLE_INSERT_SKIP_FSM;
22577 BulkInsertState bistate; /* state of bulk inserts for partition */
22578 TupleTableSlot *dstslot;
22579
22580 /* Find the work queue entry for the new partition table: newPartRel. */
22581 tab = ATGetQueueEntry(wqueue, newPartRel);
22582
22583 /* Generate the constraint and default execution states. */
22584 estate = CreateExecutorState();
22585
22586 buildExpressionExecutionStates(tab, newPartRel, estate);
22587
22588 mycid = GetCurrentCommandId(true);
22589
22590 /* Prepare a BulkInsertState for table_tuple_insert. */
22591 bistate = GetBulkInsertState();
22592
22593 /* Create the necessary tuple slot. */
22594 dstslot = table_slot_create(newPartRel, NULL);
22595
22596 foreach_oid(merging_oid, mergingPartitions)
22597 {
22598 ExprContext *econtext;
22599 TupleTableSlot *srcslot;
22600 TupleConversionMap *tuple_map;
22601 TableScanDesc scan;
22602 MemoryContext oldCxt;
22603 Snapshot snapshot;
22604 Relation mergingPartition;
22605
22606 econtext = GetPerTupleExprContext(estate);
22607
22608 /*
22609 * Partition is already locked in the transformPartitionCmdForMerge
22610 * function.
22611 */
22612 mergingPartition = table_open(merging_oid, NoLock);
22613
22614 /* Create a source tuple slot for the partition being merged. */
22615 srcslot = table_slot_create(mergingPartition, NULL);
22616
22617 /*
22618 * Map computing for moving attributes of the merged partition to the
22619 * new partition.
22620 */
22621 tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
22622 RelationGetDescr(newPartRel));
22623
22624 /* Scan through the rows. */
22625 snapshot = RegisterSnapshot(GetLatestSnapshot());
22626 scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
22627
22628 /*
22629 * Switch to per-tuple memory context and reset it for each tuple
22630 * produced, so we don't leak memory.
22631 */
22633
22634 while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
22635 {
22636 TupleTableSlot *insertslot;
22637
22639
22640 if (tuple_map)
22641 {
22642 /* Need to use a map to copy attributes. */
22643 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
22644 }
22645 else
22646 {
22647 slot_getallattrs(srcslot);
22648
22649 /* Copy attributes directly. */
22650 insertslot = dstslot;
22651
22652 ExecClearTuple(insertslot);
22653
22654 memcpy(insertslot->tts_values, srcslot->tts_values,
22655 sizeof(Datum) * srcslot->tts_nvalid);
22656 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
22657 sizeof(bool) * srcslot->tts_nvalid);
22658
22659 ExecStoreVirtualTuple(insertslot);
22660 }
22661
22662 /*
22663 * Constraints and GENERATED expressions might reference the
22664 * tableoid column, so fill tts_tableOid with the desired value.
22665 * (We must do this each time, because it gets overwritten with
22666 * newrel's OID during storing.)
22667 */
22668 insertslot->tts_tableOid = RelationGetRelid(newPartRel);
22669
22670 /*
22671 * Now, evaluate any generated expressions whose inputs come from
22672 * the new tuple. We assume these columns won't reference each
22673 * other, so that there's no ordering dependency.
22674 */
22676 insertslot, econtext);
22677
22678 /* Write the tuple out to the new relation. */
22679 table_tuple_insert(newPartRel, insertslot, mycid,
22680 ti_options, bistate);
22681
22682 ResetExprContext(econtext);
22683 }
22684
22685 MemoryContextSwitchTo(oldCxt);
22686 table_endscan(scan);
22687 UnregisterSnapshot(snapshot);
22688
22689 if (tuple_map)
22690 free_conversion_map(tuple_map);
22691
22693 table_close(mergingPartition, NoLock);
22694 }
22695
22696 FreeExecutorState(estate);
22698 FreeBulkInsertState(bistate);
22699
22700 table_finish_bulk_insert(newPartRel, ti_options);
22701
22702 /*
22703 * We don't need to process this newPartRel since we already processed it
22704 * here, so delete the ALTER TABLE queue for it.
22705 */
22706 foreach(ltab, *wqueue)
22707 {
22708 tab = (AlteredTableInfo *) lfirst(ltab);
22709 if (tab->relid == RelationGetRelid(newPartRel))
22710 {
22711 *wqueue = list_delete_cell(*wqueue, ltab);
22712 break;
22713 }
22714 }
22715}
AttrMap * attrMap
Definition: tupconvert.h:28
static void evaluateGeneratedExpressionsAndCheckConstraints(AlteredTableInfo *tab, Relation newPartRel, TupleTableSlot *insertslot, ExprContext *econtext)
Definition: tablecmds.c:22150
static void buildExpressionExecutionStates(AlteredTableInfo *tab, Relation newPartRel, EState *estate)
Definition: tablecmds.c:22112
TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc)
Definition: tupconvert.c:103
void free_conversion_map(TupleConversionMap *map)
Definition: tupconvert.c:300
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:193

References ATGetQueueEntry(), TupleConversionMap::attrMap, buildExpressionExecutionStates(), CHECK_FOR_INTERRUPTS, convert_tuples_by_name(), CreateExecutorState(), evaluateGeneratedExpressionsAndCheckConstraints(), ExecClearTuple(), ExecDropSingleTupleTableSlot(), ExecStoreVirtualTuple(), execute_attr_map_slot(), foreach_oid, ForwardScanDirection, free_conversion_map(), FreeBulkInsertState(), FreeExecutorState(), GetBulkInsertState(), GetCurrentCommandId(), GetLatestSnapshot(), GetPerTupleExprContext, GetPerTupleMemoryContext, lfirst, list_delete_cell(), MemoryContextSwitchTo(), NoLock, RegisterSnapshot(), RelationGetDescr, RelationGetRelid, AlteredTableInfo::relid, ResetExprContext, slot_getallattrs(), table_beginscan(), table_close(), table_endscan(), table_finish_bulk_insert(), TABLE_INSERT_SKIP_FSM, table_open(), table_scan_getnextslot(), table_slot_create(), table_tuple_insert(), TupleTableSlot::tts_isnull, TupleTableSlot::tts_nvalid, TupleTableSlot::tts_tableOid, TupleTableSlot::tts_values, and UnregisterSnapshot().

Referenced by ATExecMergePartitions().

◆ NotNullImpliedByRelConstraints()

static bool NotNullImpliedByRelConstraints ( Relation  rel,
Form_pg_attribute  attr 
)
static

Definition at line 8117 of file tablecmds.c.

8118{
8119 NullTest *nnulltest = makeNode(NullTest);
8120
8121 nnulltest->arg = (Expr *) makeVar(1,
8122 attr->attnum,
8123 attr->atttypid,
8124 attr->atttypmod,
8125 attr->attcollation,
8126 0);
8127 nnulltest->nulltesttype = IS_NOT_NULL;
8128
8129 /*
8130 * argisrow = false is correct even for a composite column, because
8131 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8132 * case, just IS DISTINCT FROM NULL.
8133 */
8134 nnulltest->argisrow = false;
8135 nnulltest->location = -1;
8136
8137 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8138 {
8140 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8141 RelationGetRelationName(rel), NameStr(attr->attname))));
8142 return true;
8143 }
8144
8145 return false;
8146}
@ IS_NOT_NULL
Definition: primnodes.h:1977
NullTestType nulltesttype
Definition: primnodes.h:1984
ParseLoc location
Definition: primnodes.h:1987
Expr * arg
Definition: primnodes.h:1983
static bool ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
Definition: tablecmds.c:20140

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

20087{
20088 List *existConstraint = NIL;
20089 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20090 int i;
20091
20092 if (constr && constr->has_not_null)
20093 {
20094 int natts = scanrel->rd_att->natts;
20095
20096 for (i = 1; i <= natts; i++)
20097 {
20098 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20099
20100 /* invalid not-null constraint must be ignored here */
20101 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20102 {
20103 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
20104 NullTest *ntest = makeNode(NullTest);
20105
20106 ntest->arg = (Expr *) makeVar(1,
20107 i,
20108 wholeatt->atttypid,
20109 wholeatt->atttypmod,
20110 wholeatt->attcollation,
20111 0);
20112 ntest->nulltesttype = IS_NOT_NULL;
20113
20114 /*
20115 * argisrow=false is correct even for a composite column,
20116 * because attnotnull does not represent a SQL-spec IS NOT
20117 * NULL test in such a case, just IS DISTINCT FROM NULL.
20118 */
20119 ntest->argisrow = false;
20120 ntest->location = -1;
20121 existConstraint = lappend(existConstraint, ntest);
20122 }
20123 }
20124 }
20125
20126 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20127}

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

Referenced by check_default_partition_contents(), and QueuePartitionConstraintValidation().

◆ PreCommit_on_commit_actions()

void PreCommit_on_commit_actions ( void  )

Definition at line 19346 of file tablecmds.c.

19347{
19348 ListCell *l;
19349 List *oids_to_truncate = NIL;
19350 List *oids_to_drop = NIL;
19351
19352 foreach(l, on_commits)
19353 {
19354 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19355
19356 /* Ignore entry if already dropped in this xact */
19358 continue;
19359
19360 switch (oc->oncommit)
19361 {
19362 case ONCOMMIT_NOOP:
19364 /* Do nothing (there shouldn't be such entries, actually) */
19365 break;
19367
19368 /*
19369 * If this transaction hasn't accessed any temporary
19370 * relations, we can skip truncating ON COMMIT DELETE ROWS
19371 * tables, as they must still be empty.
19372 */
19374 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19375 break;
19376 case ONCOMMIT_DROP:
19377 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19378 break;
19379 }
19380 }
19381
19382 /*
19383 * Truncate relations before dropping so that all dependencies between
19384 * relations are removed after they are worked on. Doing it like this
19385 * might be a waste as it is possible that a relation being truncated will
19386 * be dropped anyway due to its parent being dropped, but this makes the
19387 * code more robust because of not having to re-check that the relation
19388 * exists at truncation time.
19389 */
19390 if (oids_to_truncate != NIL)
19391 heap_truncate(oids_to_truncate);
19392
19393 if (oids_to_drop != NIL)
19394 {
19395 ObjectAddresses *targetObjects = new_object_addresses();
19396
19397 foreach(l, oids_to_drop)
19398 {
19399 ObjectAddress object;
19400
19401 object.classId = RelationRelationId;
19402 object.objectId = lfirst_oid(l);
19403 object.objectSubId = 0;
19404
19405 Assert(!object_address_present(&object, targetObjects));
19406
19407 add_exact_object_address(&object, targetObjects);
19408 }
19409
19410 /*
19411 * Object deletion might involve toast table access (to clean up
19412 * toasted catalog entries), so ensure we have a valid snapshot.
19413 */
19415
19416 /*
19417 * Since this is an automatic drop, rather than one directly initiated
19418 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19419 */
19422
19424
19425#ifdef USE_ASSERT_CHECKING
19426
19427 /*
19428 * Note that table deletion will call remove_on_commit_action, so the
19429 * entry should get marked as deleted.
19430 */
19431 foreach(l, on_commits)
19432 {
19433 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19434
19435 if (oc->oncommit != ONCOMMIT_DROP)
19436 continue;
19437
19439 }
19440#endif
19441 }
19442}
#define PERFORM_DELETION_QUIETLY
Definition: dependency.h:94
void heap_truncate(List *relids)
Definition: heap.c:3587
@ ONCOMMIT_DELETE_ROWS
Definition: primnodes.h:60
@ ONCOMMIT_PRESERVE_ROWS
Definition: primnodes.h:59
@ ONCOMMIT_DROP
Definition: primnodes.h:61
OnCommitAction oncommit
Definition: tablecmds.c:119
int MyXactFlags
Definition: xact.c:137
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:103

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

Referenced by CommitTransaction(), and PrepareTransaction().

◆ QueueCheckConstraintValidation()

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

Definition at line 13145 of file tablecmds.c.

13148{
13150 AlteredTableInfo *tab;
13151 HeapTuple copyTuple;
13152 Form_pg_constraint copy_con;
13153
13154 List *children = NIL;
13155 ListCell *child;
13156 NewConstraint *newcon;
13157 Datum val;
13158 char *conbin;
13159
13160 con = (Form_pg_constraint) GETSTRUCT(contuple);
13161 Assert(con->contype == CONSTRAINT_CHECK);
13162
13163 /*
13164 * If we're recursing, the parent has already done this, so skip it. Also,
13165 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13166 * for it in the children.
13167 */
13168 if (!recursing && !con->connoinherit)
13169 children = find_all_inheritors(RelationGetRelid(rel),
13170 lockmode, NULL);
13171
13172 /*
13173 * For CHECK constraints, we must ensure that we only mark the constraint
13174 * as validated on the parent if it's already validated on the children.
13175 *
13176 * We recurse before validating on the parent, to reduce risk of
13177 * deadlocks.
13178 */
13179 foreach(child, children)
13180 {
13181 Oid childoid = lfirst_oid(child);
13182 Relation childrel;
13183
13184 if (childoid == RelationGetRelid(rel))
13185 continue;
13186
13187 /*
13188 * If we are told not to recurse, there had better not be any child
13189 * tables, because we can't mark the constraint on the parent valid
13190 * unless it is valid for all child tables.
13191 */
13192 if (!recurse)
13193 ereport(ERROR,
13194 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13195 errmsg("constraint must be validated on child tables too")));
13196
13197 /* find_all_inheritors already got lock */
13198 childrel = table_open(childoid, NoLock);
13199
13200 ATExecValidateConstraint(wqueue, childrel, constrName, false,
13201 true, lockmode);
13202 table_close(childrel, NoLock);
13203 }
13204
13205 /* Queue validation for phase 3 */
13206 newcon = palloc0_object(NewConstraint);
13207 newcon->name = constrName;
13208 newcon->contype = CONSTR_CHECK;
13209 newcon->refrelid = InvalidOid;
13210 newcon->refindid = InvalidOid;
13211 newcon->conid = con->oid;
13212
13213 val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13214 Anum_pg_constraint_conbin);
13215 conbin = TextDatumGetCString(val);
13216 newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13217
13218 /* Find or create work queue entry for this table */
13219 tab = ATGetQueueEntry(wqueue, rel);
13220 tab->constraints = lappend(tab->constraints, newcon);
13221
13222 /*
13223 * Invalidate relcache so that others see the new validated constraint.
13224 */
13226
13227 /*
13228 * Now update the catalog, while we have the door open.
13229 */
13230 copyTuple = heap_copytuple(contuple);
13231 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13232 copy_con->convalidated = true;
13233 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13234
13235 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13236
13237 heap_freetuple(copyTuple);
13238}
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_object, 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 13026 of file tablecmds.c.

13028{
13030 AlteredTableInfo *tab;
13031 HeapTuple copyTuple;
13032 Form_pg_constraint copy_con;
13033
13034 con = (Form_pg_constraint) GETSTRUCT(contuple);
13035 Assert(con->contype == CONSTRAINT_FOREIGN);
13036 Assert(!con->convalidated);
13037
13038 /*
13039 * Add the validation to phase 3's queue; not needed for partitioned
13040 * tables themselves, only for their partitions.
13041 *
13042 * When the referenced table (pkrelid) is partitioned, the referencing
13043 * table (fkrel) has one pg_constraint row pointing to each partition
13044 * thereof. These rows are there only to support action triggers and no
13045 * table scan is needed, therefore skip this for them as well.
13046 */
13047 if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13048 con->confrelid == pkrelid)
13049 {
13050 NewConstraint *newcon;
13051 Constraint *fkconstraint;
13052
13053 /* Queue validation for phase 3 */
13054 fkconstraint = makeNode(Constraint);
13055 /* for now this is all we need */
13056 fkconstraint->conname = pstrdup(NameStr(con->conname));
13057
13058 newcon = palloc0_object(NewConstraint);
13059 newcon->name = fkconstraint->conname;
13060 newcon->contype = CONSTR_FOREIGN;
13061 newcon->refrelid = con->confrelid;
13062 newcon->refindid = con->conindid;
13063 newcon->conid = con->oid;
13064 newcon->qual = (Node *) fkconstraint;
13065
13066 /* Find or create work queue entry for this table */
13067 tab = ATGetQueueEntry(wqueue, fkrel);
13068 tab->constraints = lappend(tab->constraints, newcon);
13069 }
13070
13071 /*
13072 * If the table at either end of the constraint is partitioned, we need to
13073 * recurse and handle every unvalidate constraint that is a child of this
13074 * constraint.
13075 */
13076 if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13077 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13078 {
13079 ScanKeyData pkey;
13080 SysScanDesc pscan;
13081 HeapTuple childtup;
13082
13083 ScanKeyInit(&pkey,
13084 Anum_pg_constraint_conparentid,
13085 BTEqualStrategyNumber, F_OIDEQ,
13086 ObjectIdGetDatum(con->oid));
13087
13088 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13089 true, NULL, 1, &pkey);
13090
13091 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13092 {
13093 Form_pg_constraint childcon;
13094 Relation childrel;
13095
13096 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13097
13098 /*
13099 * If the child constraint has already been validated, no further
13100 * action is required for it or its descendants, as they are all
13101 * valid.
13102 */
13103 if (childcon->convalidated)
13104 continue;
13105
13106 childrel = table_open(childcon->conrelid, lockmode);
13107
13108 /*
13109 * NB: Note that pkrelid should be passed as-is during recursion,
13110 * as it is required to identify the root referenced table.
13111 */
13112 QueueFKConstraintValidation(wqueue, conrel, childrel, pkrelid,
13113 childtup, lockmode);
13114 table_close(childrel, NoLock);
13115 }
13116
13117 systable_endscan(pscan);
13118 }
13119
13120 /*
13121 * Now mark the pg_constraint row as validated (even if we didn't check,
13122 * notably the ones for partitions on the referenced side).
13123 *
13124 * We rely on transaction abort to roll back this change if phase 3
13125 * ultimately finds violating rows. This is a bit ugly.
13126 */
13127 copyTuple = heap_copytuple(contuple);
13128 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13129 copy_con->convalidated = true;
13130 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13131
13132 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13133
13134 heap_freetuple(copyTuple);
13135}

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

13251{
13253 AlteredTableInfo *tab;
13254 HeapTuple copyTuple;
13255 Form_pg_constraint copy_con;
13256 List *children = NIL;
13258 char *colname;
13259
13260 con = (Form_pg_constraint) GETSTRUCT(contuple);
13261 Assert(con->contype == CONSTRAINT_NOTNULL);
13262
13263 attnum = extractNotNullColumn(contuple);
13264
13265 /*
13266 * If we're recursing, we've already done this for parent, so skip it.
13267 * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13268 * look for it in the children.
13269 *
13270 * We recurse before validating on the parent, to reduce risk of
13271 * deadlocks.
13272 */
13273 if (!recursing && !con->connoinherit)
13274 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13275
13276 colname = get_attname(RelationGetRelid(rel), attnum, false);
13277 foreach_oid(childoid, children)
13278 {
13279 Relation childrel;
13280 HeapTuple contup;
13281 Form_pg_constraint childcon;
13282 char *conname;
13283
13284 if (childoid == RelationGetRelid(rel))
13285 continue;
13286
13287 /*
13288 * If we are told not to recurse, there had better not be any child
13289 * tables, because we can't mark the constraint on the parent valid
13290 * unless it is valid for all child tables.
13291 */
13292 if (!recurse)
13293 ereport(ERROR,
13294 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13295 errmsg("constraint must be validated on child tables too"));
13296
13297 /*
13298 * The column on child might have a different attnum, so search by
13299 * column name.
13300 */
13301 contup = findNotNullConstraint(childoid, colname);
13302 if (!contup)
13303 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13304 colname, get_rel_name(childoid));
13305 childcon = (Form_pg_constraint) GETSTRUCT(contup);
13306 if (childcon->convalidated)
13307 continue;
13308
13309 /* find_all_inheritors already got lock */
13310 childrel = table_open(childoid, NoLock);
13311 conname = pstrdup(NameStr(childcon->conname));
13312
13313 /* XXX improve ATExecValidateConstraint API to avoid double search */
13314 ATExecValidateConstraint(wqueue, childrel, conname,
13315 false, true, lockmode);
13316 table_close(childrel, NoLock);
13317 }
13318
13319 /* Set attnotnull appropriately without queueing another validation */
13320 set_attnotnull(NULL, rel, attnum, true, false);
13321
13322 tab = ATGetQueueEntry(wqueue, rel);
13323 tab->verify_new_notnull = true;
13324
13325 /*
13326 * Invalidate relcache so that others see the new validated constraint.
13327 */
13329
13330 /*
13331 * Now update the catalogs, while we have the door open.
13332 */
13333 copyTuple = heap_copytuple(contuple);
13334 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13335 copy_con->convalidated = true;
13336 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13337
13338 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13339
13340 heap_freetuple(copyTuple);
13341}

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

20206{
20207 /*
20208 * Based on the table's existing constraints, determine whether or not we
20209 * may skip scanning the table.
20210 */
20211 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20212 {
20213 if (!validate_default)
20215 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20216 RelationGetRelationName(scanrel))));
20217 else
20219 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20220 RelationGetRelationName(scanrel))));
20221 return;
20222 }
20223
20224 /*
20225 * Constraints proved insufficient. For plain relations, queue a
20226 * validation item now; for partitioned tables, recurse to process each
20227 * partition.
20228 */
20229 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20230 {
20231 AlteredTableInfo *tab;
20232
20233 /* Grab a work queue entry. */
20234 tab = ATGetQueueEntry(wqueue, scanrel);
20235 Assert(tab->partition_constraint == NULL);
20236 tab->partition_constraint = (Expr *) linitial(partConstraint);
20237 tab->validate_default = validate_default;
20238 }
20239 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20240 {
20241 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20242 int i;
20243
20244 for (i = 0; i < partdesc->nparts; i++)
20245 {
20246 Relation part_rel;
20247 List *thisPartConstraint;
20248
20249 /*
20250 * This is the minimum lock we need to prevent deadlocks.
20251 */
20252 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20253
20254 /*
20255 * Adjust the constraint for scanrel so that it matches this
20256 * partition's attribute numbers.
20257 */
20258 thisPartConstraint =
20259 map_partition_varattnos(partConstraint, 1,
20260 part_rel, scanrel);
20261
20262 QueuePartitionConstraintValidation(wqueue, part_rel,
20263 thisPartConstraint,
20264 validate_default);
20265 table_close(part_rel, NoLock); /* keep lock till commit */
20266 }
20267 }
20268}
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:20085

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

19614{
19615 Node *stmt = (Node *) arg;
19616 ObjectType reltype;
19617 HeapTuple tuple;
19618 Form_pg_class classform;
19619 AclResult aclresult;
19620 char relkind;
19621
19622 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19623 if (!HeapTupleIsValid(tuple))
19624 return; /* concurrently dropped */
19625 classform = (Form_pg_class) GETSTRUCT(tuple);
19626 relkind = classform->relkind;
19627
19628 /* Must own relation. */
19629 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19631
19632 /* No system table modifications unless explicitly allowed. */
19633 if (!allowSystemTableMods && IsSystemClass(relid, classform))
19634 ereport(ERROR,
19635 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19636 errmsg("permission denied: \"%s\" is a system catalog",
19637 rv->relname)));
19638
19639 /*
19640 * Extract the specified relation type from the statement parse tree.
19641 *
19642 * Also, for ALTER .. RENAME, check permissions: the user must (still)
19643 * have CREATE rights on the containing namespace.
19644 */
19645 if (IsA(stmt, RenameStmt))
19646 {
19647 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19649 if (aclresult != ACLCHECK_OK)
19650 aclcheck_error(aclresult, OBJECT_SCHEMA,
19651 get_namespace_name(classform->relnamespace));
19652 reltype = ((RenameStmt *) stmt)->renameType;
19653 }
19654 else if (IsA(stmt, AlterObjectSchemaStmt))
19655 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19656
19657 else if (IsA(stmt, AlterTableStmt))
19658 reltype = ((AlterTableStmt *) stmt)->objtype;
19659 else
19660 {
19661 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19662 reltype = OBJECT_TABLE; /* placate compiler */
19663 }
19664
19665 /*
19666 * For compatibility with prior releases, we allow ALTER TABLE to be used
19667 * with most other types of relations (but not composite types). We allow
19668 * similar flexibility for ALTER INDEX in the case of RENAME, but not
19669 * otherwise. Otherwise, the user must select the correct form of the
19670 * command for the relation at issue.
19671 */
19672 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19673 ereport(ERROR,
19674 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19675 errmsg("\"%s\" is not a sequence", rv->relname)));
19676
19677 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19678 ereport(ERROR,
19679 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19680 errmsg("\"%s\" is not a view", rv->relname)));
19681
19682 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19683 ereport(ERROR,
19684 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19685 errmsg("\"%s\" is not a materialized view", rv->relname)));
19686
19687 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19688 ereport(ERROR,
19689 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19690 errmsg("\"%s\" is not a foreign table", rv->relname)));
19691
19692 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19693 ereport(ERROR,
19694 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19695 errmsg("\"%s\" is not a composite type", rv->relname)));
19696
19697 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19698 relkind != RELKIND_PARTITIONED_INDEX
19699 && !IsA(stmt, RenameStmt))
19700 ereport(ERROR,
19701 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19702 errmsg("\"%s\" is not an index", rv->relname)));
19703
19704 /*
19705 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19706 * TYPE for that.
19707 */
19708 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19709 ereport(ERROR,
19710 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19711 errmsg("\"%s\" is a composite type", rv->relname),
19712 /* translator: %s is an SQL ALTER command */
19713 errhint("Use %s instead.",
19714 "ALTER TYPE")));
19715
19716 /*
19717 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19718 * to a different schema, such as indexes and TOAST tables.
19719 */
19721 {
19722 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19723 ereport(ERROR,
19724 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19725 errmsg("cannot change schema of index \"%s\"",
19726 rv->relname),
19727 errhint("Change the schema of the table instead.")));
19728 else if (relkind == RELKIND_COMPOSITE_TYPE)
19729 ereport(ERROR,
19730 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19731 errmsg("cannot change schema of composite type \"%s\"",
19732 rv->relname),
19733 /* translator: %s is an SQL ALTER command */
19734 errhint("Use %s instead.",
19735 "ALTER TYPE")));
19736 else if (relkind == RELKIND_TOASTVALUE)
19737 ereport(ERROR,
19738 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19739 errmsg("cannot change schema of TOAST table \"%s\"",
19740 rv->relname),
19741 errhint("Change the schema of the table instead.")));
19742 }
19743
19744 ReleaseSysCache(tuple);
19745}
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:86
ObjectType
Definition: parsenodes.h:2350
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2369
@ OBJECT_VIEW
Definition: parsenodes.h:2402
@ OBJECT_TYPE
Definition: parsenodes.h:2400

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

21570{
21572 Form_pg_class classform;
21573 HeapTuple tuple;
21574
21575 state = (struct AttachIndexCallbackState *) arg;
21576
21577 if (!state->lockedParentTbl)
21578 {
21579 LockRelationOid(state->parentTblOid, AccessShareLock);
21580 state->lockedParentTbl = true;
21581 }
21582
21583 /*
21584 * If we previously locked some other heap, and the name we're looking up
21585 * no longer refers to an index on that relation, release the now-useless
21586 * lock. XXX maybe we should do *after* we verify whether the index does
21587 * not actually belong to the same relation ...
21588 */
21589 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21590 {
21591 UnlockRelationOid(state->partitionOid, AccessShareLock);
21592 state->partitionOid = InvalidOid;
21593 }
21594
21595 /* Didn't find a relation, so no need for locking or permission checks. */
21596 if (!OidIsValid(relOid))
21597 return;
21598
21599 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21600 if (!HeapTupleIsValid(tuple))
21601 return; /* concurrently dropped, so nothing to do */
21602 classform = (Form_pg_class) GETSTRUCT(tuple);
21603 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21604 classform->relkind != RELKIND_INDEX)
21605 ereport(ERROR,
21606 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21607 errmsg("\"%s\" is not an index", rv->relname)));
21608 ReleaseSysCache(tuple);
21609
21610 /*
21611 * Since we need only examine the heap's tupledesc, an access share lock
21612 * on it (preventing any DDL) is sufficient.
21613 */
21614 state->partitionOid = IndexGetRelation(relOid, false);
21615 LockRelationOid(state->partitionOid, AccessShareLock);
21616}
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 1697 of file tablecmds.c.

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

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

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

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

19558{
19559 HeapTuple tuple;
19560
19561 /* Nothing to do if the relation was not found. */
19562 if (!OidIsValid(relId))
19563 return;
19564
19565 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19566 if (!HeapTupleIsValid(tuple)) /* should not happen */
19567 elog(ERROR, "cache lookup failed for relation %u", relId);
19568
19571
19572 ReleaseSysCache(tuple);
19573}

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

19522{
19523 char relkind;
19524 AclResult aclresult;
19525
19526 /* Nothing to do if the relation was not found. */
19527 if (!OidIsValid(relId))
19528 return;
19529
19530 /*
19531 * If the relation does exist, check whether it's an index. But note that
19532 * the relation might have been dropped between the time we did the name
19533 * lookup and now. In that case, there's nothing to do.
19534 */
19535 relkind = get_rel_relkind(relId);
19536 if (!relkind)
19537 return;
19538 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19539 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19540 ereport(ERROR,
19541 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19542 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19543
19544 /* Check permissions */
19545 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19546 if (aclresult != ACLCHECK_OK)
19547 aclcheck_error(aclresult,
19549 relation->relname);
19550}
#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 19580 of file tablecmds.c.

19582{
19583 HeapTuple tuple;
19584
19585 /* Nothing to do if the relation was not found. */
19586 if (!OidIsValid(relId))
19587 return;
19588
19589 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19590 if (!HeapTupleIsValid(tuple)) /* should not happen */
19591 elog(ERROR, "cache lookup failed for relation %u", relId);
19592
19593 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19595 relation->relname);
19596
19597 if (!allowSystemTableMods &&
19598 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19599 ereport(ERROR,
19600 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19601 errmsg("permission denied: \"%s\" is a system catalog",
19602 relation->relname)));
19603
19604 ReleaseSysCache(tuple);
19605}

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(), ProcessUtilitySlow(), transformPartitionCmdForMerge(), and transformPartitionCmdForSplit().

◆ RebuildConstraintComment()

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

Definition at line 15870 of file tablecmds.c.

15873{
15874 CommentStmt *cmd;
15875 char *comment_str;
15876 AlterTableCmd *newcmd;
15877
15878 /* Look for comment for object wanted, and leave if none */
15879 comment_str = GetComment(objid, ConstraintRelationId, 0);
15880 if (comment_str == NULL)
15881 return;
15882
15883 /* Build CommentStmt node, copying all input data for safety */
15884 cmd = makeNode(CommentStmt);
15885 if (rel)
15886 {
15888 cmd->object = (Node *)
15891 makeString(pstrdup(conname)));
15892 }
15893 else
15894 {
15896 cmd->object = (Node *)
15898 makeString(pstrdup(conname)));
15899 }
15900 cmd->comment = comment_str;
15901
15902 /* Append it to list of commands */
15903 newcmd = makeNode(AlterTableCmd);
15904 newcmd->subtype = AT_ReAddComment;
15905 newcmd->def = (Node *) cmd;
15906 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15907}
TypeName * makeTypeNameFromNameList(List *names)
Definition: makefuncs.c:531
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2391
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2364
#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:3390
ObjectType objtype
Definition: parsenodes.h:3388
Node * object
Definition: parsenodes.h:3389

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

21785{
21786 Oid existingIdx;
21787
21788 existingIdx = index_get_partition(partitionTbl,
21789 RelationGetRelid(parentIdx));
21790 if (OidIsValid(existingIdx))
21791 ereport(ERROR,
21792 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21793 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21794 RelationGetRelationName(partIdx),
21795 RelationGetRelationName(parentIdx)),
21796 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
21797 get_rel_name(existingIdx),
21798 RelationGetRelationName(partitionTbl))));
21799}

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

19288{
19289 OnCommitItem *oc;
19290 MemoryContext oldcxt;
19291
19292 /*
19293 * We needn't bother registering the relation unless there is an ON COMMIT
19294 * action we need to take.
19295 */
19297 return;
19298
19300
19302 oc->relid = relid;
19303 oc->oncommit = action;
19306
19307 /*
19308 * We use lcons() here so that ON COMMIT actions are processed in reverse
19309 * order of registration. That might not be essential but it seems
19310 * reasonable.
19311 */
19313
19314 MemoryContextSwitchTo(oldcxt);
19315}
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_object, 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 18428 of file tablecmds.c.

18430{
18431 Relation pg_index;
18432 Relation pg_class;
18433 HeapTuple pg_class_tuple;
18434 HeapTuple pg_index_tuple;
18435 Form_pg_class pg_class_form;
18436 Form_pg_index pg_index_form;
18437 ListCell *index;
18438
18439 /*
18440 * Check whether relreplident has changed, and update it if so.
18441 */
18442 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18443 pg_class_tuple = SearchSysCacheCopy1(RELOID,
18445 if (!HeapTupleIsValid(pg_class_tuple))
18446 elog(ERROR, "cache lookup failed for relation \"%s\"",
18448 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18449 if (pg_class_form->relreplident != ri_type)
18450 {
18451 pg_class_form->relreplident = ri_type;
18452 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18453 }
18454 table_close(pg_class, RowExclusiveLock);
18455 heap_freetuple(pg_class_tuple);
18456
18457 /*
18458 * Update the per-index indisreplident flags correctly.
18459 */
18460 pg_index = table_open(IndexRelationId, RowExclusiveLock);
18461 foreach(index, RelationGetIndexList(rel))
18462 {
18463 Oid thisIndexOid = lfirst_oid(index);
18464 bool dirty = false;
18465
18466 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18467 ObjectIdGetDatum(thisIndexOid));
18468 if (!HeapTupleIsValid(pg_index_tuple))
18469 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18470 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18471
18472 if (thisIndexOid == indexOid)
18473 {
18474 /* Set the bit if not already set. */
18475 if (!pg_index_form->indisreplident)
18476 {
18477 dirty = true;
18478 pg_index_form->indisreplident = true;
18479 }
18480 }
18481 else
18482 {
18483 /* Unset the bit if set. */
18484 if (pg_index_form->indisreplident)
18485 {
18486 dirty = true;
18487 pg_index_form->indisreplident = false;
18488 }
18489 }
18490
18491 if (dirty)
18492 {
18493 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18494 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18495 InvalidOid, is_internal);
18496
18497 /*
18498 * Invalidate the relcache for the table, so that after we commit
18499 * all sessions will refresh the table's replica identity index
18500 * before attempting any UPDATE or DELETE on the table. (If we
18501 * changed the table's pg_class row above, then a relcache inval
18502 * is already queued due to that; but we might not have.)
18503 */
18505 }
18506 heap_freetuple(pg_index_tuple);
18507 }
18508
18509 table_close(pg_index, RowExclusiveLock);
18510}
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 15071 of file tablecmds.c.

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

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

15313{
15314 if (!get_index_isclustered(indoid))
15315 return;
15316
15317 if (tab->clusterOnIndex)
15318 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15319
15320 tab->clusterOnIndex = get_rel_name(indoid);
15321}
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3766

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

15329{
15330 /*
15331 * This de-duplication check is critical for two independent reasons: we
15332 * mustn't try to recreate the same constraint twice, and if a constraint
15333 * depends on more than one column whose type is to be altered, we must
15334 * capture its definition string before applying any of the column type
15335 * changes. ruleutils.c will get confused if we ask again later.
15336 */
15337 if (!list_member_oid(tab->changedConstraintOids, conoid))
15338 {
15339 /* OK, capture the constraint's existing definition string */
15340 char *defstring = pg_get_constraintdef_command(conoid);
15341 Oid indoid;
15342
15343 /*
15344 * It is critical to create not-null constraints ahead of primary key
15345 * indexes; otherwise, the not-null constraint would be created by the
15346 * primary key, and the constraint name would be wrong.
15347 */
15348 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15349 {
15350 tab->changedConstraintOids = lcons_oid(conoid,
15352 tab->changedConstraintDefs = lcons(defstring,
15354 }
15355 else
15356 {
15357
15359 conoid);
15361 defstring);
15362 }
15363
15364 /*
15365 * For the index of a constraint, if any, remember if it is used for
15366 * the table's replica identity or if it is a clustered index, so that
15367 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15368 * those properties.
15369 */
15370 indoid = get_constraint_index(conoid);
15371 if (OidIsValid(indoid))
15372 {
15374 RememberClusterOnForRebuilding(indoid, tab);
15375 }
15376 }
15377}
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:15297
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15312

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

15385{
15386 /*
15387 * This de-duplication check is critical for two independent reasons: we
15388 * mustn't try to recreate the same index twice, and if an index depends
15389 * on more than one column whose type is to be altered, we must capture
15390 * its definition string before applying any of the column type changes.
15391 * ruleutils.c will get confused if we ask again later.
15392 */
15393 if (!list_member_oid(tab->changedIndexOids, indoid))
15394 {
15395 /*
15396 * Before adding it as an index-to-rebuild, we'd better see if it
15397 * belongs to a constraint, and if so rebuild the constraint instead.
15398 * Typically this check fails, because constraint indexes normally
15399 * have only dependencies on their constraint. But it's possible for
15400 * such an index to also have direct dependencies on table columns,
15401 * for example with a partial exclusion constraint.
15402 */
15403 Oid conoid = get_index_constraint(indoid);
15404
15405 if (OidIsValid(conoid))
15406 {
15408 }
15409 else
15410 {
15411 /* OK, capture the index's existing definition string */
15412 char *defstring = pg_get_indexdef_string(indoid);
15413
15415 indoid);
15417 defstring);
15418
15419 /*
15420 * Remember if this index is used for the table's replica identity
15421 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15422 * can queue up commands necessary to restore those properties.
15423 */
15425 RememberClusterOnForRebuilding(indoid, tab);
15426 }
15427 }
15428}
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 15297 of file tablecmds.c.

15298{
15299 if (!get_index_isreplident(indoid))
15300 return;
15301
15302 if (tab->replicaIdentityIndex)
15303 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15304
15305 tab->replicaIdentityIndex = get_rel_name(indoid);
15306}
bool get_index_isreplident(Oid index_oid)
Definition: lsyscache.c:3720

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

15436{
15437 /*
15438 * This de-duplication check is critical for two independent reasons: we
15439 * mustn't try to recreate the same statistics object twice, and if the
15440 * statistics object depends on more than one column whose type is to be
15441 * altered, we must capture its definition string before applying any of
15442 * the type changes. ruleutils.c will get confused if we ask again later.
15443 */
15444 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15445 {
15446 /* OK, capture the statistics object's existing definition string */
15447 char *defstring = pg_get_statisticsobjdef_string(stxoid);
15448
15450 stxoid);
15452 defstring);
15453 }
15454}
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 19323 of file tablecmds.c.

19324{
19325 ListCell *l;
19326
19327 foreach(l, on_commits)
19328 {
19329 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19330
19331 if (oc->relid == relid)
19332 {
19334 break;
19335 }
19336 }
19337}

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

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

◆ RemoveInheritedConstraint()

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

Definition at line 11949 of file tablecmds.c.

11951{
11952 ObjectAddresses *objs;
11953 HeapTuple consttup;
11955 SysScanDesc scan;
11956 HeapTuple trigtup;
11957
11959 Anum_pg_constraint_conrelid,
11960 BTEqualStrategyNumber, F_OIDEQ,
11961 ObjectIdGetDatum(conrelid));
11962
11963 scan = systable_beginscan(conrel,
11964 ConstraintRelidTypidNameIndexId,
11965 true, NULL, 1, &key);
11966 objs = new_object_addresses();
11967 while ((consttup = systable_getnext(scan)) != NULL)
11968 {
11969 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11970
11971 if (conform->conparentid != conoid)
11972 continue;
11973 else
11974 {
11975 ObjectAddress addr;
11976 SysScanDesc scan2;
11977 ScanKeyData key2;
11979
11980 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11981 add_exact_object_address(&addr, objs);
11982
11983 /*
11984 * First we must delete the dependency record that binds the
11985 * constraint records together.
11986 */
11987 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11988 conform->oid,
11990 ConstraintRelationId,
11991 conoid);
11992 Assert(n == 1); /* actually only one is expected */
11993
11994 /*
11995 * Now search for the triggers for this constraint and set them up
11996 * for deletion too
11997 */
11998 ScanKeyInit(&key2,
11999 Anum_pg_trigger_tgconstraint,
12000 BTEqualStrategyNumber, F_OIDEQ,
12001 ObjectIdGetDatum(conform->oid));
12002 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
12003 true, NULL, 1, &key2);
12004 while ((trigtup = systable_getnext(scan2)) != NULL)
12005 {
12006 ObjectAddressSet(addr, TriggerRelationId,
12007 ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
12008 add_exact_object_address(&addr, objs);
12009 }
12010 systable_endscan(scan2);
12011 }
12012 }
12013 /* make the dependency deletions visible */
12017 systable_endscan(scan);
12018}
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:229
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 1533 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

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

Referenced by ExecRenameStmt().

◆ RenameRelation()

ObjectAddress RenameRelation ( RenameStmt stmt)

Definition at line 4203 of file tablecmds.c.

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

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

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

◆ ResetRelRewrite()

void ResetRelRewrite ( Oid  myrelid)

Definition at line 4360 of file tablecmds.c.

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

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

7870{
7871 Form_pg_attribute attr;
7872 CompactAttribute *thisatt;
7873
7874 Assert(!queue_validation || wqueue);
7875
7877
7878 /*
7879 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7880 * attribute.
7881 */
7882 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7883 if (attr->attisdropped)
7884 return;
7885
7886 if (!attr->attnotnull)
7887 {
7888 Relation attr_rel;
7889 HeapTuple tuple;
7890
7891 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7892
7894 if (!HeapTupleIsValid(tuple))
7895 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7896 attnum, RelationGetRelid(rel));
7897
7898 thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7900
7901 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7902
7903 attr->attnotnull = true;
7904 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7905
7906 /*
7907 * If the nullness isn't already proven by validated constraints, have
7908 * ALTER TABLE phase 3 test for it.
7909 */
7910 if (queue_validation && wqueue &&
7912 {
7913 AlteredTableInfo *tab;
7914
7915 tab = ATGetQueueEntry(wqueue, rel);
7916 tab->verify_new_notnull = true;
7917 }
7918
7920
7921 table_close(attr_rel, RowExclusiveLock);
7922 heap_freetuple(tuple);
7923 }
7924 else
7925 {
7927 }
7928}
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition: tablecmds.c:8117

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

9163{
9164 ListCell *lc;
9165
9166 foreach(lc, RelationGetIndexList(rel))
9167 {
9168 Oid indexoid = lfirst_oid(lc);
9169 Relation indrel;
9170 AttrNumber indattnum = 0;
9171 HeapTuple tuple;
9172
9173 indrel = index_open(indexoid, lockmode);
9174
9175 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9176 {
9177 if (indrel->rd_index->indkey.values[i] == attnum)
9178 {
9179 indattnum = i + 1;
9180 break;
9181 }
9182 }
9183
9184 if (indattnum == 0)
9185 {
9186 index_close(indrel, lockmode);
9187 continue;
9188 }
9189
9190 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9191
9192 if (HeapTupleIsValid(tuple))
9193 {
9194 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9195
9196 if (setstorage)
9197 attrtuple->attstorage = newstorage;
9198
9199 if (setcompression)
9200 attrtuple->attcompression = newcompression;
9201
9202 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9203
9204 InvokeObjectPostAlterHook(RelationRelationId,
9205 RelationGetRelid(rel),
9206 attrtuple->attnum);
9207
9208 heap_freetuple(tuple);
9209 }
9210
9211 index_close(indrel, lockmode);
9212 }
9213}

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

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

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

◆ SplitPartitionMoveRows()

static void SplitPartitionMoveRows ( List **  wqueue,
Relation  rel,
Relation  splitRel,
List partlist,
List newPartRels 
)
static

Definition at line 23004 of file tablecmds.c.

23006{
23007 /* The FSM is empty, so don't bother using it. */
23008 int ti_options = TABLE_INSERT_SKIP_FSM;
23009 CommandId mycid;
23010 EState *estate;
23011 ListCell *listptr,
23012 *listptr2;
23013 TupleTableSlot *srcslot;
23014 ExprContext *econtext;
23015 TableScanDesc scan;
23016 Snapshot snapshot;
23017 MemoryContext oldCxt;
23018 List *partContexts = NIL;
23019 TupleConversionMap *tuple_map;
23020 SplitPartitionContext *defaultPartCtx = NULL,
23021 *pc;
23022
23023 mycid = GetCurrentCommandId(true);
23024
23025 estate = CreateExecutorState();
23026
23027 forboth(listptr, partlist, listptr2, newPartRels)
23028 {
23030
23032
23033 /* Find the work queue entry for the new partition table: newPartRel. */
23034 pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
23035
23036 buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
23037
23038 if (sps->bound->is_default)
23039 {
23040 /*
23041 * We should not create a structure to check the partition
23042 * constraint for the new DEFAULT partition.
23043 */
23044 defaultPartCtx = pc;
23045 }
23046 else
23047 {
23048 List *partConstraint;
23049
23050 /* Build expression execution states for partition check quals. */
23051 partConstraint = get_qual_from_partbound(rel, sps->bound);
23052 partConstraint =
23054 (Node *) partConstraint);
23055 /* Make a boolean expression for ExecCheck(). */
23056 partConstraint = list_make1(make_ands_explicit(partConstraint));
23057
23058 /*
23059 * Map the vars in the constraint expression from rel's attnos to
23060 * splitRel's.
23061 */
23062 partConstraint = map_partition_varattnos(partConstraint,
23063 1, splitRel, rel);
23064
23065 pc->partqualstate =
23066 ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
23067 Assert(pc->partqualstate != NULL);
23068 }
23069
23070 /* Store partition context into a list. */
23071 partContexts = lappend(partContexts, pc);
23072 }
23073
23074 econtext = GetPerTupleExprContext(estate);
23075
23076 /* Create the necessary tuple slot. */
23077 srcslot = table_slot_create(splitRel, NULL);
23078
23079 /*
23080 * Map computing for moving attributes of the split partition to the new
23081 * partition (for the first new partition, but other new partitions can
23082 * use the same map).
23083 */
23084 pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
23085 tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
23086 RelationGetDescr(pc->partRel));
23087
23088 /* Scan through the rows. */
23089 snapshot = RegisterSnapshot(GetLatestSnapshot());
23090 scan = table_beginscan(splitRel, snapshot, 0, NULL);
23091
23092 /*
23093 * Switch to per-tuple memory context and reset it for each tuple
23094 * produced, so we don't leak memory.
23095 */
23097
23098 while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
23099 {
23100 bool found = false;
23101 TupleTableSlot *insertslot;
23102
23104
23105 econtext->ecxt_scantuple = srcslot;
23106
23107 /* Search partition for the current slot, srcslot. */
23108 foreach(listptr, partContexts)
23109 {
23110 pc = (SplitPartitionContext *) lfirst(listptr);
23111
23112 /* skip DEFAULT partition */
23113 if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
23114 {
23115 found = true;
23116 break;
23117 }
23118 }
23119 if (!found)
23120 {
23121 /* Use the DEFAULT partition if it exists. */
23122 if (defaultPartCtx)
23123 pc = defaultPartCtx;
23124 else
23125 ereport(ERROR,
23126 errcode(ERRCODE_CHECK_VIOLATION),
23127 errmsg("can not find partition for split partition row"),
23128 errtable(splitRel));
23129 }
23130
23131 if (tuple_map)
23132 {
23133 /* Need to use a map to copy attributes. */
23134 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
23135 }
23136 else
23137 {
23138 /* Extract data from the old tuple. */
23139 slot_getallattrs(srcslot);
23140
23141 /* Copy attributes directly. */
23142 insertslot = pc->dstslot;
23143
23144 ExecClearTuple(insertslot);
23145
23146 memcpy(insertslot->tts_values, srcslot->tts_values,
23147 sizeof(Datum) * srcslot->tts_nvalid);
23148 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
23149 sizeof(bool) * srcslot->tts_nvalid);
23150
23151 ExecStoreVirtualTuple(insertslot);
23152 }
23153
23154 /*
23155 * Constraints and GENERATED expressions might reference the tableoid
23156 * column, so fill tts_tableOid with the desired value. (We must do
23157 * this each time, because it gets overwritten with newrel's OID
23158 * during storing.)
23159 */
23160 insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
23161
23162 /*
23163 * Now, evaluate any generated expressions whose inputs come from the
23164 * new tuple. We assume these columns won't reference each other, so
23165 * that there's no ordering dependency.
23166 */
23168 insertslot, econtext);
23169
23170 /* Write the tuple out to the new relation. */
23171 table_tuple_insert(pc->partRel, insertslot, mycid,
23172 ti_options, pc->bistate);
23173
23174 ResetExprContext(econtext);
23175 }
23176
23177 MemoryContextSwitchTo(oldCxt);
23178
23179 table_endscan(scan);
23180 UnregisterSnapshot(snapshot);
23181
23182 if (tuple_map)
23183 free_conversion_map(tuple_map);
23184
23186
23187 FreeExecutorState(estate);
23188
23189 foreach_ptr(SplitPartitionContext, spc, partContexts)
23190 deleteSplitPartitionContext(spc, wqueue, ti_options);
23191}
static SplitPartitionContext * createSplitPartitionContext(Relation partRel)
Definition: tablecmds.c:22943
static void deleteSplitPartitionContext(SplitPartitionContext *pc, List **wqueue, int ti_options)
Definition: tablecmds.c:22966

References Assert(), ATGetQueueEntry(), TupleConversionMap::attrMap, SinglePartitionSpec::bound, buildExpressionExecutionStates(), CHECK_FOR_INTERRUPTS, convert_tuples_by_name(), CreateExecutorState(), createSplitPartitionContext(), deleteSplitPartitionContext(), ExprContext::ecxt_scantuple, ereport, errcode(), errmsg(), ERROR, errtable(), eval_const_expressions(), evaluateGeneratedExpressionsAndCheckConstraints(), ExecCheck(), ExecClearTuple(), ExecDropSingleTupleTableSlot(), ExecPrepareExpr(), ExecStoreVirtualTuple(), execute_attr_map_slot(), forboth, foreach_ptr, ForwardScanDirection, free_conversion_map(), FreeExecutorState(), get_qual_from_partbound(), GetCurrentCommandId(), GetLatestSnapshot(), GetPerTupleExprContext, GetPerTupleMemoryContext, PartitionBoundSpec::is_default, lappend(), lfirst, linitial, list_head(), list_make1, make_ands_explicit(), map_partition_varattnos(), MemoryContextSwitchTo(), NIL, RegisterSnapshot(), RelationGetDescr, RelationGetRelid, ResetExprContext, slot_getallattrs(), table_beginscan(), table_endscan(), TABLE_INSERT_SKIP_FSM, table_scan_getnextslot(), table_slot_create(), table_tuple_insert(), TupleTableSlot::tts_isnull, TupleTableSlot::tts_nvalid, TupleTableSlot::tts_tableOid, TupleTableSlot::tts_values, and UnregisterSnapshot().

Referenced by ATExecSplitPartition().

◆ storage_name()

static const char * storage_name ( char  c)
static

Definition at line 2459 of file tablecmds.c.

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

Referenced by MergeChildAttribute(), and MergeInheritedAttribute().

◆ StoreCatalogInheritance()

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

Definition at line 3518 of file tablecmds.c.

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

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

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

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

13357{
13358 ListCell *l;
13359 int attnum;
13360
13361 attnum = 0;
13362 foreach(l, colList)
13363 {
13364 char *attname = strVal(lfirst(l));
13365 HeapTuple atttuple;
13366 Form_pg_attribute attform;
13367
13368 atttuple = SearchSysCacheAttName(relId, attname);
13369 if (!HeapTupleIsValid(atttuple))
13370 ereport(ERROR,
13371 (errcode(ERRCODE_UNDEFINED_COLUMN),
13372 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13373 attname)));
13374 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13375 if (attform->attnum < 0)
13376 ereport(ERROR,
13377 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13378 errmsg("system columns cannot be used in foreign keys")));
13379 if (attnum >= INDEX_MAX_KEYS)
13380 ereport(ERROR,
13381 (errcode(ERRCODE_TOO_MANY_COLUMNS),
13382 errmsg("cannot have more than %d keys in a foreign key",
13383 INDEX_MAX_KEYS)));
13384 attnums[attnum] = attform->attnum;
13385 if (atttypids != NULL)
13386 atttypids[attnum] = attform->atttypid;
13387 if (attcollids != NULL)
13388 attcollids[attnum] = attform->attcollation;
13389 ReleaseSysCache(atttuple);
13390 attnum++;
13391 }
13392
13393 return attnum;
13394}

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

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

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

13414{
13415 List *indexoidlist;
13416 ListCell *indexoidscan;
13417 HeapTuple indexTuple = NULL;
13418 Form_pg_index indexStruct = NULL;
13419 Datum indclassDatum;
13420 oidvector *indclass;
13421 int i;
13422
13423 /*
13424 * Get the list of index OIDs for the table from the relcache, and look up
13425 * each one in the pg_index syscache until we find one marked primary key
13426 * (hopefully there isn't more than one such). Insist it's valid, too.
13427 */
13428 *indexOid = InvalidOid;
13429
13430 indexoidlist = RelationGetIndexList(pkrel);
13431
13432 foreach(indexoidscan, indexoidlist)
13433 {
13434 Oid indexoid = lfirst_oid(indexoidscan);
13435
13436 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13437 if (!HeapTupleIsValid(indexTuple))
13438 elog(ERROR, "cache lookup failed for index %u", indexoid);
13439 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13440 if (indexStruct->indisprimary && indexStruct->indisvalid)
13441 {
13442 /*
13443 * Refuse to use a deferrable primary key. This is per SQL spec,
13444 * and there would be a lot of interesting semantic problems if we
13445 * tried to allow it.
13446 */
13447 if (!indexStruct->indimmediate)
13448 ereport(ERROR,
13449 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13450 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13451 RelationGetRelationName(pkrel))));
13452
13453 *indexOid = indexoid;
13454 break;
13455 }
13456 ReleaseSysCache(indexTuple);
13457 }
13458
13459 list_free(indexoidlist);
13460
13461 /*
13462 * Check that we found it
13463 */
13464 if (!OidIsValid(*indexOid))
13465 ereport(ERROR,
13466 (errcode(ERRCODE_UNDEFINED_OBJECT),
13467 errmsg("there is no primary key for referenced table \"%s\"",
13468 RelationGetRelationName(pkrel))));
13469
13470 /* Must get indclass the hard way */
13471 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13472 Anum_pg_index_indclass);
13473 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13474
13475 /*
13476 * Now build the list of PK attributes from the indkey definition (we
13477 * assume a primary key cannot have expressional elements)
13478 */
13479 *attnamelist = NIL;
13480 for (i = 0; i < indexStruct->indnkeyatts; i++)
13481 {
13482 int pkattno = indexStruct->indkey.values[i];
13483
13484 attnums[i] = pkattno;
13485 atttypids[i] = attnumTypeId(pkrel, pkattno);
13486 attcollids[i] = attnumCollationId(pkrel, pkattno);
13487 opclasses[i] = indclass->values[i];
13488 *attnamelist = lappend(*attnamelist,
13489 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13490 }
13491
13492 *pk_has_without_overlaps = indexStruct->indisexclusion;
13493
13494 ReleaseSysCache(indexTuple);
13495
13496 return i;
13497}
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 19753 of file tablecmds.c.

19754{
19755 PartitionSpec *newspec;
19756 ParseState *pstate;
19757 ParseNamespaceItem *nsitem;
19758 ListCell *l;
19759
19760 newspec = makeNode(PartitionSpec);
19761
19762 newspec->strategy = partspec->strategy;
19763 newspec->partParams = NIL;
19764 newspec->location = partspec->location;
19765
19766 /* Check valid number of columns for strategy */
19767 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19768 list_length(partspec->partParams) != 1)
19769 ereport(ERROR,
19770 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19771 errmsg("cannot use \"list\" partition strategy with more than one column")));
19772
19773 /*
19774 * Create a dummy ParseState and insert the target relation as its sole
19775 * rangetable entry. We need a ParseState for transformExpr.
19776 */
19777 pstate = make_parsestate(NULL);
19778 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19779 NULL, false, true);
19780 addNSItemToQuery(pstate, nsitem, true, true, true);
19781
19782 /* take care of any partition expressions */
19783 foreach(l, partspec->partParams)
19784 {
19786
19787 if (pelem->expr)
19788 {
19789 /* Copy, to avoid scribbling on the input */
19790 pelem = copyObject(pelem);
19791
19792 /* Now do parse transformation of the expression */
19793 pelem->expr = transformExpr(pstate, pelem->expr,
19795
19796 /* we have to fix its collations too */
19797 assign_expr_collations(pstate, pelem->expr);
19798 }
19799
19800 newspec->partParams = lappend(newspec->partParams, pelem);
19801 }
19802
19803 return newspec;
19804}
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:119
@ EXPR_KIND_PARTITION_EXPRESSION
Definition: parse_node.h:80
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:900
List * partParams
Definition: parsenodes.h:914
ParseLoc location
Definition: parsenodes.h:915
PartitionStrategy strategy
Definition: parsenodes.h:913

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

Referenced by DefineRelation().

◆ truncate_check_activity()

static void truncate_check_activity ( Relation  rel)
static

Definition at line 2436 of file tablecmds.c.

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

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

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

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

11733{
11734 HeapTuple parentConstrTup;
11735 Form_pg_constraint parentConstr;
11736 HeapTuple partcontup;
11737 Form_pg_constraint partConstr;
11738
11739 parentConstrTup = SearchSysCache1(CONSTROID,
11740 ObjectIdGetDatum(parentConstrOid));
11741 if (!HeapTupleIsValid(parentConstrTup))
11742 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11743 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11744
11745 /*
11746 * Do some quick & easy initial checks. If any of these fail, we cannot
11747 * use this constraint.
11748 */
11749 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11750 {
11751 ReleaseSysCache(parentConstrTup);
11752 return false;
11753 }
11754 for (int i = 0; i < numfks; i++)
11755 {
11756 if (fk->conkey[i] != mapped_conkey[i] ||
11757 fk->confkey[i] != confkey[i] ||
11758 fk->conpfeqop[i] != conpfeqop[i])
11759 {
11760 ReleaseSysCache(parentConstrTup);
11761 return false;
11762 }
11763 }
11764
11765 /* Looks good so far; perform more extensive checks. */
11766 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11767 if (!HeapTupleIsValid(partcontup))
11768 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11769 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11770
11771 /*
11772 * An error should be raised if the constraint enforceability is
11773 * different. Returning false without raising an error, as we do for other
11774 * attributes, could lead to a duplicate constraint with the same
11775 * enforceability as the parent. While this may be acceptable, it may not
11776 * be ideal. Therefore, it's better to raise an error and allow the user
11777 * to correct the enforceability before proceeding.
11778 */
11779 if (partConstr->conenforced != parentConstr->conenforced)
11780 ereport(ERROR,
11781 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11782 errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11783 NameStr(parentConstr->conname),
11784 NameStr(partConstr->conname),
11785 RelationGetRelationName(partition))));
11786
11787 if (OidIsValid(partConstr->conparentid) ||
11788 partConstr->condeferrable != parentConstr->condeferrable ||
11789 partConstr->condeferred != parentConstr->condeferred ||
11790 partConstr->confupdtype != parentConstr->confupdtype ||
11791 partConstr->confdeltype != parentConstr->confdeltype ||
11792 partConstr->confmatchtype != parentConstr->confmatchtype)
11793 {
11794 ReleaseSysCache(parentConstrTup);
11795 ReleaseSysCache(partcontup);
11796 return false;
11797 }
11798
11799 ReleaseSysCache(parentConstrTup);
11800 ReleaseSysCache(partcontup);
11801
11802 /* Looks good! Attach this constraint. */
11803 AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11804 parentConstrOid, parentInsTrigger,
11805 parentUpdTrigger, trigrel);
11806
11807 return true;
11808}
static void AttachPartitionForeignKey(List **wqueue, Relation partition, Oid partConstrOid, Oid parentConstrOid, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11818

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

15944{
15945 HeapTuple tup;
15946 Datum adatum;
15947 ArrayType *arr;
15948 Oid *rawarr;
15949 int numkeys;
15950 int i;
15951
15952 Assert(con->contype == CONSTR_FOREIGN);
15953 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15954
15955 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15956 if (!HeapTupleIsValid(tup)) /* should not happen */
15957 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15958
15959 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15960 Anum_pg_constraint_conpfeqop);
15961 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15962 numkeys = ARR_DIMS(arr)[0];
15963 /* test follows the one in ri_FetchConstraintInfo() */
15964 if (ARR_NDIM(arr) != 1 ||
15965 ARR_HASNULL(arr) ||
15966 ARR_ELEMTYPE(arr) != OIDOID)
15967 elog(ERROR, "conpfeqop is not a 1-D Oid array");
15968 rawarr = (Oid *) ARR_DATA_PTR(arr);
15969
15970 /* stash a List of the operator Oids in our Constraint node */
15971 for (i = 0; i < numkeys; i++)
15972 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15973
15974 ReleaseSysCache(tup);
15975}
#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 15914 of file tablecmds.c.

15915{
15916 if (CheckIndexCompatible(oldId,
15917 stmt->accessMethod,
15918 stmt->indexParams,
15919 stmt->excludeOpNames,
15920 stmt->iswithoutoverlaps))
15921 {
15922 Relation irel = index_open(oldId, NoLock);
15923
15924 /* If it's a partitioned index, there is no storage to share. */
15925 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15926 {
15927 stmt->oldNumber = irel->rd_locator.relNumber;
15928 stmt->oldCreateSubid = irel->rd_createSubid;
15929 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15930 }
15931 index_close(irel, NoLock);
15932 }
15933}
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 10674 of file tablecmds.c.

10677{
10678 int numcolsout = 0;
10679
10680 for (int i = 0; i < numfksetcols; i++)
10681 {
10682 int16 setcol_attnum = fksetcolsattnums[i];
10683 bool seen = false;
10684
10685 /* Make sure it's in fkattnums[] */
10686 for (int j = 0; j < numfks; j++)
10687 {
10688 if (fkattnums[j] == setcol_attnum)
10689 {
10690 seen = true;
10691 break;
10692 }
10693 }
10694
10695 if (!seen)
10696 {
10697 char *col = strVal(list_nth(fksetcols, i));
10698
10699 ereport(ERROR,
10700 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10701 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10702 }
10703
10704 /* Now check for dups */
10705 seen = false;
10706 for (int j = 0; j < numcolsout; j++)
10707 {
10708 if (fksetcolsattnums[j] == setcol_attnum)
10709 {
10710 seen = true;
10711 break;
10712 }
10713 }
10714 if (!seen)
10715 fksetcolsattnums[numcolsout++] = setcol_attnum;
10716 }
10717 return numcolsout;
10718}

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

13728{
13729 TupleTableSlot *slot;
13730 TableScanDesc scan;
13731 Trigger trig = {0};
13732 Snapshot snapshot;
13733 MemoryContext oldcxt;
13734 MemoryContext perTupCxt;
13735
13737 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13738
13739 /*
13740 * Build a trigger call structure; we'll need it either way.
13741 */
13742 trig.tgoid = InvalidOid;
13743 trig.tgname = conname;
13745 trig.tgisinternal = true;
13746 trig.tgconstrrelid = RelationGetRelid(pkrel);
13747 trig.tgconstrindid = pkindOid;
13748 trig.tgconstraint = constraintOid;
13749 trig.tgdeferrable = false;
13750 trig.tginitdeferred = false;
13751 /* we needn't fill in remaining fields */
13752
13753 /*
13754 * See if we can do it with a single LEFT JOIN query. A false result
13755 * indicates we must proceed with the fire-the-trigger method. We can't do
13756 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13757 * left joins.
13758 */
13759 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13760 return;
13761
13762 /*
13763 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13764 * if that tuple had just been inserted. If any of those fail, it should
13765 * ereport(ERROR) and that's that.
13766 */
13767 snapshot = RegisterSnapshot(GetLatestSnapshot());
13768 slot = table_slot_create(rel, NULL);
13769 scan = table_beginscan(rel, snapshot, 0, NULL);
13770
13772 "validateForeignKeyConstraint",
13774 oldcxt = MemoryContextSwitchTo(perTupCxt);
13775
13776 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13777 {
13778 LOCAL_FCINFO(fcinfo, 0);
13779 TriggerData trigdata = {0};
13780
13782
13783 /*
13784 * Make a call to the trigger function
13785 *
13786 * No parameters are passed, but we do set a context
13787 */
13788 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13789
13790 /*
13791 * We assume RI_FKey_check_ins won't look at flinfo...
13792 */
13793 trigdata.type = T_TriggerData;
13795 trigdata.tg_relation = rel;
13796 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13797 trigdata.tg_trigslot = slot;
13798 trigdata.tg_trigger = &trig;
13799
13800 fcinfo->context = (Node *) &trigdata;
13801
13802 RI_FKey_check_ins(fcinfo);
13803
13804 MemoryContextReset(perTupCxt);
13805 }
13806
13807 MemoryContextSwitchTo(oldcxt);
13808 MemoryContextDelete(perTupCxt);
13809 table_endscan(scan);
13810 UnregisterSnapshot(snapshot);
13812}
#define MemSet(start, val, len)
Definition: c.h:1032
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1833
#define SizeForFunctionCallInfo(nargs)
Definition: fmgr.h:102
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
Datum RI_FKey_check_ins(PG_FUNCTION_ARGS)
Definition: ri_triggers.c:474
bool RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1519
NodeTag type
Definition: trigger.h:33
Relation tg_relation
Definition: trigger.h:35
TriggerEvent tg_event
Definition: trigger.h:34
TupleTableSlot * tg_trigslot
Definition: trigger.h:39
Trigger * tg_trigger
Definition: trigger.h:38
HeapTuple tg_trigtuple
Definition: trigger.h:36
#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 21808 of file tablecmds.c.

21809{
21810 Relation inheritsRel;
21811 SysScanDesc scan;
21813 int tuples = 0;
21814 HeapTuple inhTup;
21815 bool updated = false;
21816
21817 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21818
21819 /*
21820 * Scan pg_inherits for this parent index. Count each valid index we find
21821 * (verifying the pg_index entry for each), and if we reach the total
21822 * amount we expect, we can mark this parent index as valid.
21823 */
21824 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21825 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21826 BTEqualStrategyNumber, F_OIDEQ,
21828 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21829 NULL, 1, &key);
21830 while ((inhTup = systable_getnext(scan)) != NULL)
21831 {
21832 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21833 HeapTuple indTup;
21834 Form_pg_index indexForm;
21835
21836 indTup = SearchSysCache1(INDEXRELID,
21837 ObjectIdGetDatum(inhForm->inhrelid));
21838 if (!HeapTupleIsValid(indTup))
21839 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21840 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21841 if (indexForm->indisvalid)
21842 tuples += 1;
21843 ReleaseSysCache(indTup);
21844 }
21845
21846 /* Done with pg_inherits */
21847 systable_endscan(scan);
21848 table_close(inheritsRel, AccessShareLock);
21849
21850 /*
21851 * If we found as many inherited indexes as the partitioned table has
21852 * partitions, we're good; update pg_index to set indisvalid.
21853 */
21854 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21855 {
21856 Relation idxRel;
21857 HeapTuple indTup;
21858 Form_pg_index indexForm;
21859
21860 idxRel = table_open(IndexRelationId, RowExclusiveLock);
21861 indTup = SearchSysCacheCopy1(INDEXRELID,
21863 if (!HeapTupleIsValid(indTup))
21864 elog(ERROR, "cache lookup failed for index %u",
21865 RelationGetRelid(partedIdx));
21866 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21867
21868 indexForm->indisvalid = true;
21869 updated = true;
21870
21871 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21872
21874 heap_freetuple(indTup);
21875 }
21876
21877 /*
21878 * If this index is in turn a partition of a larger index, validating it
21879 * might cause the parent to become valid also. Try that.
21880 */
21881 if (updated && partedIdx->rd_rel->relispartition)
21882 {
21883 Oid parentIdxId,
21884 parentTblId;
21885 Relation parentIdx,
21886 parentTbl;
21887
21888 /* make sure we see the validation we just did */
21890
21891 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21892 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21893 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21894 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21895 Assert(!parentIdx->rd_index->indisvalid);
21896
21897 validatePartitionedIndex(parentIdx, parentTbl);
21898
21901 }
21902}

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

9607{
9609
9610 if (conForm->contype != CONSTRAINT_NOTNULL)
9611 elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9612
9613 /* a NO INHERIT constraint is no good */
9614 if (conForm->connoinherit)
9615 ereport(ERROR,
9616 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9617 errmsg("cannot create primary key on column \"%s\"", colname),
9618 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9619 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9620 NameStr(conForm->conname), colname,
9621 get_rel_name(conForm->conrelid), "NO INHERIT"),
9622 errhint("You might need to make the existing constraint inheritable using %s.",
9623 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9624
9625 /* an unvalidated constraint is no good */
9626 if (!conForm->convalidated)
9627 ereport(ERROR,
9628 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9629 errmsg("cannot create primary key on column \"%s\"", colname),
9630 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9631 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9632 NameStr(conForm->conname), colname,
9633 get_rel_name(conForm->conrelid), "NOT VALID"),
9634 errhint("You might need to validate it using %s.",
9635 "ALTER TABLE ... VALIDATE CONSTRAINT"));
9636}

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

21911{
21912 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21913 {
21915 iinfo->ii_IndexAttrNumbers[i] - 1);
21916
21917 if (!att->attnotnull)
21918 ereport(ERROR,
21919 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21920 errmsg("invalid primary key definition"),
21921 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21922 NameStr(att->attname),
21923 RelationGetRelationName(partition)));
21924 }
21925}
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