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

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

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

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

10759{
10760 ObjectAddress address;
10761 Oid constrOid;
10762 char *conname;
10763 bool conislocal;
10764 int16 coninhcount;
10765 bool connoinherit;
10766
10767 /*
10768 * Verify relkind for each referenced partition. At the top level, this
10769 * is redundant with a previous check, but we need it when recursing.
10770 */
10771 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10772 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10773 ereport(ERROR,
10774 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10775 errmsg("referenced relation \"%s\" is not a table",
10776 RelationGetRelationName(pkrel))));
10777
10778 /*
10779 * Caller supplies us with a constraint name; however, it may be used in
10780 * this partition, so come up with a different one in that case. Unless
10781 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10782 * supplied name with an underscore and digit(s) appended.
10783 */
10785 RelationGetRelid(rel),
10786 constraintname))
10787 conname = ChooseConstraintName(constraintname,
10788 NULL,
10789 "",
10791 else
10792 conname = constraintname;
10793
10794 if (fkconstraint->conname == NULL)
10795 fkconstraint->conname = pstrdup(conname);
10796
10797 if (OidIsValid(parentConstr))
10798 {
10799 conislocal = false;
10800 coninhcount = 1;
10801 connoinherit = false;
10802 }
10803 else
10804 {
10805 conislocal = true;
10806 coninhcount = 0;
10807
10808 /*
10809 * always inherit for partitioned tables, never for legacy inheritance
10810 */
10811 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10812 }
10813
10814 /*
10815 * Record the FK constraint in pg_constraint.
10816 */
10817 constrOid = CreateConstraintEntry(conname,
10819 CONSTRAINT_FOREIGN,
10820 fkconstraint->deferrable,
10821 fkconstraint->initdeferred,
10822 fkconstraint->is_enforced,
10823 fkconstraint->initially_valid,
10824 parentConstr,
10825 RelationGetRelid(rel),
10826 fkattnum,
10827 numfks,
10828 numfks,
10829 InvalidOid, /* not a domain constraint */
10830 indexOid,
10831 RelationGetRelid(pkrel),
10832 pkattnum,
10833 pfeqoperators,
10834 ppeqoperators,
10835 ffeqoperators,
10836 numfks,
10837 fkconstraint->fk_upd_action,
10838 fkconstraint->fk_del_action,
10839 fkdelsetcols,
10840 numfkdelsetcols,
10841 fkconstraint->fk_matchtype,
10842 NULL, /* no exclusion constraint */
10843 NULL, /* no check constraint */
10844 NULL,
10845 conislocal, /* islocal */
10846 coninhcount, /* inhcount */
10847 connoinherit, /* conNoInherit */
10848 with_period, /* conPeriod */
10849 is_internal); /* is_internal */
10850
10851 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10852
10853 /*
10854 * In partitioning cases, create the dependency entries for this
10855 * constraint. (For non-partitioned cases, relevant entries were created
10856 * by CreateConstraintEntry.)
10857 *
10858 * On the referenced side, we need the constraint to have an internal
10859 * dependency on its parent constraint; this means that this constraint
10860 * cannot be dropped on its own -- only through the parent constraint. It
10861 * also means the containing partition cannot be dropped on its own, but
10862 * it can be detached, at which point this dependency is removed (after
10863 * verifying that no rows are referenced via this FK.)
10864 *
10865 * When processing the referencing side, we link the constraint via the
10866 * special partitioning dependencies: the parent constraint is the primary
10867 * dependent, and the partition on which the foreign key exists is the
10868 * secondary dependency. That way, this constraint is dropped if either
10869 * of these objects is.
10870 *
10871 * Note that this is only necessary for the subsidiary pg_constraint rows
10872 * in partitions; the topmost row doesn't need any of this.
10873 */
10874 if (OidIsValid(parentConstr))
10875 {
10876 ObjectAddress referenced;
10877
10878 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10879
10880 Assert(fkside != addFkBothSides);
10881 if (fkside == addFkReferencedSide)
10882 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10883 else
10884 {
10885 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10886 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10887 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10888 }
10889 }
10890
10891 /* make new constraint visible, in case we add more */
10893
10894 return address;
10895}
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:1781
#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:2865
char fk_upd_action
Definition: parsenodes.h:2899
bool is_enforced
Definition: parsenodes.h:2866
char fk_matchtype
Definition: parsenodes.h:2898
bool initially_valid
Definition: parsenodes.h:2868
bool deferrable
Definition: parsenodes.h:2864
char * conname
Definition: parsenodes.h:2863
char fk_del_action
Definition: parsenodes.h:2900
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 10930 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

12727{
12728 HeapTuple tgtuple;
12729 ScanKeyData tgkey;
12730 SysScanDesc tgscan;
12731
12732 ScanKeyInit(&tgkey,
12733 Anum_pg_trigger_tgconstraint,
12734 BTEqualStrategyNumber, F_OIDEQ,
12735 ObjectIdGetDatum(conoid));
12736 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12737 NULL, 1, &tgkey);
12738 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12739 {
12740 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12741 Form_pg_trigger copy_tg;
12742 HeapTuple tgCopyTuple;
12743
12744 /*
12745 * Remember OIDs of other relation(s) involved in FK constraint.
12746 * (Note: it's likely that we could skip forcing a relcache inval for
12747 * other rels that don't have a trigger whose properties change, but
12748 * let's be conservative.)
12749 */
12750 if (tgform->tgrelid != RelationGetRelid(rel))
12751 *otherrelids = list_append_unique_oid(*otherrelids,
12752 tgform->tgrelid);
12753
12754 /*
12755 * Update enable status and deferrability of RI_FKey_noaction_del,
12756 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12757 * triggers, but not others; see createForeignKeyActionTriggers and
12758 * CreateFKCheckTrigger.
12759 */
12760 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12761 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12762 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12763 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12764 continue;
12765
12766 tgCopyTuple = heap_copytuple(tgtuple);
12767 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12768
12769 copy_tg->tgdeferrable = deferrable;
12770 copy_tg->tginitdeferred = initdeferred;
12771 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12772
12773 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12774
12775 heap_freetuple(tgCopyTuple);
12776 }
12777
12778 systable_endscan(tgscan);
12779}
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 12884 of file tablecmds.c.

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

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

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

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

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

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

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

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

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4606 of file tablecmds.c.

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

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

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

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4473 of file tablecmds.c.

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

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

Referenced by ProcessUtilitySlow().

◆ AlterTableMoveAll()

Oid AlterTableMoveAll ( AlterTableMoveAllStmt stmt)

Definition at line 17015 of file tablecmds.c.

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

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

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

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

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

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

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

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

14710{
14711 Assert(expr != NULL);
14712
14713 for (;;)
14714 {
14715 /* only one varno, so no need to check that */
14716 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14717 return false;
14718 else if (IsA(expr, RelabelType))
14719 expr = (Node *) ((RelabelType *) expr)->arg;
14720 else if (IsA(expr, CoerceToDomain))
14721 {
14722 CoerceToDomain *d = (CoerceToDomain *) expr;
14723
14725 return true;
14726 expr = (Node *) d->arg;
14727 }
14728 else if (IsA(expr, FuncExpr))
14729 {
14730 FuncExpr *f = (FuncExpr *) expr;
14731
14732 switch (f->funcid)
14733 {
14734 case F_TIMESTAMPTZ_TIMESTAMP:
14735 case F_TIMESTAMP_TIMESTAMPTZ:
14737 return true;
14738 else
14739 expr = linitial(f->args);
14740 break;
14741 default:
14742 return true;
14743 }
14744 }
14745 else
14746 return true;
14747 }
14748}
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 4873 of file tablecmds.c.

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

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

21989{
21990 List *constraints;
21991 ListCell *cell;
21992
21993 constraints = GetParentedForeignKeyRefs(partition);
21994
21995 foreach(cell, constraints)
21996 {
21997 Oid constrOid = lfirst_oid(cell);
21998 HeapTuple tuple;
21999 Form_pg_constraint constrForm;
22000 Relation rel;
22001 Trigger trig = {0};
22002
22003 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
22004 if (!HeapTupleIsValid(tuple))
22005 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22006 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
22007
22008 Assert(OidIsValid(constrForm->conparentid));
22009 Assert(constrForm->confrelid == RelationGetRelid(partition));
22010
22011 /* prevent data changes into the referencing table until commit */
22012 rel = table_open(constrForm->conrelid, ShareLock);
22013
22014 trig.tgoid = InvalidOid;
22015 trig.tgname = NameStr(constrForm->conname);
22017 trig.tgisinternal = true;
22018 trig.tgconstrrelid = RelationGetRelid(partition);
22019 trig.tgconstrindid = constrForm->conindid;
22020 trig.tgconstraint = constrForm->oid;
22021 trig.tgdeferrable = false;
22022 trig.tginitdeferred = false;
22023 /* we needn't fill in remaining fields */
22024
22025 RI_PartitionRemove_Check(&trig, rel, partition);
22026
22027 ReleaseSysCache(tuple);
22028
22029 table_close(rel, NoLock);
22030 }
22031}
#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:21935
#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 19487 of file tablecmds.c.

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

19456{
19457 ListCell *cur_item;
19458
19459 foreach(cur_item, on_commits)
19460 {
19461 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19462
19463 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19465 {
19466 /* cur_item must be removed */
19468 pfree(oc);
19469 }
19470 else
19471 {
19472 /* cur_item must be preserved */
19475 }
19476 }
19477}

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

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

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

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

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

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

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

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

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

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

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

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

9715{
9716 ObjectAddress address;
9717
9719
9720 /* The CreateStatsStmt has already been through transformStatsStmt */
9721 Assert(stmt->transformed);
9722
9723 address = CreateStatistics(stmt, !is_rebuild);
9724
9725 return address;
9726}
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 15985 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

16510{
16511 mark_index_clustered(rel, InvalidOid, false);
16512}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

◆ ATPostAlterTypeCleanup()

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

Definition at line 15466 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

22116{
22117 /*
22118 * Build the needed expression execution states. Here, we expect only NOT
22119 * NULL and CHECK constraint.
22120 */
22122 {
22123 switch (con->contype)
22124 {
22125 case CONSTR_CHECK:
22126
22127 /*
22128 * We already expanded virtual expression in
22129 * createTableConstraints.
22130 */
22131 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
22132 break;
22133 case CONSTR_NOTNULL:
22134 /* Nothing to do here. */
22135 break;
22136 default:
22137 elog(ERROR, "unrecognized constraint type: %d",
22138 (int) con->contype);
22139 }
22140 }
22141
22142 /* Expression already planned in createTableConstraints */
22144 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
22145}
#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 16343 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

13696{
13697 Oid roleid = GetUserId();
13698 AclResult aclresult;
13699 int i;
13700
13701 /* Okay if we have relation-level REFERENCES permission */
13702 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13704 if (aclresult == ACLCHECK_OK)
13705 return;
13706 /* Else we must have REFERENCES on each column */
13707 for (i = 0; i < natts; i++)
13708 {
13709 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13710 roleid, ACL_REFERENCES);
13711 if (aclresult != ACLCHECK_OK)
13712 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13714 }
13715}
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 3691 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

13891{
13892 CreateTrigStmt *fk_trigger;
13893 ObjectAddress trigAddress;
13894
13895 /*
13896 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13897 * DELETE action on the referenced table.
13898 */
13899 fk_trigger = makeNode(CreateTrigStmt);
13900 fk_trigger->replace = false;
13901 fk_trigger->isconstraint = true;
13902 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13903 fk_trigger->relation = NULL;
13904 fk_trigger->args = NIL;
13905 fk_trigger->row = true;
13906 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13907 fk_trigger->events = TRIGGER_TYPE_DELETE;
13908 fk_trigger->columns = NIL;
13909 fk_trigger->whenClause = NULL;
13910 fk_trigger->transitionRels = NIL;
13911 fk_trigger->constrrel = NULL;
13912
13913 switch (fkconstraint->fk_del_action)
13914 {
13916 fk_trigger->deferrable = fkconstraint->deferrable;
13917 fk_trigger->initdeferred = fkconstraint->initdeferred;
13918 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13919 break;
13921 fk_trigger->deferrable = false;
13922 fk_trigger->initdeferred = false;
13923 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13924 break;
13926 fk_trigger->deferrable = false;
13927 fk_trigger->initdeferred = false;
13928 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13929 break;
13931 fk_trigger->deferrable = false;
13932 fk_trigger->initdeferred = false;
13933 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13934 break;
13936 fk_trigger->deferrable = false;
13937 fk_trigger->initdeferred = false;
13938 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13939 break;
13940 default:
13941 elog(ERROR, "unrecognized FK action type: %d",
13942 (int) fkconstraint->fk_del_action);
13943 break;
13944 }
13945
13946 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13947 constraintOid, indexOid, InvalidOid,
13948 parentDelTrigger, NULL, true, false);
13949 if (deleteTrigOid)
13950 *deleteTrigOid = trigAddress.objectId;
13951
13952 /* Make changes-so-far visible */
13954
13955 /*
13956 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13957 * UPDATE action on the referenced table.
13958 */
13959 fk_trigger = makeNode(CreateTrigStmt);
13960 fk_trigger->replace = false;
13961 fk_trigger->isconstraint = true;
13962 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13963 fk_trigger->relation = NULL;
13964 fk_trigger->args = NIL;
13965 fk_trigger->row = true;
13966 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13967 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13968 fk_trigger->columns = NIL;
13969 fk_trigger->whenClause = NULL;
13970 fk_trigger->transitionRels = NIL;
13971 fk_trigger->constrrel = NULL;
13972
13973 switch (fkconstraint->fk_upd_action)
13974 {
13976 fk_trigger->deferrable = fkconstraint->deferrable;
13977 fk_trigger->initdeferred = fkconstraint->initdeferred;
13978 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13979 break;
13981 fk_trigger->deferrable = false;
13982 fk_trigger->initdeferred = false;
13983 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13984 break;
13986 fk_trigger->deferrable = false;
13987 fk_trigger->initdeferred = false;
13988 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13989 break;
13991 fk_trigger->deferrable = false;
13992 fk_trigger->initdeferred = false;
13993 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13994 break;
13996 fk_trigger->deferrable = false;
13997 fk_trigger->initdeferred = false;
13998 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13999 break;
14000 default:
14001 elog(ERROR, "unrecognized FK action type: %d",
14002 (int) fkconstraint->fk_upd_action);
14003 break;
14004 }
14005
14006 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
14007 constraintOid, indexOid, InvalidOid,
14008 parentUpdTrigger, NULL, true, false);
14009 if (updateTrigOid)
14010 *updateTrigOid = trigAddress.objectId;
14011}
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2848

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

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

References CreateFKCheckTrigger().

Referenced by addFkRecurseReferencing(), and ATExecAlterConstrEnforceability().

◆ CreateInheritance()

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

Definition at line 17402 of file tablecmds.c.

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

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

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

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

22947{
22949
22951 pc->partRel = partRel;
22952
22953 /*
22954 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
22955 * don't bother using it.
22956 */
22958
22959 /* Create a destination tuple slot for the new partition. */
22960 pc->dstslot = table_slot_create(pc->partRel, NULL);
22961
22962 return pc;
22963}
TupleTableSlot * dstslot
Definition: tablecmds.c:22936
BulkInsertState bistate
Definition: tablecmds.c:22935
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 22251 of file tablecmds.c.

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

17472{
17474 bool isnull;
17475 Datum attr;
17476 Datum expr;
17477
17478 con = (Form_pg_constraint) GETSTRUCT(contup);
17479 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17480 if (isnull)
17481 elog(ERROR, "null conbin for constraint %u", con->oid);
17482
17483 expr = DirectFunctionCall2(pg_get_expr, attr,
17484 ObjectIdGetDatum(con->conrelid));
17485 return TextDatumGetCString(expr);
17486}
#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);
1305 DefineIndex(NULL,
1306 RelationGetRelid(rel),
1307 idxstmt,
1308 InvalidOid,
1309 RelationGetRelid(idxRel),
1310 constraintOid,
1311 -1,
1312 false, false, false, false, false);
1313
1315 }
1316
1317 list_free(idxlist);
1318
1319 /*
1320 * If there are any row-level triggers, clone them to the new
1321 * partition.
1322 */
1323 if (parent->trigdesc != NULL)
1324 CloneRowTriggersToPartition(parent, rel);
1325
1326 /*
1327 * And foreign keys too. Note that because we're freshly creating the
1328 * table, there is no need to verify these new constraints.
1329 */
1330 CloneForeignKeyConstraints(NULL, parent, rel);
1331
1332 table_close(parent, NoLock);
1333 }
1334
1335 /*
1336 * Now add any newly specified CHECK constraints to the new relation. Same
1337 * as for defaults above, but these need to come after partitioning is set
1338 * up.
1339 */
1340 if (stmt->constraints)
1341 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1342 true, true, false, queryString);
1343
1344 /*
1345 * Finally, merge the not-null constraints that are declared directly with
1346 * those that come from parent relations (making sure to count inheritance
1347 * appropriately for each), create them, and set the attnotnull flag on
1348 * columns that don't yet have it.
1349 */
1350 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1351 old_notnulls);
1352 foreach_int(attrnum, nncols)
1353 set_attnotnull(NULL, rel, attrnum, true, false);
1354
1355 ObjectAddressSet(address, RelationRelationId, relationId);
1356
1357 /*
1358 * Clean up. We keep lock on new relation (although it shouldn't be
1359 * visible to anyone else anyway, until commit).
1360 */
1361 relation_close(rel, NoLock);
1362
1363 return address;
1364}
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:2250
Oid get_rel_tablespace(Oid relid)
Definition: lsyscache.c:2204
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:2831
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:19813
static void StoreCatalogInheritance(Oid relationId, List *supers, bool child_is_partition)
Definition: tablecmds.c:3519
static PartitionSpec * transformPartitionSpec(Relation rel, PartitionSpec *partspec)
Definition: tablecmds.c:19755
static List * MergeAttributes(List *columns, const List *supers, char relpersistence, bool is_partition, List **supconstr, List **supnotnulls)
Definition: tablecmds.c:2545

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

22970{
22971 ListCell *ltab;
22972
22975
22976 table_finish_bulk_insert(pc->partRel, ti_options);
22977
22978 /*
22979 * We don't need to process this pc->partRel so delete the ALTER TABLE
22980 * queue of it.
22981 */
22982 foreach(ltab, *wqueue)
22983 {
22984 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
22985
22986 if (tab->relid == RelationGetRelid(pc->partRel))
22987 {
22988 *wqueue = list_delete_cell(*wqueue, ltab);
22989 break;
22990 }
22991 }
22992
22993 pfree(pc);
22994}
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 21130 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

References i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 13666 of file tablecmds.c.

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

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

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

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

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

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

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

17178{
17179 SMgrRelation dstrel;
17180
17181 /*
17182 * Since we copy the file directly without looking at the shared buffers,
17183 * we'd better first flush out any pages of the source relation that are
17184 * in shared buffers. We assume no new changes will be made while we are
17185 * holding exclusive lock on the rel.
17186 */
17188
17189 /*
17190 * Create and copy all forks of the relation, and schedule unlinking of
17191 * old physical files.
17192 *
17193 * NOTE: any conflict in relfilenumber value will be caught in
17194 * RelationCreateStorage().
17195 */
17196 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17197
17198 /* copy main fork */
17200 rel->rd_rel->relpersistence);
17201
17202 /* copy those extra forks that exist */
17203 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17204 forkNum <= MAX_FORKNUM; forkNum++)
17205 {
17206 if (smgrexists(RelationGetSmgr(rel), forkNum))
17207 {
17208 smgrcreate(dstrel, forkNum, false);
17209
17210 /*
17211 * WAL log creation if the relation is persistent, or this is the
17212 * init fork of an unlogged relation.
17213 */
17214 if (RelationIsPermanent(rel) ||
17215 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17216 forkNum == INIT_FORKNUM))
17217 log_smgrcreate(&newrlocator, forkNum);
17218 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17219 rel->rd_rel->relpersistence);
17220 }
17221 }
17222
17223 /* drop old relation, and close new one */
17225 smgrclose(dstrel);
17226}
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:5037
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 17895 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

8119{
8120 NullTest *nnulltest = makeNode(NullTest);
8121
8122 nnulltest->arg = (Expr *) makeVar(1,
8123 attr->attnum,
8124 attr->atttypid,
8125 attr->atttypmod,
8126 attr->attcollation,
8127 0);
8128 nnulltest->nulltesttype = IS_NOT_NULL;
8129
8130 /*
8131 * argisrow = false is correct even for a composite column, because
8132 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8133 * case, just IS DISTINCT FROM NULL.
8134 */
8135 nnulltest->argisrow = false;
8136 nnulltest->location = -1;
8137
8138 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8139 {
8141 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8142 RelationGetRelationName(rel), NameStr(attr->attname))));
8143 return true;
8144 }
8145
8146 return false;
8147}
@ 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:20142

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Referenced by MergeChildAttribute(), and MergeInheritedAttribute().

◆ StoreCatalogInheritance()

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

Definition at line 3519 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

15917{
15918 if (CheckIndexCompatible(oldId,
15919 stmt->accessMethod,
15920 stmt->indexParams,
15921 stmt->excludeOpNames,
15922 stmt->iswithoutoverlaps))
15923 {
15924 Relation irel = index_open(oldId, NoLock);
15925
15926 /* If it's a partitioned index, there is no storage to share. */
15927 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15928 {
15929 stmt->oldNumber = irel->rd_locator.relNumber;
15930 stmt->oldCreateSubid = irel->rd_createSubid;
15931 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15932 }
15933 index_close(irel, NoLock);
15934 }
15935}
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
Definition: indexcmds.c:178

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

Referenced by ATPostAlterTypeParse().

◆ validateFkOnDeleteSetColumns()

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

Definition at line 10676 of file tablecmds.c.

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

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

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

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

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

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

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

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