PostgreSQL Source Code  git master
tablecmds.c File Reference
#include "postgres.h"
#include "access/attmap.h"
#include "access/genam.h"
#include "access/gist.h"
#include "access/heapam.h"
#include "access/heapam_xlog.h"
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/sysattr.h"
#include "access/tableam.h"
#include "access/toast_compression.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "access/xloginsert.h"
#include "catalog/catalog.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_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 "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 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 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_COL_ATTRS , 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
}
 

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)
 
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 (Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
 
static bool ATExecAlterConstrRecurse (Constraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, List **otherrelids, LOCKMODE lockmode)
 
static ObjectAddress ATExecValidateConstraint (List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
 
static int transformColumnNameList (Oid relId, List *colList, int16 *attnums, Oid *atttypids)
 
static int transformFkeyGetPrimaryKey (Relation pkrel, Oid *indexOid, List **attnamelist, int16 *attnums, Oid *atttypids, 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 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, LOCKMODE lockmode)
 
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 bool set_attnotnull (List **wqueue, Relation rel, AttrNumber attnum, bool recurse, LOCKMODE lockmode)
 
static ObjectAddress ATExecSetNotNull (List **wqueue, Relation rel, char *constrname, char *colName, bool recurse, bool recursing, List **readyRels, LOCKMODE lockmode)
 
static ObjectAddress ATExecSetAttNotNull (List **wqueue, Relation rel, const char *colName, 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, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static ObjectAddress ATExecAddIndex (AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
 
static ObjectAddress ATExecAddStatistics (AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
 
static ObjectAddress ATExecAddConstraint (List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
 
static char * ChooseForeignKeyConstraintNameAddition (List *colnames)
 
static ObjectAddress ATExecAddIndexConstraint (AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
 
static ObjectAddress ATAddCheckNNConstraint (List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
 
static ObjectAddress ATAddForeignKeyConstraint (List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
 
static ObjectAddress addFkRecurseReferenced (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, Oid parentDelTrigger, Oid parentUpdTrigger, bool with_period)
 
static void validateFkOnDeleteSetColumns (int numfks, const int16 *fkattnums, int numfksetcols, const int16 *fksetcolsattnums, List *fksetcols)
 
static void addFkRecurseReferencing (List **wqueue, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, LOCKMODE lockmode, Oid parentInsTrigger, Oid parentUpdTrigger, bool with_period)
 
static void CloneForeignKeyConstraints (List **wqueue, Relation parentRel, Relation partitionRel)
 
static void CloneFkReferenced (Relation parentRel, Relation partitionRel)
 
static void CloneFkReferencing (List **wqueue, Relation parentRel, Relation partRel)
 
static void createForeignKeyCheckTriggers (Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
 
static void createForeignKeyActionTriggers (Relation rel, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
 
static bool tryAttachPartitionForeignKey (ForeignKeyCacheInfo *fk, Oid partRelid, Oid parentConstrOid, int numfks, AttrNumber *mapped_conkey, AttrNumber *confkey, Oid *conpfeqop, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
 
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, List **readyRels, 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 newAccessMethod)
 
static bool ATPrepChangePersistence (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 void ATInheritAdjustNotNulls (Relation parent_rel, Relation child_rel, int inhcount)
 
static ObjectAddress ATExecAttachPartition (List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
 
static void AttachPartitionEnsureIndexes (List **wqueue, Relation rel, Relation attachrel)
 
static void QueuePartitionConstraintValidation (List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
 
static void CloneRowTriggersToPartition (Relation parent, Relation partition)
 
static void DetachAddConstraintIfNeeded (List **wqueue, Relation partRel)
 
static void DropClonedTriggersFromPartition (Oid partitionId)
 
static ObjectAddress ATExecDetachPartition (List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
 
static void DetachPartitionFinalize (Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
 
static ObjectAddress ATExecDetachPartitionFinalize (Relation rel, RangeVar *name)
 
static ObjectAddress ATExecAttachPartitionIdx (List **wqueue, Relation parentIdx, RangeVar *name)
 
static void validatePartitionedIndex (Relation partedIdx, Relation partedTbl)
 
static void refuseDupeIndexAttach (Relation parentIdx, Relation partIdx, Relation partitionTbl)
 
static void verifyPartitionIndexNotNull (IndexInfo *iinfo, Relation partIdx)
 
static ListGetParentedForeignKeyRefs (Relation partition)
 
static void ATDetachCheckNoForeignKeyRefs (Relation partition)
 
static char GetAttributeCompression (Oid atttypid, const char *compression)
 
static char GetAttributeStorage (Oid atttypid, const char *storagemode)
 
static void ATExecSplitPartition (List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
 
static void ATExecMergePartitions (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 SplitPartitionContextcreateSplitPartitionContext (Relation partRel)
 
static void deleteSplitPartitionContext (SplitPartitionContext *pc, int ti_options)
 
static void moveSplitTableRows (Relation rel, Relation splitRel, List *partlist, List *newPartRels, Oid defaultPartOid)
 
static void createPartitionTable (RangeVar *newPartName, RangeVar *modelRelName, AlterTableUtilityContext *context)
 
static void moveMergedTablesRows (Relation rel, List *mergingPartitionsList, Relation newPartRel)
 

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

◆ ATT_COMPOSITE_TYPE

#define ATT_COMPOSITE_TYPE   0x0010

Definition at line 330 of file tablecmds.c.

◆ ATT_FOREIGN_TABLE

#define ATT_FOREIGN_TABLE   0x0020

Definition at line 331 of file tablecmds.c.

◆ ATT_INDEX

#define ATT_INDEX   0x0008

Definition at line 329 of file tablecmds.c.

◆ ATT_MATVIEW

#define ATT_MATVIEW   0x0004

Definition at line 328 of file tablecmds.c.

◆ ATT_PARTITIONED_INDEX

#define ATT_PARTITIONED_INDEX   0x0040

Definition at line 332 of file tablecmds.c.

◆ ATT_SEQUENCE

#define ATT_SEQUENCE   0x0080

Definition at line 333 of file tablecmds.c.

◆ ATT_TABLE

#define ATT_TABLE   0x0001

Definition at line 326 of file tablecmds.c.

◆ ATT_VIEW

#define ATT_VIEW   0x0002

Definition at line 327 of file tablecmds.c.

◆ child_dependency_type

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

Definition at line 354 of file tablecmds.c.

Typedef Documentation

◆ AlteredTableInfo

◆ AlterTablePass

◆ ForeignTruncateInfo

◆ NewColumnValue

◆ NewConstraint

typedef struct NewConstraint NewConstraint

◆ OnCommitItem

typedef struct OnCommitItem OnCommitItem

◆ SplitPartitionContext

Enumeration Type Documentation

◆ AlterTablePass

Enumerator
AT_PASS_UNSET 
AT_PASS_DROP 
AT_PASS_ALTER_TYPE 
AT_PASS_ADD_COL 
AT_PASS_SET_EXPRESSION 
AT_PASS_OLD_COL_ATTRS 
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 145 of file tablecmds.c.

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

Function Documentation

◆ add_column_collation_dependency()

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

Definition at line 7545 of file tablecmds.c.

7546 {
7547  ObjectAddress myself,
7548  referenced;
7549 
7550  /* We know the default collation is pinned, so don't bother recording it */
7551  if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7552  {
7553  myself.classId = RelationRelationId;
7554  myself.objectId = relid;
7555  myself.objectSubId = attnum;
7556  referenced.classId = CollationRelationId;
7557  referenced.objectId = collid;
7558  referenced.objectSubId = 0;
7559  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7560  }
7561 }
#define OidIsValid(objectId)
Definition: c.h:775
Oid collid
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
int16 attnum
Definition: pg_attribute.h:74
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44

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

7528 {
7529  ObjectAddress myself,
7530  referenced;
7531 
7532  myself.classId = RelationRelationId;
7533  myself.objectId = relid;
7534  myself.objectSubId = attnum;
7535  referenced.classId = TypeRelationId;
7536  referenced.objectId = typid;
7537  referenced.objectSubId = 0;
7538  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7539 }

References attnum, ObjectAddress::classId, DEPENDENCY_NORMAL, ObjectAddress::objectId, ObjectAddress::objectSubId, and recordDependencyOn().

Referenced by ATExecAddColumn(), and ATExecAlterColumnType().

◆ addFkRecurseReferenced()

static ObjectAddress addFkRecurseReferenced ( 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,
Oid  parentDelTrigger,
Oid  parentUpdTrigger,
bool  with_period 
)
static

Definition at line 10482 of file tablecmds.c.

10491 {
10492  ObjectAddress address;
10493  Oid constrOid;
10494  char *conname;
10495  bool conislocal;
10496  int coninhcount;
10497  bool connoinherit;
10498  Oid deleteTriggerOid,
10499  updateTriggerOid;
10500 
10501  /*
10502  * Verify relkind for each referenced partition. At the top level, this
10503  * is redundant with a previous check, but we need it when recursing.
10504  */
10505  if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10506  pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10507  ereport(ERROR,
10508  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10509  errmsg("referenced relation \"%s\" is not a table",
10510  RelationGetRelationName(pkrel))));
10511 
10512  /*
10513  * Caller supplies us with a constraint name; however, it may be used in
10514  * this partition, so come up with a different one in that case.
10515  */
10517  RelationGetRelid(rel),
10518  fkconstraint->conname))
10521  "fkey",
10522  RelationGetNamespace(rel), NIL);
10523  else
10524  conname = fkconstraint->conname;
10525 
10526  if (OidIsValid(parentConstr))
10527  {
10528  conislocal = false;
10529  coninhcount = 1;
10530  connoinherit = false;
10531  }
10532  else
10533  {
10534  conislocal = true;
10535  coninhcount = 0;
10536 
10537  /*
10538  * always inherit for partitioned tables, never for legacy inheritance
10539  */
10540  connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10541  }
10542 
10543  /*
10544  * Record the FK constraint in pg_constraint.
10545  */
10546  constrOid = CreateConstraintEntry(conname,
10547  RelationGetNamespace(rel),
10548  CONSTRAINT_FOREIGN,
10549  fkconstraint->deferrable,
10550  fkconstraint->initdeferred,
10551  fkconstraint->initially_valid,
10552  parentConstr,
10553  RelationGetRelid(rel),
10554  fkattnum,
10555  numfks,
10556  numfks,
10557  InvalidOid, /* not a domain constraint */
10558  indexOid,
10559  RelationGetRelid(pkrel),
10560  pkattnum,
10561  pfeqoperators,
10562  ppeqoperators,
10563  ffeqoperators,
10564  numfks,
10565  fkconstraint->fk_upd_action,
10566  fkconstraint->fk_del_action,
10567  fkdelsetcols,
10568  numfkdelsetcols,
10569  fkconstraint->fk_matchtype,
10570  NULL, /* no exclusion constraint */
10571  NULL, /* no check constraint */
10572  NULL,
10573  conislocal, /* islocal */
10574  coninhcount, /* inhcount */
10575  connoinherit, /* conNoInherit */
10576  with_period, /* conPeriod */
10577  false); /* is_internal */
10578 
10579  ObjectAddressSet(address, ConstraintRelationId, constrOid);
10580 
10581  /*
10582  * Mark the child constraint as part of the parent constraint; it must not
10583  * be dropped on its own. (This constraint is deleted when the partition
10584  * is detached, but a special check needs to occur that the partition
10585  * contains no referenced values.)
10586  */
10587  if (OidIsValid(parentConstr))
10588  {
10589  ObjectAddress referenced;
10590 
10591  ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10592  recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10593  }
10594 
10595  /* make new constraint visible, in case we add more */
10597 
10598  /*
10599  * Create the action triggers that enforce the constraint.
10600  */
10602  fkconstraint,
10603  constrOid, indexOid,
10604  parentDelTrigger, parentUpdTrigger,
10605  &deleteTriggerOid, &updateTriggerOid);
10606 
10607  /*
10608  * If the referenced table is partitioned, recurse on ourselves to handle
10609  * each partition. We need one pg_constraint row created for each
10610  * partition in addition to the pg_constraint row for the parent table.
10611  */
10612  if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10613  {
10614  PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10615 
10616  for (int i = 0; i < pd->nparts; i++)
10617  {
10618  Relation partRel;
10619  AttrMap *map;
10620  AttrNumber *mapped_pkattnum;
10621  Oid partIndexId;
10622 
10623  partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10624 
10625  /*
10626  * Map the attribute numbers in the referenced side of the FK
10627  * definition to match the partition's column layout.
10628  */
10630  RelationGetDescr(pkrel),
10631  false);
10632  if (map)
10633  {
10634  mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10635  for (int j = 0; j < numfks; j++)
10636  mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10637  }
10638  else
10639  mapped_pkattnum = pkattnum;
10640 
10641  /* do the deed */
10642  partIndexId = index_get_partition(partRel, indexOid);
10643  if (!OidIsValid(partIndexId))
10644  elog(ERROR, "index for %u not found in partition %s",
10645  indexOid, RelationGetRelationName(partRel));
10646  addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
10647  partIndexId, constrOid, numfks,
10648  mapped_pkattnum, fkattnum,
10649  pfeqoperators, ppeqoperators, ffeqoperators,
10650  numfkdelsetcols, fkdelsetcols,
10651  old_check_ok,
10652  deleteTriggerOid, updateTriggerOid,
10653  with_period);
10654 
10655  /* Done -- clean up (but keep the lock) */
10656  table_close(partRel, NoLock);
10657  if (map)
10658  {
10659  pfree(mapped_pkattnum);
10660  free_attrmap(map);
10661  }
10662  }
10663  }
10664 
10665  return address;
10666 }
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:263
int16 AttrNumber
Definition: attnum.h:21
@ DEPENDENCY_INTERNAL
Definition: dependency.h:35
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
int j
Definition: isn.c:74
int i
Definition: isn.c:73
#define NoLock
Definition: lockdefs.h:34
#define ShareRowExclusiveLock
Definition: lockdefs.h:41
void pfree(void *pointer)
Definition: mcxt.c:1520
void * palloc(Size size)
Definition: mcxt.c:1316
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:70
Oid index_get_partition(Relation partition, Oid indexId)
Definition: partition.c:176
char * ChooseConstraintName(const char *name1, const char *name2, const char *label, Oid namespaceid, List *others)
bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, const char *conname)
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, 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, int conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
Definition: pg_constraint.c:51
@ CONSTRAINT_RELATION
#define NIL
Definition: pg_list.h:68
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetRelid(relation)
Definition: rel.h:505
#define RelationGetDescr(relation)
Definition: rel.h:531
#define RelationGetRelationName(relation)
Definition: rel.h:539
#define RelationGetNamespace(relation)
Definition: rel.h:546
Definition: attmap.h:35
AttrNumber * attnums
Definition: attmap.h:36
bool initdeferred
Definition: parsenodes.h:2742
char fk_upd_action
Definition: parsenodes.h:2776
char fk_matchtype
Definition: parsenodes.h:2775
bool initially_valid
Definition: parsenodes.h:2744
bool deferrable
Definition: parsenodes.h:2741
char * conname
Definition: parsenodes.h:2740
char fk_del_action
Definition: parsenodes.h:2777
List * fk_attrs
Definition: parsenodes.h:2771
Form_pg_class rd_rel
Definition: rel.h:111
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition: tablecmds.c:9686
static ObjectAddress addFkRecurseReferenced(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, Oid parentDelTrigger, Oid parentUpdTrigger, bool with_period)
Definition: tablecmds.c:10482
static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:12716
void CommandCounterIncrement(void)
Definition: xact.c:1097

References AttrMap::attnums, build_attrmap_by_name_if_req(), ChooseConstraintName(), ChooseForeignKeyConstraintNameAddition(), CommandCounterIncrement(), Constraint::conname, CONSTRAINT_RELATION, ConstraintNameIsUsed(), CreateConstraintEntry(), createForeignKeyActionTriggers(), Constraint::deferrable, DEPENDENCY_INTERNAL, elog, ereport, errcode(), errmsg(), ERROR, Constraint::fk_attrs, Constraint::fk_del_action, Constraint::fk_matchtype, Constraint::fk_upd_action, free_attrmap(), i, index_get_partition(), Constraint::initdeferred, Constraint::initially_valid, InvalidOid, j, NIL, NoLock, PartitionDescData::nparts, ObjectAddressSet, OidIsValid, PartitionDescData::oids, palloc(), pfree(), RelationData::rd_rel, recordDependencyOn(), RelationGetDescr, RelationGetNamespace, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, ShareRowExclusiveLock, table_close(), and table_open().

Referenced by ATAddForeignKeyConstraint(), and CloneFkReferenced().

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

10713 {
10714  Oid insertTriggerOid,
10715  updateTriggerOid;
10716 
10717  Assert(OidIsValid(parentConstr));
10718 
10719  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10720  ereport(ERROR,
10721  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10722  errmsg("foreign key constraints are not supported on foreign tables")));
10723 
10724  /*
10725  * Add the check triggers to it and, if necessary, schedule it to be
10726  * checked in Phase 3.
10727  *
10728  * If the relation is partitioned, drill down to do it to its partitions.
10729  */
10731  RelationGetRelid(pkrel),
10732  fkconstraint,
10733  parentConstr,
10734  indexOid,
10735  parentInsTrigger, parentUpdTrigger,
10736  &insertTriggerOid, &updateTriggerOid);
10737 
10738  if (rel->rd_rel->relkind == RELKIND_RELATION)
10739  {
10740  /*
10741  * Tell Phase 3 to check that the constraint is satisfied by existing
10742  * rows. We can skip this during table creation, when requested
10743  * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10744  * and when we're recreating a constraint following a SET DATA TYPE
10745  * operation that did not impugn its validity.
10746  */
10747  if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10748  {
10749  NewConstraint *newcon;
10750  AlteredTableInfo *tab;
10751 
10752  tab = ATGetQueueEntry(wqueue, rel);
10753 
10754  newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10755  newcon->name = get_constraint_name(parentConstr);
10756  newcon->contype = CONSTR_FOREIGN;
10757  newcon->refrelid = RelationGetRelid(pkrel);
10758  newcon->refindid = indexOid;
10759  newcon->conid = parentConstr;
10760  newcon->conwithperiod = fkconstraint->fk_with_period;
10761  newcon->qual = (Node *) fkconstraint;
10762 
10763  tab->constraints = lappend(tab->constraints, newcon);
10764  }
10765  }
10766  else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10767  {
10768  PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10769  Relation trigrel;
10770 
10771  /*
10772  * Triggers of the foreign keys will be manipulated a bunch of times
10773  * in the loop below. To avoid repeatedly opening/closing the trigger
10774  * catalog relation, we open it here and pass it to the subroutines
10775  * called below.
10776  */
10777  trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10778 
10779  /*
10780  * Recurse to take appropriate action on each partition; either we
10781  * find an existing constraint to reparent to ours, or we create a new
10782  * one.
10783  */
10784  for (int i = 0; i < pd->nparts; i++)
10785  {
10786  Oid partitionId = pd->oids[i];
10787  Relation partition = table_open(partitionId, lockmode);
10788  List *partFKs;
10789  AttrMap *attmap;
10790  AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10791  bool attached;
10792  char *conname;
10793  Oid constrOid;
10794  ObjectAddress address,
10795  referenced;
10796  ListCell *cell;
10797 
10798  CheckTableNotInUse(partition, "ALTER TABLE");
10799 
10800  attmap = build_attrmap_by_name(RelationGetDescr(partition),
10801  RelationGetDescr(rel),
10802  false);
10803  for (int j = 0; j < numfks; j++)
10804  mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10805 
10806  /* Check whether an existing constraint can be repurposed */
10807  partFKs = copyObject(RelationGetFKeyList(partition));
10808  attached = false;
10809  foreach(cell, partFKs)
10810  {
10811  ForeignKeyCacheInfo *fk;
10812 
10813  fk = lfirst_node(ForeignKeyCacheInfo, cell);
10815  partitionId,
10816  parentConstr,
10817  numfks,
10818  mapped_fkattnum,
10819  pkattnum,
10820  pfeqoperators,
10821  insertTriggerOid,
10822  updateTriggerOid,
10823  trigrel))
10824  {
10825  attached = true;
10826  break;
10827  }
10828  }
10829  if (attached)
10830  {
10831  table_close(partition, NoLock);
10832  continue;
10833  }
10834 
10835  /*
10836  * No luck finding a good constraint to reuse; create our own.
10837  */
10839  RelationGetRelid(partition),
10840  fkconstraint->conname))
10841  conname = ChooseConstraintName(RelationGetRelationName(partition),
10843  "fkey",
10844  RelationGetNamespace(partition), NIL);
10845  else
10846  conname = fkconstraint->conname;
10847  constrOid =
10848  CreateConstraintEntry(conname,
10849  RelationGetNamespace(partition),
10850  CONSTRAINT_FOREIGN,
10851  fkconstraint->deferrable,
10852  fkconstraint->initdeferred,
10853  fkconstraint->initially_valid,
10854  parentConstr,
10855  partitionId,
10856  mapped_fkattnum,
10857  numfks,
10858  numfks,
10859  InvalidOid,
10860  indexOid,
10861  RelationGetRelid(pkrel),
10862  pkattnum,
10863  pfeqoperators,
10864  ppeqoperators,
10865  ffeqoperators,
10866  numfks,
10867  fkconstraint->fk_upd_action,
10868  fkconstraint->fk_del_action,
10869  fkdelsetcols,
10870  numfkdelsetcols,
10871  fkconstraint->fk_matchtype,
10872  NULL,
10873  NULL,
10874  NULL,
10875  false,
10876  1,
10877  false,
10878  with_period, /* conPeriod */
10879  false);
10880 
10881  /*
10882  * Give this constraint partition-type dependencies on the parent
10883  * constraint as well as the table.
10884  */
10885  ObjectAddressSet(address, ConstraintRelationId, constrOid);
10886  ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10887  recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10888  ObjectAddressSet(referenced, RelationRelationId, partitionId);
10889  recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10890 
10891  /* Make all this visible before recursing */
10893 
10894  /* call ourselves to finalize the creation and we're done */
10895  addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10896  indexOid,
10897  constrOid,
10898  numfks,
10899  pkattnum,
10900  mapped_fkattnum,
10901  pfeqoperators,
10902  ppeqoperators,
10903  ffeqoperators,
10904  numfkdelsetcols,
10905  fkdelsetcols,
10906  old_check_ok,
10907  lockmode,
10908  insertTriggerOid,
10909  updateTriggerOid,
10910  with_period);
10911 
10912  table_close(partition, NoLock);
10913  }
10914 
10915  table_close(trigrel, RowExclusiveLock);
10916  }
10917 }
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:177
#define Assert(condition)
Definition: c.h:858
@ DEPENDENCY_PARTITION_PRI
Definition: dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition: dependency.h:37
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:1081
void * palloc0(Size size)
Definition: mcxt.c:1346
#define copyObject(obj)
Definition: nodes.h:224
@ CONSTR_FOREIGN
Definition: parsenodes.h:2717
#define INDEX_MAX_KEYS
#define lfirst_node(type, lc)
Definition: pg_list.h:176
List * RelationGetFKeyList(Relation relation)
Definition: relcache.c:4651
List * constraints
Definition: tablecmds.c:185
bool fk_with_period
Definition: parsenodes.h:2773
bool skip_validation
Definition: parsenodes.h:2743
Definition: pg_list.h:54
char * name
Definition: tablecmds.c:214
ConstrType contype
Definition: tablecmds.c:215
bool conwithperiod
Definition: tablecmds.c:218
Node * qual
Definition: tablecmds.c:220
Definition: nodes.h:129
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition: tablecmds.c:6429
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:10705
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4346
static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk, Oid partRelid, Oid parentConstrOid, int numfks, AttrNumber *mapped_conkey, AttrNumber *confkey, Oid *conpfeqop, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11442
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:12853

References Assert, ATGetQueueEntry(), AttrMap::attnums, build_attrmap_by_name(), CheckTableNotInUse(), ChooseConstraintName(), ChooseForeignKeyConstraintNameAddition(), CommandCounterIncrement(), NewConstraint::conid, Constraint::conname, CONSTR_FOREIGN, CONSTRAINT_RELATION, ConstraintNameIsUsed(), AlteredTableInfo::constraints, NewConstraint::contype, NewConstraint::conwithperiod, copyObject, CreateConstraintEntry(), createForeignKeyCheckTriggers(), Constraint::deferrable, DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, ereport, errcode(), errmsg(), ERROR, Constraint::fk_attrs, Constraint::fk_del_action, Constraint::fk_matchtype, Constraint::fk_upd_action, Constraint::fk_with_period, get_constraint_name(), i, INDEX_MAX_KEYS, Constraint::initdeferred, Constraint::initially_valid, InvalidOid, j, lappend(), lfirst_node, NewConstraint::name, NIL, NoLock, PartitionDescData::nparts, ObjectAddressSet, OidIsValid, PartitionDescData::oids, palloc0(), NewConstraint::qual, RelationData::rd_rel, recordDependencyOn(), NewConstraint::refindid, NewConstraint::refrelid, RelationGetDescr, RelationGetFKeyList(), RelationGetNamespace, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, Constraint::skip_validation, table_close(), table_open(), and tryAttachPartitionForeignKey().

Referenced by ATAddForeignKeyConstraint(), and CloneFkReferencing().

◆ alter_table_type_to_string()

static const char* alter_table_type_to_string ( AlterTableType  cmdtype)
static

Definition at line 6463 of file tablecmds.c.

6464 {
6465  switch (cmdtype)
6466  {
6467  case AT_AddColumn:
6468  case AT_AddColumnToView:
6469  return "ADD COLUMN";
6470  case AT_ColumnDefault:
6472  return "ALTER COLUMN ... SET DEFAULT";
6473  case AT_DropNotNull:
6474  return "ALTER COLUMN ... DROP NOT NULL";
6475  case AT_SetNotNull:
6476  return "ALTER COLUMN ... SET NOT NULL";
6477  case AT_SetAttNotNull:
6478  return NULL; /* not real grammar */
6479  case AT_SetExpression:
6480  return "ALTER COLUMN ... SET EXPRESSION";
6481  case AT_DropExpression:
6482  return "ALTER COLUMN ... DROP EXPRESSION";
6483  case AT_SetStatistics:
6484  return "ALTER COLUMN ... SET STATISTICS";
6485  case AT_SetOptions:
6486  return "ALTER COLUMN ... SET";
6487  case AT_ResetOptions:
6488  return "ALTER COLUMN ... RESET";
6489  case AT_SetStorage:
6490  return "ALTER COLUMN ... SET STORAGE";
6491  case AT_SetCompression:
6492  return "ALTER COLUMN ... SET COMPRESSION";
6493  case AT_DropColumn:
6494  return "DROP COLUMN";
6495  case AT_AddIndex:
6496  case AT_ReAddIndex:
6497  return NULL; /* not real grammar */
6498  case AT_AddConstraint:
6499  case AT_ReAddConstraint:
6501  case AT_AddIndexConstraint:
6502  return "ADD CONSTRAINT";
6503  case AT_AlterConstraint:
6504  return "ALTER CONSTRAINT";
6505  case AT_ValidateConstraint:
6506  return "VALIDATE CONSTRAINT";
6507  case AT_DropConstraint:
6508  return "DROP CONSTRAINT";
6509  case AT_ReAddComment:
6510  return NULL; /* not real grammar */
6511  case AT_AlterColumnType:
6512  return "ALTER COLUMN ... SET DATA TYPE";
6514  return "ALTER COLUMN ... OPTIONS";
6515  case AT_ChangeOwner:
6516  return "OWNER TO";
6517  case AT_ClusterOn:
6518  return "CLUSTER ON";
6519  case AT_DropCluster:
6520  return "SET WITHOUT CLUSTER";
6521  case AT_SetAccessMethod:
6522  return "SET ACCESS METHOD";
6523  case AT_SetLogged:
6524  return "SET LOGGED";
6525  case AT_SetUnLogged:
6526  return "SET UNLOGGED";
6527  case AT_DropOids:
6528  return "SET WITHOUT OIDS";
6529  case AT_SetTableSpace:
6530  return "SET TABLESPACE";
6531  case AT_SetRelOptions:
6532  return "SET";
6533  case AT_ResetRelOptions:
6534  return "RESET";
6535  case AT_ReplaceRelOptions:
6536  return NULL; /* not real grammar */
6537  case AT_EnableTrig:
6538  return "ENABLE TRIGGER";
6539  case AT_EnableAlwaysTrig:
6540  return "ENABLE ALWAYS TRIGGER";
6541  case AT_EnableReplicaTrig:
6542  return "ENABLE REPLICA TRIGGER";
6543  case AT_DisableTrig:
6544  return "DISABLE TRIGGER";
6545  case AT_EnableTrigAll:
6546  return "ENABLE TRIGGER ALL";
6547  case AT_DisableTrigAll:
6548  return "DISABLE TRIGGER ALL";
6549  case AT_EnableTrigUser:
6550  return "ENABLE TRIGGER USER";
6551  case AT_DisableTrigUser:
6552  return "DISABLE TRIGGER USER";
6553  case AT_EnableRule:
6554  return "ENABLE RULE";
6555  case AT_EnableAlwaysRule:
6556  return "ENABLE ALWAYS RULE";
6557  case AT_EnableReplicaRule:
6558  return "ENABLE REPLICA RULE";
6559  case AT_DisableRule:
6560  return "DISABLE RULE";
6561  case AT_AddInherit:
6562  return "INHERIT";
6563  case AT_DropInherit:
6564  return "NO INHERIT";
6565  case AT_AddOf:
6566  return "OF";
6567  case AT_DropOf:
6568  return "NOT OF";
6569  case AT_ReplicaIdentity:
6570  return "REPLICA IDENTITY";
6571  case AT_EnableRowSecurity:
6572  return "ENABLE ROW SECURITY";
6573  case AT_DisableRowSecurity:
6574  return "DISABLE ROW SECURITY";
6575  case AT_ForceRowSecurity:
6576  return "FORCE ROW SECURITY";
6577  case AT_NoForceRowSecurity:
6578  return "NO FORCE ROW SECURITY";
6579  case AT_GenericOptions:
6580  return "OPTIONS";
6581  case AT_AttachPartition:
6582  return "ATTACH PARTITION";
6583  case AT_DetachPartition:
6584  return "DETACH PARTITION";
6586  return "DETACH PARTITION ... FINALIZE";
6587  case AT_SplitPartition:
6588  return "SPLIT PARTITION";
6589  case AT_MergePartitions:
6590  return "MERGE PARTITIONS";
6591  case AT_AddIdentity:
6592  return "ALTER COLUMN ... ADD IDENTITY";
6593  case AT_SetIdentity:
6594  return "ALTER COLUMN ... SET";
6595  case AT_DropIdentity:
6596  return "ALTER COLUMN ... DROP IDENTITY";
6597  case AT_ReAddStatistics:
6598  return NULL; /* not real grammar */
6599  }
6600 
6601  return NULL;
6602 }
@ AT_AddIndexConstraint
Definition: parsenodes.h:2377
@ AT_MergePartitions
Definition: parsenodes.h:2419
@ AT_DropOf
Definition: parsenodes.h:2408
@ AT_SetOptions
Definition: parsenodes.h:2365
@ AT_DropIdentity
Definition: parsenodes.h:2422
@ AT_SetAttNotNull
Definition: parsenodes.h:2361
@ AT_DisableTrigUser
Definition: parsenodes.h:2400
@ AT_DropNotNull
Definition: parsenodes.h:2359
@ AT_AddOf
Definition: parsenodes.h:2407
@ AT_ResetOptions
Definition: parsenodes.h:2366
@ AT_ReplicaIdentity
Definition: parsenodes.h:2409
@ AT_ReplaceRelOptions
Definition: parsenodes.h:2392
@ AT_EnableRowSecurity
Definition: parsenodes.h:2410
@ AT_AddColumnToView
Definition: parsenodes.h:2356
@ AT_ResetRelOptions
Definition: parsenodes.h:2391
@ AT_EnableReplicaTrig
Definition: parsenodes.h:2395
@ AT_DropOids
Definition: parsenodes.h:2387
@ AT_SetIdentity
Definition: parsenodes.h:2421
@ AT_ReAddStatistics
Definition: parsenodes.h:2423
@ AT_SetUnLogged
Definition: parsenodes.h:2386
@ AT_DisableTrig
Definition: parsenodes.h:2396
@ AT_SetCompression
Definition: parsenodes.h:2368
@ AT_DropExpression
Definition: parsenodes.h:2363
@ AT_AddIndex
Definition: parsenodes.h:2370
@ AT_EnableReplicaRule
Definition: parsenodes.h:2403
@ AT_ReAddIndex
Definition: parsenodes.h:2371
@ AT_DropConstraint
Definition: parsenodes.h:2378
@ AT_SetNotNull
Definition: parsenodes.h:2360
@ AT_ClusterOn
Definition: parsenodes.h:2383
@ AT_AddIdentity
Definition: parsenodes.h:2420
@ AT_ForceRowSecurity
Definition: parsenodes.h:2412
@ AT_EnableAlwaysRule
Definition: parsenodes.h:2402
@ AT_SetAccessMethod
Definition: parsenodes.h:2388
@ AT_AlterColumnType
Definition: parsenodes.h:2380
@ AT_DetachPartitionFinalize
Definition: parsenodes.h:2417
@ AT_AddInherit
Definition: parsenodes.h:2405
@ AT_ReAddDomainConstraint
Definition: parsenodes.h:2374
@ AT_EnableTrig
Definition: parsenodes.h:2393
@ AT_DropColumn
Definition: parsenodes.h:2369
@ AT_ReAddComment
Definition: parsenodes.h:2379
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:2381
@ AT_DisableTrigAll
Definition: parsenodes.h:2398
@ AT_EnableRule
Definition: parsenodes.h:2401
@ AT_NoForceRowSecurity
Definition: parsenodes.h:2413
@ AT_DetachPartition
Definition: parsenodes.h:2416
@ AT_SetStatistics
Definition: parsenodes.h:2364
@ AT_AttachPartition
Definition: parsenodes.h:2415
@ AT_AddConstraint
Definition: parsenodes.h:2372
@ AT_DropInherit
Definition: parsenodes.h:2406
@ AT_EnableAlwaysTrig
Definition: parsenodes.h:2394
@ AT_SetLogged
Definition: parsenodes.h:2385
@ AT_SetStorage
Definition: parsenodes.h:2367
@ AT_DisableRule
Definition: parsenodes.h:2404
@ AT_DisableRowSecurity
Definition: parsenodes.h:2411
@ AT_SetRelOptions
Definition: parsenodes.h:2390
@ AT_ChangeOwner
Definition: parsenodes.h:2382
@ AT_EnableTrigUser
Definition: parsenodes.h:2399
@ AT_SetExpression
Definition: parsenodes.h:2362
@ AT_ReAddConstraint
Definition: parsenodes.h:2373
@ AT_SetTableSpace
Definition: parsenodes.h:2389
@ AT_GenericOptions
Definition: parsenodes.h:2414
@ AT_ColumnDefault
Definition: parsenodes.h:2357
@ AT_CookedColumnDefault
Definition: parsenodes.h:2358
@ AT_AlterConstraint
Definition: parsenodes.h:2375
@ AT_EnableTrigAll
Definition: parsenodes.h:2397
@ AT_SplitPartition
Definition: parsenodes.h:2418
@ AT_DropCluster
Definition: parsenodes.h:2384
@ AT_ValidateConstraint
Definition: parsenodes.h:2376
@ AT_AddColumn
Definition: parsenodes.h:2355

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

◆ AlterIndexNamespaces()

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

Definition at line 18051 of file tablecmds.c.

18053 {
18054  List *indexList;
18055  ListCell *l;
18056 
18057  indexList = RelationGetIndexList(rel);
18058 
18059  foreach(l, indexList)
18060  {
18061  Oid indexOid = lfirst_oid(l);
18062  ObjectAddress thisobj;
18063 
18064  thisobj.classId = RelationRelationId;
18065  thisobj.objectId = indexOid;
18066  thisobj.objectSubId = 0;
18067 
18068  /*
18069  * Note: currently, the index will not have its own dependency on the
18070  * namespace, so we don't need to do changeDependencyFor(). There's no
18071  * row type in pg_type, either.
18072  *
18073  * XXX this objsMoved test may be pointless -- surely we have a single
18074  * dependency link from a relation to each index?
18075  */
18076  if (!object_address_present(&thisobj, objsMoved))
18077  {
18078  AlterRelationNamespaceInternal(classRel, indexOid,
18079  oldNspOid, newNspOid,
18080  false, objsMoved);
18081  add_exact_object_address(&thisobj, objsMoved);
18082  }
18083  }
18084 
18085  list_free(indexList);
18086 }
bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs)
Definition: dependency.c:2591
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2531
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:4760
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
Definition: tablecmds.c:17981

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

17985 {
17986  HeapTuple classTup;
17987  Form_pg_class classForm;
17988  ObjectAddress thisobj;
17989  bool already_done = false;
17990 
17991  classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
17992  if (!HeapTupleIsValid(classTup))
17993  elog(ERROR, "cache lookup failed for relation %u", relOid);
17994  classForm = (Form_pg_class) GETSTRUCT(classTup);
17995 
17996  Assert(classForm->relnamespace == oldNspOid);
17997 
17998  thisobj.classId = RelationRelationId;
17999  thisobj.objectId = relOid;
18000  thisobj.objectSubId = 0;
18001 
18002  /*
18003  * If the object has already been moved, don't move it again. If it's
18004  * already in the right place, don't move it, but still fire the object
18005  * access hook.
18006  */
18007  already_done = object_address_present(&thisobj, objsMoved);
18008  if (!already_done && oldNspOid != newNspOid)
18009  {
18010  /* check for duplicate name (more friendly than unique-index failure) */
18011  if (get_relname_relid(NameStr(classForm->relname),
18012  newNspOid) != InvalidOid)
18013  ereport(ERROR,
18014  (errcode(ERRCODE_DUPLICATE_TABLE),
18015  errmsg("relation \"%s\" already exists in schema \"%s\"",
18016  NameStr(classForm->relname),
18017  get_namespace_name(newNspOid))));
18018 
18019  /* classTup is a copy, so OK to scribble on */
18020  classForm->relnamespace = newNspOid;
18021 
18022  CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
18023 
18024  /* Update dependency on schema if caller said so */
18025  if (hasDependEntry &&
18026  changeDependencyFor(RelationRelationId,
18027  relOid,
18028  NamespaceRelationId,
18029  oldNspOid,
18030  newNspOid) != 1)
18031  elog(ERROR, "could not change schema dependency for relation \"%s\"",
18032  NameStr(classForm->relname));
18033  }
18034  if (!already_done)
18035  {
18036  add_exact_object_address(&thisobj, objsMoved);
18037 
18038  InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
18039  }
18040 
18041  heap_freetuple(classTup);
18042 }
#define NameStr(name)
Definition: c.h:746
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3366
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:1885
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
FormData_pg_class * Form_pg_class
Definition: pg_class.h:153
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition: pg_depend.c:456
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
ItemPointerData t_self
Definition: htup.h:65
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:86

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, InvalidOid, InvokeObjectPostAlterHook, NameStr, object_address_present(), ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, SearchSysCacheCopy1, and HeapTupleData::t_self.

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

18099 {
18100  Relation depRel;
18101  SysScanDesc scan;
18102  ScanKeyData key[2];
18103  HeapTuple tup;
18104 
18105  /*
18106  * SERIAL sequences are those having an auto dependency on one of the
18107  * table's columns (we don't care *which* column, exactly).
18108  */
18109  depRel = table_open(DependRelationId, AccessShareLock);
18110 
18111  ScanKeyInit(&key[0],
18112  Anum_pg_depend_refclassid,
18113  BTEqualStrategyNumber, F_OIDEQ,
18114  ObjectIdGetDatum(RelationRelationId));
18115  ScanKeyInit(&key[1],
18116  Anum_pg_depend_refobjid,
18117  BTEqualStrategyNumber, F_OIDEQ,
18119  /* we leave refobjsubid unspecified */
18120 
18121  scan = systable_beginscan(depRel, DependReferenceIndexId, true,
18122  NULL, 2, key);
18123 
18124  while (HeapTupleIsValid(tup = systable_getnext(scan)))
18125  {
18126  Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
18127  Relation seqRel;
18128 
18129  /* skip dependencies other than auto dependencies on columns */
18130  if (depForm->refobjsubid == 0 ||
18131  depForm->classid != RelationRelationId ||
18132  depForm->objsubid != 0 ||
18133  !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
18134  continue;
18135 
18136  /* Use relation_open just in case it's an index */
18137  seqRel = relation_open(depForm->objid, lockmode);
18138 
18139  /* skip non-sequence relations */
18140  if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
18141  {
18142  /* No need to keep the lock */
18143  relation_close(seqRel, lockmode);
18144  continue;
18145  }
18146 
18147  /* Fix the pg_class and pg_depend entries */
18148  AlterRelationNamespaceInternal(classRel, depForm->objid,
18149  oldNspOid, newNspOid,
18150  true, objsMoved);
18151 
18152  /*
18153  * Sequences used to have entries in pg_type, but no longer do. If we
18154  * ever re-instate that, we'll need to move the pg_type entry to the
18155  * new namespace, too (using AlterTypeNamespaceInternal).
18156  */
18157  Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
18158 
18159  /* Now we can close it. Keep the lock till end of transaction. */
18160  relation_close(seqRel, NoLock);
18161  }
18162 
18163  systable_endscan(scan);
18164 
18166 }
@ DEPENDENCY_AUTO
Definition: dependency.h:34
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:596
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:503
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:384
#define AccessShareLock
Definition: lockdefs.h:36
FormData_pg_depend * Form_pg_depend
Definition: pg_depend.h:72
#define RelationGetForm(relation)
Definition: rel.h:499
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47
#define BTEqualStrategyNumber
Definition: stratnum.h:31

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

4435 {
4436  Relation rel;
4437 
4438  /* Caller is required to provide an adequate lock. */
4439  rel = relation_open(context->relid, NoLock);
4440 
4441  CheckTableNotInUse(rel, "ALTER TABLE");
4442 
4443  ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4444 }
#define stmt
Definition: indent_codes.h:59
tree context
Definition: radixtree.h:1833
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4778

References ATController(), CheckTableNotInUse(), context, NoLock, relation_open(), and stmt.

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4507 of file tablecmds.c.

4508 {
4509  /*
4510  * This only works if we read catalog tables using MVCC snapshots.
4511  */
4512  ListCell *lcmd;
4514 
4515  foreach(lcmd, cmds)
4516  {
4517  AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4518  LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4519 
4520  switch (cmd->subtype)
4521  {
4522  /*
4523  * These subcommands rewrite the heap, so require full locks.
4524  */
4525  case AT_AddColumn: /* may rewrite heap, in some cases and visible
4526  * to SELECT */
4527  case AT_SetAccessMethod: /* must rewrite heap */
4528  case AT_SetTableSpace: /* must rewrite heap */
4529  case AT_AlterColumnType: /* must rewrite heap */
4530  cmd_lockmode = AccessExclusiveLock;
4531  break;
4532 
4533  /*
4534  * These subcommands may require addition of toast tables. If
4535  * we add a toast table to a table currently being scanned, we
4536  * might miss data added to the new toast table by concurrent
4537  * insert transactions.
4538  */
4539  case AT_SetStorage: /* may add toast tables, see
4540  * ATRewriteCatalogs() */
4541  cmd_lockmode = AccessExclusiveLock;
4542  break;
4543 
4544  /*
4545  * Removing constraints can affect SELECTs that have been
4546  * optimized assuming the constraint holds true. See also
4547  * CloneFkReferenced.
4548  */
4549  case AT_DropConstraint: /* as DROP INDEX */
4550  case AT_DropNotNull: /* may change some SQL plans */
4551  cmd_lockmode = AccessExclusiveLock;
4552  break;
4553 
4554  /*
4555  * Subcommands that may be visible to concurrent SELECTs
4556  */
4557  case AT_DropColumn: /* change visible to SELECT */
4558  case AT_AddColumnToView: /* CREATE VIEW */
4559  case AT_DropOids: /* used to equiv to DropColumn */
4560  case AT_EnableAlwaysRule: /* may change SELECT rules */
4561  case AT_EnableReplicaRule: /* may change SELECT rules */
4562  case AT_EnableRule: /* may change SELECT rules */
4563  case AT_DisableRule: /* may change SELECT rules */
4564  cmd_lockmode = AccessExclusiveLock;
4565  break;
4566 
4567  /*
4568  * Changing owner may remove implicit SELECT privileges
4569  */
4570  case AT_ChangeOwner: /* change visible to SELECT */
4571  cmd_lockmode = AccessExclusiveLock;
4572  break;
4573 
4574  /*
4575  * Changing foreign table options may affect optimization.
4576  */
4577  case AT_GenericOptions:
4579  cmd_lockmode = AccessExclusiveLock;
4580  break;
4581 
4582  /*
4583  * These subcommands affect write operations only.
4584  */
4585  case AT_EnableTrig:
4586  case AT_EnableAlwaysTrig:
4587  case AT_EnableReplicaTrig:
4588  case AT_EnableTrigAll:
4589  case AT_EnableTrigUser:
4590  case AT_DisableTrig:
4591  case AT_DisableTrigAll:
4592  case AT_DisableTrigUser:
4593  cmd_lockmode = ShareRowExclusiveLock;
4594  break;
4595 
4596  /*
4597  * These subcommands affect write operations only. XXX
4598  * Theoretically, these could be ShareRowExclusiveLock.
4599  */
4600  case AT_ColumnDefault:
4602  case AT_AlterConstraint:
4603  case AT_AddIndex: /* from ADD CONSTRAINT */
4604  case AT_AddIndexConstraint:
4605  case AT_ReplicaIdentity:
4606  case AT_SetNotNull:
4607  case AT_SetAttNotNull:
4608  case AT_EnableRowSecurity:
4609  case AT_DisableRowSecurity:
4610  case AT_ForceRowSecurity:
4611  case AT_NoForceRowSecurity:
4612  case AT_AddIdentity:
4613  case AT_DropIdentity:
4614  case AT_SetIdentity:
4615  case AT_SetExpression:
4616  case AT_DropExpression:
4617  case AT_SetCompression:
4618  cmd_lockmode = AccessExclusiveLock;
4619  break;
4620 
4621  case AT_AddConstraint:
4622  case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4623  case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4624  if (IsA(cmd->def, Constraint))
4625  {
4626  Constraint *con = (Constraint *) cmd->def;
4627 
4628  switch (con->contype)
4629  {
4630  case CONSTR_EXCLUSION:
4631  case CONSTR_PRIMARY:
4632  case CONSTR_UNIQUE:
4633 
4634  /*
4635  * Cases essentially the same as CREATE INDEX. We
4636  * could reduce the lock strength to ShareLock if
4637  * we can work out how to allow concurrent catalog
4638  * updates. XXX Might be set down to
4639  * ShareRowExclusiveLock but requires further
4640  * analysis.
4641  */
4642  cmd_lockmode = AccessExclusiveLock;
4643  break;
4644  case CONSTR_FOREIGN:
4645 
4646  /*
4647  * We add triggers to both tables when we add a
4648  * Foreign Key, so the lock level must be at least
4649  * as strong as CREATE TRIGGER.
4650  */
4651  cmd_lockmode = ShareRowExclusiveLock;
4652  break;
4653 
4654  default:
4655  cmd_lockmode = AccessExclusiveLock;
4656  }
4657  }
4658  break;
4659 
4660  /*
4661  * These subcommands affect inheritance behaviour. Queries
4662  * started before us will continue to see the old inheritance
4663  * behaviour, while queries started after we commit will see
4664  * new behaviour. No need to prevent reads or writes to the
4665  * subtable while we hook it up though. Changing the TupDesc
4666  * may be a problem, so keep highest lock.
4667  */
4668  case AT_AddInherit:
4669  case AT_DropInherit:
4670  cmd_lockmode = AccessExclusiveLock;
4671  break;
4672 
4673  /*
4674  * These subcommands affect implicit row type conversion. They
4675  * have affects similar to CREATE/DROP CAST on queries. don't
4676  * provide for invalidating parse trees as a result of such
4677  * changes, so we keep these at AccessExclusiveLock.
4678  */
4679  case AT_AddOf:
4680  case AT_DropOf:
4681  cmd_lockmode = AccessExclusiveLock;
4682  break;
4683 
4684  /*
4685  * Only used by CREATE OR REPLACE VIEW which must conflict
4686  * with an SELECTs currently using the view.
4687  */
4688  case AT_ReplaceRelOptions:
4689  cmd_lockmode = AccessExclusiveLock;
4690  break;
4691 
4692  /*
4693  * These subcommands affect general strategies for performance
4694  * and maintenance, though don't change the semantic results
4695  * from normal data reads and writes. Delaying an ALTER TABLE
4696  * behind currently active writes only delays the point where
4697  * the new strategy begins to take effect, so there is no
4698  * benefit in waiting. In this case the minimum restriction
4699  * applies: we don't currently allow concurrent catalog
4700  * updates.
4701  */
4702  case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4703  case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4704  case AT_DropCluster: /* Uses MVCC in getIndexes() */
4705  case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4706  case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4707  cmd_lockmode = ShareUpdateExclusiveLock;
4708  break;
4709 
4710  case AT_SetLogged:
4711  case AT_SetUnLogged:
4712  cmd_lockmode = AccessExclusiveLock;
4713  break;
4714 
4715  case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4716  cmd_lockmode = ShareUpdateExclusiveLock;
4717  break;
4718 
4719  /*
4720  * Rel options are more complex than first appears. Options
4721  * are set here for tables, views and indexes; for historical
4722  * reasons these can all be used with ALTER TABLE, so we can't
4723  * decide between them using the basic grammar.
4724  */
4725  case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4726  * getTables() */
4727  case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4728  * getTables() */
4729  cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4730  break;
4731 
4732  case AT_AttachPartition:
4733  cmd_lockmode = ShareUpdateExclusiveLock;
4734  break;
4735 
4736  case AT_DetachPartition:
4737  if (((PartitionCmd *) cmd->def)->concurrent)
4738  cmd_lockmode = ShareUpdateExclusiveLock;
4739  else
4740  cmd_lockmode = AccessExclusiveLock;
4741  break;
4742 
4744  cmd_lockmode = ShareUpdateExclusiveLock;
4745  break;
4746 
4747  case AT_SplitPartition:
4748  cmd_lockmode = AccessExclusiveLock;
4749  break;
4750 
4751  case AT_MergePartitions:
4752  cmd_lockmode = AccessExclusiveLock;
4753  break;
4754 
4755  default: /* oops */
4756  elog(ERROR, "unrecognized alter table type: %d",
4757  (int) cmd->subtype);
4758  break;
4759  }
4760 
4761  /*
4762  * Take the greatest lockmode from any subcommand
4763  */
4764  if (cmd_lockmode > lockmode)
4765  lockmode = cmd_lockmode;
4766  }
4767 
4768  return lockmode;
4769 }
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:158
@ CONSTR_UNIQUE
Definition: parsenodes.h:2715
@ CONSTR_EXCLUSION
Definition: parsenodes.h:2716
@ CONSTR_PRIMARY
Definition: parsenodes.h:2714
#define lfirst(lc)
Definition: pg_list.h:172
LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList)
Definition: reloptions.c:2108
AlterTableType subtype
Definition: parsenodes.h:2436
ConstrType contype
Definition: parsenodes.h:2739

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

4463 {
4464  Relation rel;
4465  LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4466 
4467  rel = relation_open(relid, lockmode);
4468 
4470 
4471  ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4472 }
void EventTriggerAlterTableRelid(Oid objectId)
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition: tablecmds.c:4507

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4374 of file tablecmds.c.

4375 {
4376  return RangeVarGetRelidExtended(stmt->relation, lockmode,
4377  stmt->missing_ok ? RVR_MISSING_OK : 0,
4379  (void *) stmt);
4380 }
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:426
@ RVR_MISSING_OK
Definition: namespace.h:72
static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:18506

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

Referenced by ProcessUtilitySlow().

◆ AlterTableMoveAll()

Oid AlterTableMoveAll ( AlterTableMoveAllStmt stmt)

Definition at line 15890 of file tablecmds.c.

15891 {
15892  List *relations = NIL;
15893  ListCell *l;
15894  ScanKeyData key[1];
15895  Relation rel;
15896  TableScanDesc scan;
15897  HeapTuple tuple;
15898  Oid orig_tablespaceoid;
15899  Oid new_tablespaceoid;
15900  List *role_oids = roleSpecsToIds(stmt->roles);
15901 
15902  /* Ensure we were not asked to move something we can't */
15903  if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15904  stmt->objtype != OBJECT_MATVIEW)
15905  ereport(ERROR,
15906  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15907  errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15908 
15909  /* Get the orig and new tablespace OIDs */
15910  orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15911  new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15912 
15913  /* Can't move shared relations in to or out of pg_global */
15914  /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15915  if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15916  new_tablespaceoid == GLOBALTABLESPACE_OID)
15917  ereport(ERROR,
15918  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15919  errmsg("cannot move relations in to or out of pg_global tablespace")));
15920 
15921  /*
15922  * Must have CREATE rights on the new tablespace, unless it is the
15923  * database default tablespace (which all users implicitly have CREATE
15924  * rights on).
15925  */
15926  if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15927  {
15928  AclResult aclresult;
15929 
15930  aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15931  ACL_CREATE);
15932  if (aclresult != ACLCHECK_OK)
15933  aclcheck_error(aclresult, OBJECT_TABLESPACE,
15934  get_tablespace_name(new_tablespaceoid));
15935  }
15936 
15937  /*
15938  * Now that the checks are done, check if we should set either to
15939  * InvalidOid because it is our database's default tablespace.
15940  */
15941  if (orig_tablespaceoid == MyDatabaseTableSpace)
15942  orig_tablespaceoid = InvalidOid;
15943 
15944  if (new_tablespaceoid == MyDatabaseTableSpace)
15945  new_tablespaceoid = InvalidOid;
15946 
15947  /* no-op */
15948  if (orig_tablespaceoid == new_tablespaceoid)
15949  return new_tablespaceoid;
15950 
15951  /*
15952  * Walk the list of objects in the tablespace and move them. This will
15953  * only find objects in our database, of course.
15954  */
15955  ScanKeyInit(&key[0],
15956  Anum_pg_class_reltablespace,
15957  BTEqualStrategyNumber, F_OIDEQ,
15958  ObjectIdGetDatum(orig_tablespaceoid));
15959 
15960  rel = table_open(RelationRelationId, AccessShareLock);
15961  scan = table_beginscan_catalog(rel, 1, key);
15962  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
15963  {
15964  Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
15965  Oid relOid = relForm->oid;
15966 
15967  /*
15968  * Do not move objects in pg_catalog as part of this, if an admin
15969  * really wishes to do so, they can issue the individual ALTER
15970  * commands directly.
15971  *
15972  * Also, explicitly avoid any shared tables, temp tables, or TOAST
15973  * (TOAST will be moved with the main table).
15974  */
15975  if (IsCatalogNamespace(relForm->relnamespace) ||
15976  relForm->relisshared ||
15977  isAnyTempNamespace(relForm->relnamespace) ||
15978  IsToastNamespace(relForm->relnamespace))
15979  continue;
15980 
15981  /* Only move the object type requested */
15982  if ((stmt->objtype == OBJECT_TABLE &&
15983  relForm->relkind != RELKIND_RELATION &&
15984  relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
15985  (stmt->objtype == OBJECT_INDEX &&
15986  relForm->relkind != RELKIND_INDEX &&
15987  relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
15988  (stmt->objtype == OBJECT_MATVIEW &&
15989  relForm->relkind != RELKIND_MATVIEW))
15990  continue;
15991 
15992  /* Check if we are only moving objects owned by certain roles */
15993  if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
15994  continue;
15995 
15996  /*
15997  * Handle permissions-checking here since we are locking the tables
15998  * and also to avoid doing a bunch of work only to fail part-way. Note
15999  * that permissions will also be checked by AlterTableInternal().
16000  *
16001  * Caller must be considered an owner on the table to move it.
16002  */
16003  if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
16005  NameStr(relForm->relname));
16006 
16007  if (stmt->nowait &&
16009  ereport(ERROR,
16010  (errcode(ERRCODE_OBJECT_IN_USE),
16011  errmsg("aborting because lock on relation \"%s.%s\" is not available",
16012  get_namespace_name(relForm->relnamespace),
16013  NameStr(relForm->relname))));
16014  else
16016 
16017  /* Add to our list of objects to move */
16018  relations = lappend_oid(relations, relOid);
16019  }
16020 
16021  table_endscan(scan);
16023 
16024  if (relations == NIL)
16025  ereport(NOTICE,
16026  (errcode(ERRCODE_NO_DATA_FOUND),
16027  errmsg("no matching relations in tablespace \"%s\" found",
16028  orig_tablespaceoid == InvalidOid ? "(database default)" :
16029  get_tablespace_name(orig_tablespaceoid))));
16030 
16031  /* Everything is locked, loop through and move all of the relations. */
16032  foreach(l, relations)
16033  {
16034  List *cmds = NIL;
16036 
16037  cmd->subtype = AT_SetTableSpace;
16038  cmd->name = stmt->new_tablespacename;
16039 
16040  cmds = lappend(cmds, cmd);
16041 
16043  /* OID is set by AlterTableInternal */
16044  AlterTableInternal(lfirst_oid(l), cmds, false);
16046  }
16047 
16048  return new_tablespaceoid;
16049 }
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:2688
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3876
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4130
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
List * roleSpecsToIds(List *memberNames)
Definition: user.c:1652
bool IsToastNamespace(Oid namespaceId)
Definition: catalog.c:200
bool IsCatalogNamespace(Oid namespaceId)
Definition: catalog.c:182
#define NOTICE
Definition: elog.h:35
void EventTriggerAlterTableStart(Node *parsetree)
void EventTriggerAlterTableEnd(void)
Oid MyDatabaseTableSpace
Definition: globals.c:93
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1248
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:108
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2003
Oid GetUserId(void)
Definition: miscinit.c:514
bool isAnyTempNamespace(Oid namespaceId)
Definition: namespace.c:3672
#define makeNode(_type_)
Definition: nodes.h:155
ObjectType get_relkind_objtype(char relkind)
@ OBJECT_MATVIEW
Definition: parsenodes.h:2286
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2305
@ OBJECT_INDEX
Definition: parsenodes.h:2283
@ OBJECT_TABLE
Definition: parsenodes.h:2304
#define ACL_CREATE
Definition: parsenodes.h:85
@ ForwardScanDirection
Definition: sdir.h:28
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:112
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:1029
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition: tablecmds.c:4462

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

17877 {
17878  Relation rel;
17879  Oid relid;
17880  Oid oldNspOid;
17881  Oid nspOid;
17882  RangeVar *newrv;
17883  ObjectAddresses *objsMoved;
17884  ObjectAddress myself;
17885 
17887  stmt->missing_ok ? RVR_MISSING_OK : 0,
17889  (void *) stmt);
17890 
17891  if (!OidIsValid(relid))
17892  {
17893  ereport(NOTICE,
17894  (errmsg("relation \"%s\" does not exist, skipping",
17895  stmt->relation->relname)));
17896  return InvalidObjectAddress;
17897  }
17898 
17899  rel = relation_open(relid, NoLock);
17900 
17901  oldNspOid = RelationGetNamespace(rel);
17902 
17903  /* If it's an owned sequence, disallow moving it by itself. */
17904  if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17905  {
17906  Oid tableId;
17907  int32 colId;
17908 
17909  if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17910  sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17911  ereport(ERROR,
17912  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17913  errmsg("cannot move an owned sequence into another schema"),
17914  errdetail("Sequence \"%s\" is linked to table \"%s\".",
17916  get_rel_name(tableId))));
17917  }
17918 
17919  /* Get and lock schema OID and check its permissions. */
17920  newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17921  nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17922 
17923  /* common checks on switching namespaces */
17924  CheckSetNamespace(oldNspOid, nspOid);
17925 
17926  objsMoved = new_object_addresses();
17927  AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17928  free_object_addresses(objsMoved);
17929 
17930  ObjectAddressSet(myself, RelationRelationId, relid);
17931 
17932  if (oldschema)
17933  *oldschema = oldNspOid;
17934 
17935  /* close rel, but keep lock until commit */
17936  relation_close(rel, NoLock);
17937 
17938  return myself;
17939 }
signed int int32
Definition: c.h:494
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2485
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2771
int errdetail(const char *fmt,...)
Definition: elog.c:1205
char * get_rel_name(Oid relid)
Definition: lsyscache.c:1928
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:424
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition: namespace.c:724
void CheckSetNamespace(Oid oldNspOid, Oid nspOid)
Definition: namespace.c:3444
const ObjectAddress InvalidObjectAddress
bool sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
Definition: pg_depend.c:827
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:17947

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

17949 {
17950  Relation classRel;
17951 
17952  Assert(objsMoved != NULL);
17953 
17954  /* OK, modify the pg_class row and pg_depend entry */
17955  classRel = table_open(RelationRelationId, RowExclusiveLock);
17956 
17957  AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
17958  nspOid, true, objsMoved);
17959 
17960  /* Fix the table's row type too, if it has one */
17961  if (OidIsValid(rel->rd_rel->reltype))
17962  AlterTypeNamespaceInternal(rel->rd_rel->reltype,
17963  nspOid, false, false, objsMoved);
17964 
17965  /* Fix other dependent stuff */
17966  AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
17967  AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
17968  objsMoved, AccessExclusiveLock);
17969  AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
17970  false, objsMoved);
17971 
17972  table_close(classRel, RowExclusiveLock);
17973 }
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:18096
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:18051
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition: typecmds.c:4132

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

9731 {
9732  List *newcons;
9733  ListCell *lcon;
9734  List *children;
9735  ListCell *child;
9737 
9738  /* Guard against stack overflow due to overly deep inheritance tree. */
9740 
9741  /* At top level, permission check was done in ATPrepCmd, else do it */
9742  if (recursing)
9744 
9745  /*
9746  * Call AddRelationNewConstraints to do the work, making sure it works on
9747  * a copy of the Constraint so transformExpr can't modify the original. It
9748  * returns a list of cooked constraints.
9749  *
9750  * If the constraint ends up getting merged with a pre-existing one, it's
9751  * omitted from the returned list, which is what we want: we do not need
9752  * to do any validation work. That can only happen at child tables,
9753  * though, since we disallow merging at the top level.
9754  */
9755  newcons = AddRelationNewConstraints(rel, NIL,
9756  list_make1(copyObject(constr)),
9757  recursing || is_readd, /* allow_merge */
9758  !recursing, /* is_local */
9759  is_readd, /* is_internal */
9760  NULL); /* queryString not available
9761  * here */
9762 
9763  /* we don't expect more than one constraint here */
9764  Assert(list_length(newcons) <= 1);
9765 
9766  /* Add each to-be-validated constraint to Phase 3's queue */
9767  foreach(lcon, newcons)
9768  {
9769  CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9770 
9771  if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9772  {
9773  NewConstraint *newcon;
9774 
9775  newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9776  newcon->name = ccon->name;
9777  newcon->contype = ccon->contype;
9778  newcon->qual = ccon->expr;
9779 
9780  tab->constraints = lappend(tab->constraints, newcon);
9781  }
9782 
9783  /* Save the actually assigned name if it was defaulted */
9784  if (constr->conname == NULL)
9785  constr->conname = ccon->name;
9786 
9787  /*
9788  * If adding a not-null constraint, set the pg_attribute flag and tell
9789  * phase 3 to verify existing rows, if needed.
9790  */
9791  if (constr->contype == CONSTR_NOTNULL)
9792  set_attnotnull(wqueue, rel, ccon->attnum,
9793  !ccon->is_no_inherit, lockmode);
9794 
9795  ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9796  }
9797 
9798  /* At this point we must have a locked-down name to use */
9799  Assert(newcons == NIL || constr->conname != NULL);
9800 
9801  /* Advance command counter in case same table is visited multiple times */
9803 
9804  /*
9805  * If the constraint got merged with an existing constraint, we're done.
9806  * We mustn't recurse to child tables in this case, because they've
9807  * already got the constraint, and visiting them again would lead to an
9808  * incorrect value for coninhcount.
9809  */
9810  if (newcons == NIL)
9811  return address;
9812 
9813  /*
9814  * If adding a NO INHERIT constraint, no need to find our children.
9815  */
9816  if (constr->is_no_inherit)
9817  return address;
9818 
9819  /*
9820  * Propagate to children as appropriate. Unlike most other ALTER
9821  * routines, we have to do this one level of recursion at a time; we can't
9822  * use find_all_inheritors to do it in one pass.
9823  */
9824  children =
9826 
9827  /*
9828  * Check if ONLY was specified with ALTER TABLE. If so, allow the
9829  * constraint creation only if there are no children currently. Error out
9830  * otherwise.
9831  */
9832  if (!recurse && children != NIL)
9833  ereport(ERROR,
9834  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9835  errmsg("constraint must be added to child tables too")));
9836 
9837  /*
9838  * The constraint must appear as inherited in children, so create a
9839  * modified constraint object to use.
9840  */
9841  constr = copyObject(constr);
9842  constr->inhcount = 1;
9843  foreach(child, children)
9844  {
9845  Oid childrelid = lfirst_oid(child);
9846  Relation childrel;
9847  AlteredTableInfo *childtab;
9848 
9849  /* find_inheritance_children already got lock */
9850  childrel = table_open(childrelid, NoLock);
9851  CheckTableNotInUse(childrel, "ALTER TABLE");
9852 
9853  /* Find or create work queue entry for this table */
9854  childtab = ATGetQueueEntry(wqueue, childrel);
9855 
9856  /*
9857  * Recurse to child. XXX if we didn't create a constraint on the
9858  * parent because it already existed, and we do create one on a child,
9859  * should we return that child's constraint ObjectAddress here?
9860  */
9861  ATAddCheckNNConstraint(wqueue, childtab, childrel,
9862  constr, recurse, true, is_readd, lockmode);
9863 
9864  table_close(childrel, NoLock);
9865  }
9866 
9867  return address;
9868 }
List * AddRelationNewConstraints(Relation rel, List *newColDefaults, List *newConstraints, bool allow_merge, bool is_local, bool is_internal, const char *queryString)
Definition: heap.c:2307
@ CONSTR_NOTNULL
Definition: parsenodes.h:2709
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: postgres.c:3531
bool is_no_inherit
Definition: parsenodes.h:2745
Oid conoid
Definition: heap.h:39
char * name
Definition: heap.h:40
AttrNumber attnum
Definition: heap.h:41
bool skip_validation
Definition: heap.h:43
bool is_no_inherit
Definition: heap.h:46
ConstrType contype
Definition: heap.h:37
Node * expr
Definition: heap.h:42
#define ATT_TABLE
Definition: tablecmds.c:326
static ObjectAddress ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9728
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition: tablecmds.c:6612
#define ATT_FOREIGN_TABLE
Definition: tablecmds.c:331
static bool set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:7732

References AddRelationNewConstraints(), Assert, AT_AddConstraint, ATGetQueueEntry(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_TABLE, CookedConstraint::attnum, check_stack_depth(), CheckTableNotInUse(), 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(), Constraint::inhcount, InvalidObjectAddress, CookedConstraint::is_no_inherit, Constraint::is_no_inherit, lappend(), lfirst, lfirst_oid, list_length(), list_make1, NewConstraint::name, CookedConstraint::name, NIL, NoLock, ObjectAddressSet, palloc0(), NewConstraint::qual, RelationGetRelid, set_attnotnull(), CookedConstraint::skip_validation, table_close(), and table_open().

Referenced by ATExecAddConstraint(), and DetachAddConstraintIfNeeded().

◆ ATAddForeignKeyConstraint()

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

Definition at line 9886 of file tablecmds.c.

9889 {
9890  Relation pkrel;
9891  int16 pkattnum[INDEX_MAX_KEYS] = {0};
9892  int16 fkattnum[INDEX_MAX_KEYS] = {0};
9893  Oid pktypoid[INDEX_MAX_KEYS] = {0};
9894  Oid fktypoid[INDEX_MAX_KEYS] = {0};
9895  Oid opclasses[INDEX_MAX_KEYS] = {0};
9896  Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9897  Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9898  Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9899  int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9900  bool with_period;
9901  bool pk_has_without_overlaps;
9902  int i;
9903  int numfks,
9904  numpks,
9905  numfkdelsetcols;
9906  Oid indexOid;
9907  bool old_check_ok;
9908  ObjectAddress address;
9909  ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9910 
9911  /*
9912  * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9913  * delete rows out from under us.
9914  */
9915  if (OidIsValid(fkconstraint->old_pktable_oid))
9916  pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9917  else
9918  pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9919 
9920  /*
9921  * Validity checks (permission checks wait till we have the column
9922  * numbers)
9923  */
9924  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9925  {
9926  if (!recurse)
9927  ereport(ERROR,
9928  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9929  errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9931  RelationGetRelationName(pkrel))));
9932  if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9933  ereport(ERROR,
9934  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9935  errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9937  RelationGetRelationName(pkrel)),
9938  errdetail("This feature is not yet supported on partitioned tables.")));
9939  }
9940 
9941  if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9942  pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9943  ereport(ERROR,
9944  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9945  errmsg("referenced relation \"%s\" is not a table",
9946  RelationGetRelationName(pkrel))));
9947 
9948  if (!allowSystemTableMods && IsSystemRelation(pkrel))
9949  ereport(ERROR,
9950  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9951  errmsg("permission denied: \"%s\" is a system catalog",
9952  RelationGetRelationName(pkrel))));
9953 
9954  /*
9955  * References from permanent or unlogged tables to temp tables, and from
9956  * permanent tables to unlogged tables, are disallowed because the
9957  * referenced data can vanish out from under us. References from temp
9958  * tables to any other table type are also disallowed, because other
9959  * backends might need to run the RI triggers on the perm table, but they
9960  * can't reliably see tuples in the local buffers of other backends.
9961  */
9962  switch (rel->rd_rel->relpersistence)
9963  {
9964  case RELPERSISTENCE_PERMANENT:
9965  if (!RelationIsPermanent(pkrel))
9966  ereport(ERROR,
9967  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9968  errmsg("constraints on permanent tables may reference only permanent tables")));
9969  break;
9970  case RELPERSISTENCE_UNLOGGED:
9971  if (!RelationIsPermanent(pkrel)
9972  && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9973  ereport(ERROR,
9974  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9975  errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9976  break;
9977  case RELPERSISTENCE_TEMP:
9978  if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9979  ereport(ERROR,
9980  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9981  errmsg("constraints on temporary tables may reference only temporary tables")));
9982  if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9983  ereport(ERROR,
9984  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9985  errmsg("constraints on temporary tables must involve temporary tables of this session")));
9986  break;
9987  }
9988 
9989  /*
9990  * Look up the referencing attributes to make sure they exist, and record
9991  * their attnums and type OIDs.
9992  */
9994  fkconstraint->fk_attrs,
9995  fkattnum, fktypoid);
9996  with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
9997  if (with_period && !fkconstraint->fk_with_period)
9998  ereport(ERROR,
9999  errcode(ERRCODE_INVALID_FOREIGN_KEY),
10000  errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10001 
10002  numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10003  fkconstraint->fk_del_set_cols,
10004  fkdelsetcols, NULL);
10005  validateFkOnDeleteSetColumns(numfks, fkattnum,
10006  numfkdelsetcols, fkdelsetcols,
10007  fkconstraint->fk_del_set_cols);
10008 
10009  /*
10010  * If the attribute list for the referenced table was omitted, lookup the
10011  * definition of the primary key and use it. Otherwise, validate the
10012  * supplied attribute list. In either case, discover the index OID and
10013  * index opclasses, and the attnums and type OIDs of the attributes.
10014  */
10015  if (fkconstraint->pk_attrs == NIL)
10016  {
10017  numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10018  &fkconstraint->pk_attrs,
10019  pkattnum, pktypoid,
10020  opclasses, &pk_has_without_overlaps);
10021 
10022  /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10023  if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10024  ereport(ERROR,
10025  errcode(ERRCODE_INVALID_FOREIGN_KEY),
10026  errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10027  }
10028  else
10029  {
10030  numpks = transformColumnNameList(RelationGetRelid(pkrel),
10031  fkconstraint->pk_attrs,
10032  pkattnum, pktypoid);
10033 
10034  /* Since we got pk_attrs, one should be a period. */
10035  if (with_period && !fkconstraint->pk_with_period)
10036  ereport(ERROR,
10037  errcode(ERRCODE_INVALID_FOREIGN_KEY),
10038  errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10039 
10040  /* Look for an index matching the column list */
10041  indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10042  with_period, opclasses, &pk_has_without_overlaps);
10043  }
10044 
10045  /*
10046  * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10047  * must use PERIOD.
10048  */
10049  if (pk_has_without_overlaps && !with_period)
10050  ereport(ERROR,
10051  errcode(ERRCODE_INVALID_FOREIGN_KEY),
10052  errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
10053 
10054  /*
10055  * Now we can check permissions.
10056  */
10057  checkFkeyPermissions(pkrel, pkattnum, numpks);
10058 
10059  /*
10060  * Check some things for generated columns.
10061  */
10062  for (i = 0; i < numfks; i++)
10063  {
10064  char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10065 
10066  if (attgenerated)
10067  {
10068  /*
10069  * Check restrictions on UPDATE/DELETE actions, per SQL standard
10070  */
10071  if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10072  fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10073  fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10074  ereport(ERROR,
10075  (errcode(ERRCODE_SYNTAX_ERROR),
10076  errmsg("invalid %s action for foreign key constraint containing generated column",
10077  "ON UPDATE")));
10078  if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10079  fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10080  ereport(ERROR,
10081  (errcode(ERRCODE_SYNTAX_ERROR),
10082  errmsg("invalid %s action for foreign key constraint containing generated column",
10083  "ON DELETE")));
10084  }
10085  }
10086 
10087  /*
10088  * Some actions are currently unsupported for foreign keys using PERIOD.
10089  */
10090  if (fkconstraint->fk_with_period)
10091  {
10092  if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10093  fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10094  fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10095  ereport(ERROR,
10096  errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10097  errmsg("unsupported %s action for foreign key constraint using PERIOD",
10098  "ON UPDATE"));
10099 
10100  if (fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10101  fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10102  fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10103  ereport(ERROR,
10104  errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10105  errmsg("unsupported %s action for foreign key constraint using PERIOD",
10106  "ON DELETE"));
10107  }
10108 
10109  /*
10110  * Look up the equality operators to use in the constraint.
10111  *
10112  * Note that we have to be careful about the difference between the actual
10113  * PK column type and the opclass' declared input type, which might be
10114  * only binary-compatible with it. The declared opcintype is the right
10115  * thing to probe pg_amop with.
10116  */
10117  if (numfks != numpks)
10118  ereport(ERROR,
10119  (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10120  errmsg("number of referencing and referenced columns for foreign key disagree")));
10121 
10122  /*
10123  * On the strength of a previous constraint, we might avoid scanning
10124  * tables to validate this one. See below.
10125  */
10126  old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10127  Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10128 
10129  for (i = 0; i < numpks; i++)
10130  {
10131  Oid pktype = pktypoid[i];
10132  Oid fktype = fktypoid[i];
10133  Oid fktyped;
10134  HeapTuple cla_ht;
10135  Form_pg_opclass cla_tup;
10136  Oid amid;
10137  Oid opfamily;
10138  Oid opcintype;
10139  Oid pfeqop;
10140  Oid ppeqop;
10141  Oid ffeqop;
10142  int16 eqstrategy;
10143  Oid pfeqop_right;
10144 
10145  /* We need several fields out of the pg_opclass entry */
10146  cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10147  if (!HeapTupleIsValid(cla_ht))
10148  elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10149  cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10150  amid = cla_tup->opcmethod;
10151  opfamily = cla_tup->opcfamily;
10152  opcintype = cla_tup->opcintype;
10153  ReleaseSysCache(cla_ht);
10154 
10155  if (with_period)
10156  {
10157  StrategyNumber rtstrategy;
10158  bool for_overlaps = with_period && i == numpks - 1;
10159 
10160  /*
10161  * GiST indexes are required to support temporal foreign keys
10162  * because they combine equals and overlaps.
10163  */
10164  if (amid != GIST_AM_OID)
10165  elog(ERROR, "only GiST indexes are supported for temporal foreign keys");
10166 
10167  rtstrategy = for_overlaps ? RTOverlapStrategyNumber : RTEqualStrategyNumber;
10168 
10169  /*
10170  * An opclass can use whatever strategy numbers it wants, so we
10171  * ask the opclass what number it actually uses instead of our RT*
10172  * constants.
10173  */
10174  eqstrategy = GistTranslateStratnum(opclasses[i], rtstrategy);
10175  if (eqstrategy == InvalidStrategy)
10176  {
10177  HeapTuple tuple;
10178 
10179  tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10180  if (!HeapTupleIsValid(tuple))
10181  elog(ERROR, "cache lookup failed for operator class %u", opclasses[i]);
10182 
10183  ereport(ERROR,
10184  errcode(ERRCODE_UNDEFINED_OBJECT),
10185  for_overlaps
10186  ? errmsg("could not identify an overlaps operator for foreign key")
10187  : errmsg("could not identify an equality operator for foreign key"),
10188  errdetail("Could not translate strategy number %d for operator class \"%s\" for access method \"%s\".",
10189  rtstrategy, NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname), "gist"));
10190  }
10191  }
10192  else
10193  {
10194  /*
10195  * Check it's a btree; currently this can never fail since no
10196  * other index AMs support unique indexes. If we ever did have
10197  * other types of unique indexes, we'd need a way to determine
10198  * which operator strategy number is equality. (We could use
10199  * something like GistTranslateStratnum.)
10200  */
10201  if (amid != BTREE_AM_OID)
10202  elog(ERROR, "only b-tree indexes are supported for foreign keys");
10203  eqstrategy = BTEqualStrategyNumber;
10204  }
10205 
10206  /*
10207  * There had better be a primary equality operator for the index.
10208  * We'll use it for PK = PK comparisons.
10209  */
10210  ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10211  eqstrategy);
10212 
10213  if (!OidIsValid(ppeqop))
10214  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10215  eqstrategy, opcintype, opcintype, opfamily);
10216 
10217  /*
10218  * Are there equality operators that take exactly the FK type? Assume
10219  * we should look through any domain here.
10220  */
10221  fktyped = getBaseType(fktype);
10222 
10223  pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10224  eqstrategy);
10225  if (OidIsValid(pfeqop))
10226  {
10227  pfeqop_right = fktyped;
10228  ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10229  eqstrategy);
10230  }
10231  else
10232  {
10233  /* keep compiler quiet */
10234  pfeqop_right = InvalidOid;
10235  ffeqop = InvalidOid;
10236  }
10237 
10238  if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10239  {
10240  /*
10241  * Otherwise, look for an implicit cast from the FK type to the
10242  * opcintype, and if found, use the primary equality operator.
10243  * This is a bit tricky because opcintype might be a polymorphic
10244  * type such as ANYARRAY or ANYENUM; so what we have to test is
10245  * whether the two actual column types can be concurrently cast to
10246  * that type. (Otherwise, we'd fail to reject combinations such
10247  * as int[] and point[].)
10248  */
10249  Oid input_typeids[2];
10250  Oid target_typeids[2];
10251 
10252  input_typeids[0] = pktype;
10253  input_typeids[1] = fktype;
10254  target_typeids[0] = opcintype;
10255  target_typeids[1] = opcintype;
10256  if (can_coerce_type(2, input_typeids, target_typeids,
10258  {
10259  pfeqop = ffeqop = ppeqop;
10260  pfeqop_right = opcintype;
10261  }
10262  }
10263 
10264  if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10265  ereport(ERROR,
10266  (errcode(ERRCODE_DATATYPE_MISMATCH),
10267  errmsg("foreign key constraint \"%s\" cannot be implemented",
10268  fkconstraint->conname),
10269  errdetail("Key columns \"%s\" and \"%s\" "
10270  "are of incompatible types: %s and %s.",
10271  strVal(list_nth(fkconstraint->fk_attrs, i)),
10272  strVal(list_nth(fkconstraint->pk_attrs, i)),
10273  format_type_be(fktype),
10274  format_type_be(pktype))));
10275 
10276  if (old_check_ok)
10277  {
10278  /*
10279  * When a pfeqop changes, revalidate the constraint. We could
10280  * permit intra-opfamily changes, but that adds subtle complexity
10281  * without any concrete benefit for core types. We need not
10282  * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10283  */
10284  old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10285  old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10286  old_pfeqop_item);
10287  }
10288  if (old_check_ok)
10289  {
10290  Oid old_fktype;
10291  Oid new_fktype;
10292  CoercionPathType old_pathtype;
10293  CoercionPathType new_pathtype;
10294  Oid old_castfunc;
10295  Oid new_castfunc;
10297  fkattnum[i] - 1);
10298 
10299  /*
10300  * Identify coercion pathways from each of the old and new FK-side
10301  * column types to the right (foreign) operand type of the pfeqop.
10302  * We may assume that pg_constraint.conkey is not changing.
10303  */
10304  old_fktype = attr->atttypid;
10305  new_fktype = fktype;
10306  old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10307  &old_castfunc);
10308  new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10309  &new_castfunc);
10310 
10311  /*
10312  * Upon a change to the cast from the FK column to its pfeqop
10313  * operand, revalidate the constraint. For this evaluation, a
10314  * binary coercion cast is equivalent to no cast at all. While
10315  * type implementors should design implicit casts with an eye
10316  * toward consistency of operations like equality, we cannot
10317  * assume here that they have done so.
10318  *
10319  * A function with a polymorphic argument could change behavior
10320  * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10321  * when the cast destination is polymorphic, we only avoid
10322  * revalidation if the input type has not changed at all. Given
10323  * just the core data types and operator classes, this requirement
10324  * prevents no would-be optimizations.
10325  *
10326  * If the cast converts from a base type to a domain thereon, then
10327  * that domain type must be the opcintype of the unique index.
10328  * Necessarily, the primary key column must then be of the domain
10329  * type. Since the constraint was previously valid, all values on
10330  * the foreign side necessarily exist on the primary side and in
10331  * turn conform to the domain. Consequently, we need not treat
10332  * domains specially here.
10333  *
10334  * Since we require that all collations share the same notion of
10335  * equality (which they do, because texteq reduces to bitwise
10336  * equality), we don't compare collation here.
10337  *
10338  * We need not directly consider the PK type. It's necessarily
10339  * binary coercible to the opcintype of the unique index column,
10340  * and ri_triggers.c will only deal with PK datums in terms of
10341  * that opcintype. Changing the opcintype also changes pfeqop.
10342  */
10343  old_check_ok = (new_pathtype == old_pathtype &&
10344  new_castfunc == old_castfunc &&
10345  (!IsPolymorphicType(pfeqop_right) ||
10346  new_fktype == old_fktype));
10347  }
10348 
10349  pfeqoperators[i] = pfeqop;
10350  ppeqoperators[i] = ppeqop;
10351  ffeqoperators[i] = ffeqop;
10352  }
10353 
10354  /*
10355  * For FKs with PERIOD we need additional operators to check whether the
10356  * referencing row's range is contained by the aggregated ranges of the
10357  * referenced row(s). For rangetypes and multirangetypes this is
10358  * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10359  * support for now. FKs will look these up at "runtime", but we should
10360  * make sure the lookup works here, even if we don't use the values.
10361  */
10362  if (with_period)
10363  {
10364  Oid periodoperoid;
10365  Oid aggedperiodoperoid;
10366 
10367  FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid);
10368  }
10369 
10370  /*
10371  * Create all the constraint and trigger objects, recursing to partitions
10372  * as necessary. First handle the referenced side.
10373  */
10374  address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
10375  indexOid,
10376  InvalidOid, /* no parent constraint */
10377  numfks,
10378  pkattnum,
10379  fkattnum,
10380  pfeqoperators,
10381  ppeqoperators,
10382  ffeqoperators,
10383  numfkdelsetcols,
10384  fkdelsetcols,
10385  old_check_ok,
10387  with_period);
10388 
10389  /* Now handle the referencing side. */
10390  addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10391  indexOid,
10392  address.objectId,
10393  numfks,
10394  pkattnum,
10395  fkattnum,
10396  pfeqoperators,
10397  ppeqoperators,
10398  ffeqoperators,
10399  numfkdelsetcols,
10400  fkdelsetcols,
10401  old_check_ok,
10402  lockmode,
10404  with_period);
10405 
10406  /*
10407  * Done. Close pk table, but keep lock until we've committed.
10408  */
10409  table_close(pkrel, NoLock);
10410 
10411  return address;
10412 }
signed short int16
Definition: c.h:493
bool IsSystemRelation(Relation relation)
Definition: catalog.c:73
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
StrategyNumber GistTranslateStratnum(Oid opclass, StrategyNumber strat)
Definition: gistutil.c:1081
bool allowSystemTableMods
Definition: globals.c:127
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:166
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2521
bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids, CoercionContext ccontext)
Definition: parse_coerce.c:556
CoercionPathType
Definition: parse_coerce.h:25
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2729
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2727
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2728
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
void FindFKPeriodOpers(Oid opclass, Oid *containedbyoperoid, Oid *aggedcontainedbyoperoid)
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
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:684
#define RelationIsPermanent(relation)
Definition: rel.h:617
uint16 StrategyNumber
Definition: stratnum.h:22
#define RTOverlapStrategyNumber
Definition: stratnum.h:53
#define RTEqualStrategyNumber
Definition: stratnum.h:68
#define InvalidStrategy
Definition: stratnum.h:24
TupleDesc oldDesc
Definition: tablecmds.c:171
List * pk_attrs
Definition: parsenodes.h:2772
List * fk_del_set_cols
Definition: parsenodes.h:2778
Oid old_pktable_oid
Definition: parsenodes.h:2780
List * old_conpfeqop
Definition: parsenodes.h:2779
bool pk_with_period
Definition: parsenodes.h:2774
RangeVar * pktable
Definition: parsenodes.h:2770
bool rd_islocaltemp
Definition: rel.h:61
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:266
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:218
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: table.c:83
static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, List **attnamelist, int16 *attnums, Oid *atttypids, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:12242
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
Definition: tablecmds.c:12495
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:12344
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids)
Definition: tablecmds.c:12189
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
Definition: tablecmds.c:12524
static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, const int16 *fksetcolsattnums, List *fksetcols)
Definition: tablecmds.c:10420
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92
#define strVal(v)
Definition: value.h:82

References addFkRecurseReferenced(), addFkRecurseReferencing(), allowSystemTableMods, Assert, BTEqualStrategyNumber, can_coerce_type(), checkFkeyPermissions(), COERCION_IMPLICIT, 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_SETDEFAULT, FKCONSTR_ACTION_SETNULL, format_type_be(), get_opfamily_member(), getBaseType(), GETSTRUCT, GistTranslateStratnum(), HeapTupleIsValid, i, INDEX_MAX_KEYS, Constraint::initially_valid, InvalidOid, InvalidStrategy, IsSystemRelation(), lfirst_oid, list_head(), list_length(), list_nth(), lnext(), NameStr, 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(), RTEqualStrategyNumber, RTOverlapStrategyNumber, SearchSysCache1(), ShareRowExclusiveLock, Constraint::skip_validation, 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 6732 of file tablecmds.c.

6733 {
6734  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6735  {
6736  List *inh;
6737  ListCell *cell;
6738 
6739  inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6740  /* first element is the parent rel; must ignore it */
6741  for_each_from(cell, inh, 1)
6742  {
6743  Relation childrel;
6744 
6745  /* find_all_inheritors already got lock */
6746  childrel = table_open(lfirst_oid(cell), NoLock);
6747  CheckTableNotInUse(childrel, "ALTER TABLE");
6748  table_close(childrel, NoLock);
6749  }
6750  list_free(inh);
6751  }
6752 }
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 CheckTableNotInUse(), 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 13662 of file tablecmds.c.

13663 {
13664  Assert(expr != NULL);
13665 
13666  for (;;)
13667  {
13668  /* only one varno, so no need to check that */
13669  if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13670  return false;
13671  else if (IsA(expr, RelabelType))
13672  expr = (Node *) ((RelabelType *) expr)->arg;
13673  else if (IsA(expr, CoerceToDomain))
13674  {
13675  CoerceToDomain *d = (CoerceToDomain *) expr;
13676 
13678  return true;
13679  expr = (Node *) d->arg;
13680  }
13681  else if (IsA(expr, FuncExpr))
13682  {
13683  FuncExpr *f = (FuncExpr *) expr;
13684 
13685  switch (f->funcid)
13686  {
13687  case F_TIMESTAMPTZ_TIMESTAMP:
13688  case F_TIMESTAMP_TIMESTAMPTZ:
13690  return true;
13691  else
13692  expr = linitial(f->args);
13693  break;
13694  default:
13695  return true;
13696  }
13697  }
13698  else
13699  return true;
13700  }
13701 }
bool TimestampTimestampTzRequiresRewrite(void)
Definition: timestamp.c:6261
void * arg
#define linitial(l)
Definition: pg_list.h:178
Oid funcid
Definition: primnodes.h:720
List * args
Definition: primnodes.h:738
Definition: primnodes.h:248
bool DomainHasConstraints(Oid type_id)
Definition: typcache.c:1400

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

4781 {
4782  List *wqueue = NIL;
4783  ListCell *lcmd;
4784 
4785  /* Phase 1: preliminary examination of commands, create work queue */
4786  foreach(lcmd, cmds)
4787  {
4788  AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4789 
4790  ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4791  }
4792 
4793  /* Close the relation, but keep lock until commit */
4794  relation_close(rel, NoLock);
4795 
4796  /* Phase 2: update system catalogs */
4797  ATRewriteCatalogs(&wqueue, lockmode, context);
4798 
4799  /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4800  ATRewriteTables(parsetree, &wqueue, lockmode, context);
4801 }
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5204
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4813
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5767

References ATPrepCmd(), ATRewriteCatalogs(), ATRewriteTables(), context, lfirst, NIL, NoLock, and relation_close().

Referenced by AlterTable(), and AlterTableInternal().

◆ ATDetachCheckNoForeignKeyRefs()

static void ATDetachCheckNoForeignKeyRefs ( Relation  partition)
static

Definition at line 20814 of file tablecmds.c.

20815 {
20816  List *constraints;
20817  ListCell *cell;
20818 
20819  constraints = GetParentedForeignKeyRefs(partition);
20820 
20821  foreach(cell, constraints)
20822  {
20823  Oid constrOid = lfirst_oid(cell);
20824  HeapTuple tuple;
20825  Form_pg_constraint constrForm;
20826  Relation rel;
20827  Trigger trig = {0};
20828 
20829  tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
20830  if (!HeapTupleIsValid(tuple))
20831  elog(ERROR, "cache lookup failed for constraint %u", constrOid);
20832  constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20833 
20834  Assert(OidIsValid(constrForm->conparentid));
20835  Assert(constrForm->confrelid == RelationGetRelid(partition));
20836 
20837  /* prevent data changes into the referencing table until commit */
20838  rel = table_open(constrForm->conrelid, ShareLock);
20839 
20840  trig.tgoid = InvalidOid;
20841  trig.tgname = NameStr(constrForm->conname);
20843  trig.tgisinternal = true;
20844  trig.tgconstrrelid = RelationGetRelid(partition);
20845  trig.tgconstrindid = constrForm->conindid;
20846  trig.tgconstraint = constrForm->oid;
20847  trig.tgdeferrable = false;
20848  trig.tginitdeferred = false;
20849  /* we needn't fill in remaining fields */
20850 
20851  RI_PartitionRemove_Check(&trig, rel, partition);
20852 
20853  ReleaseSysCache(tuple);
20854 
20855  table_close(rel, NoLock);
20856  }
20857 }
#define ShareLock
Definition: lockdefs.h:40
FormData_pg_constraint * Form_pg_constraint
void RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1738
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:20761
#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 18379 of file tablecmds.c.

18381 {
18382  ListCell *cur_item;
18383 
18384  foreach(cur_item, on_commits)
18385  {
18386  OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18387 
18388  if (!isCommit && oc->creating_subid == mySubid)
18389  {
18390  /* cur_item must be removed */
18392  pfree(oc);
18393  }
18394  else
18395  {
18396  /* cur_item must be preserved */
18397  if (oc->creating_subid == mySubid)
18398  oc->creating_subid = parentSubid;
18399  if (oc->deleting_subid == mySubid)
18400  oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18401  }
18402  }
18403 }
#define InvalidSubTransactionId
Definition: c.h:658
#define foreach_delete_current(lst, var_or_cell)
Definition: pg_list.h:391
SubTransactionId creating_subid
Definition: tablecmds.c:124
SubTransactionId deleting_subid
Definition: tablecmds.c:125
static List * on_commits
Definition: tablecmds.c:128

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

18348 {
18349  ListCell *cur_item;
18350 
18351  foreach(cur_item, on_commits)
18352  {
18353  OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18354 
18355  if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18357  {
18358  /* cur_item must be removed */
18360  pfree(oc);
18361  }
18362  else
18363  {
18364  /* cur_item must be preserved */
18367  }
18368  }
18369 }

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

7085 {
7086  Oid myrelid = RelationGetRelid(rel);
7087  ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7088  bool if_not_exists = (*cmd)->missing_ok;
7089  Relation pgclass,
7090  attrdesc;
7091  HeapTuple reltup;
7092  Form_pg_attribute attribute;
7093  int newattnum;
7094  char relkind;
7095  Expr *defval;
7096  List *children;
7097  ListCell *child;
7098  AlterTableCmd *childcmd;
7099  ObjectAddress address;
7100  TupleDesc tupdesc;
7101 
7102  /* since this function recurses, it could be driven to stack overflow */
7104 
7105  /* At top level, permission check was done in ATPrepCmd, else do it */
7106  if (recursing)
7107  ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7108 
7109  if (rel->rd_rel->relispartition && !recursing)
7110  ereport(ERROR,
7111  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7112  errmsg("cannot add column to a partition")));
7113 
7114  attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7115 
7116  /*
7117  * Are we adding the column to a recursion child? If so, check whether to
7118  * merge with an existing definition for the column. If we do merge, we
7119  * must not recurse. Children will already have the column, and recursing
7120  * into them would mess up attinhcount.
7121  */
7122  if (colDef->inhcount > 0)
7123  {
7124  HeapTuple tuple;
7125 
7126  /* Does child already have a column by this name? */
7127  tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7128  if (HeapTupleIsValid(tuple))
7129  {
7130  Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7131  Oid ctypeId;
7132  int32 ctypmod;
7133  Oid ccollid;
7134 
7135  /* Child column must match on type, typmod, and collation */
7136  typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7137  if (ctypeId != childatt->atttypid ||
7138  ctypmod != childatt->atttypmod)
7139  ereport(ERROR,
7140  (errcode(ERRCODE_DATATYPE_MISMATCH),
7141  errmsg("child table \"%s\" has different type for column \"%s\"",
7142  RelationGetRelationName(rel), colDef->colname)));
7143  ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7144  if (ccollid != childatt->attcollation)
7145  ereport(ERROR,
7146  (errcode(ERRCODE_COLLATION_MISMATCH),
7147  errmsg("child table \"%s\" has different collation for column \"%s\"",
7148  RelationGetRelationName(rel), colDef->colname),
7149  errdetail("\"%s\" versus \"%s\"",
7150  get_collation_name(ccollid),
7151  get_collation_name(childatt->attcollation))));
7152 
7153  /* Bump the existing child att's inhcount */
7154  childatt->attinhcount++;
7155  if (childatt->attinhcount < 0)
7156  ereport(ERROR,
7157  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7158  errmsg("too many inheritance parents"));
7159  CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7160 
7161  heap_freetuple(tuple);
7162 
7163  /* Inform the user about the merge */
7164  ereport(NOTICE,
7165  (errmsg("merging definition of column \"%s\" for child \"%s\"",
7166  colDef->colname, RelationGetRelationName(rel))));
7167 
7168  table_close(attrdesc, RowExclusiveLock);
7169 
7170  /* Make the child column change visible */
7172 
7173  return InvalidObjectAddress;
7174  }
7175  }
7176 
7177  /* skip if the name already exists and if_not_exists is true */
7178  if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7179  {
7180  table_close(attrdesc, RowExclusiveLock);
7181  return InvalidObjectAddress;
7182  }
7183 
7184  /*
7185  * Okay, we need to add the column, so go ahead and do parse
7186  * transformation. This can result in queueing up, or even immediately
7187  * executing, subsidiary operations (such as creation of unique indexes);
7188  * so we mustn't do it until we have made the if_not_exists check.
7189  *
7190  * When recursing, the command was already transformed and we needn't do
7191  * so again. Also, if context isn't given we can't transform. (That
7192  * currently happens only for AT_AddColumnToView; we expect that view.c
7193  * passed us a ColumnDef that doesn't need work.)
7194  */
7195  if (context != NULL && !recursing)
7196  {
7197  *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7198  cur_pass, context);
7199  Assert(*cmd != NULL);
7200  colDef = castNode(ColumnDef, (*cmd)->def);
7201  }
7202 
7203  /*
7204  * Regular inheritance children are independent enough not to inherit the
7205  * identity column from parent hence cannot recursively add identity
7206  * column if the table has inheritance children.
7207  *
7208  * Partitions, on the other hand, are integral part of a partitioned table
7209  * and inherit identity column. Hence propagate identity column down the
7210  * partition hierarchy.
7211  */
7212  if (colDef->identity &&
7213  recurse &&
7214  rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7215  find_inheritance_children(myrelid, NoLock) != NIL)
7216  ereport(ERROR,
7217  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7218  errmsg("cannot recursively add identity column to table that has child tables")));
7219 
7220  pgclass = table_open(RelationRelationId, RowExclusiveLock);
7221 
7222  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7223  if (!HeapTupleIsValid(reltup))
7224  elog(ERROR, "cache lookup failed for relation %u", myrelid);
7225  relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
7226 
7227  /* Determine the new attribute's number */
7228  newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
7229  if (newattnum > MaxHeapAttributeNumber)
7230  ereport(ERROR,
7231  (errcode(ERRCODE_TOO_MANY_COLUMNS),
7232  errmsg("tables can have at most %d columns",
7234 
7235  /*
7236  * Construct new attribute's pg_attribute entry.
7237  */
7238  tupdesc = BuildDescForRelation(list_make1(colDef));
7239 
7240  attribute = TupleDescAttr(tupdesc, 0);
7241 
7242  /* Fix up attribute number */
7243  attribute->attnum = newattnum;
7244 
7245  /* make sure datatype is legal for a column */
7246  CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7247  list_make1_oid(rel->rd_rel->reltype),
7248  0);
7249 
7250  InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7251 
7252  table_close(attrdesc, RowExclusiveLock);
7253 
7254  /*
7255  * Update pg_class tuple as appropriate
7256  */
7257  ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
7258 
7259  CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7260 
7261  heap_freetuple(reltup);
7262 
7263  /* Post creation hook for new attribute */
7264  InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7265 
7266  table_close(pgclass, RowExclusiveLock);
7267 
7268  /* Make the attribute's catalog entry visible */
7270 
7271  /*
7272  * Store the DEFAULT, if any, in the catalogs
7273  */
7274  if (colDef->raw_default)
7275  {
7276  RawColumnDefault *rawEnt;
7277 
7278  rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7279  rawEnt->attnum = attribute->attnum;
7280  rawEnt->raw_default = copyObject(colDef->raw_default);
7281 
7282  /*
7283  * Attempt to skip a complete table rewrite by storing the specified
7284  * DEFAULT value outside of the heap. This may be disabled inside
7285  * AddRelationNewConstraints if the optimization cannot be applied.
7286  */
7287  rawEnt->missingMode = (!colDef->generated);
7288 
7289  rawEnt->generated = colDef->generated;
7290 
7291  /*
7292  * This function is intended for CREATE TABLE, so it processes a
7293  * _list_ of defaults, but we just do one.
7294  */
7296  false, true, false, NULL);
7297 
7298  /* Make the additional catalog changes visible */
7300 
7301  /*
7302  * Did the request for a missing value work? If not we'll have to do a
7303  * rewrite
7304  */
7305  if (!rawEnt->missingMode)
7307  }
7308 
7309  /*
7310  * Tell Phase 3 to fill in the default expression, if there is one.
7311  *
7312  * If there is no default, Phase 3 doesn't have to do anything, because
7313  * that effectively means that the default is NULL. The heap tuple access
7314  * routines always check for attnum > # of attributes in tuple, and return
7315  * NULL if so, so without any modification of the tuple data we will get
7316  * the effect of NULL values in the new column.
7317  *
7318  * An exception occurs when the new column is of a domain type: the domain
7319  * might have a not-null constraint, or a check constraint that indirectly
7320  * rejects nulls. If there are any domain constraints then we construct
7321  * an explicit NULL default value that will be passed through
7322  * CoerceToDomain processing. (This is a tad inefficient, since it causes
7323  * rewriting the table which we really don't have to do, but the present
7324  * design of domain processing doesn't offer any simple way of checking
7325  * the constraints more directly.)
7326  *
7327  * Note: we use build_column_default, and not just the cooked default
7328  * returned by AddRelationNewConstraints, so that the right thing happens
7329  * when a datatype's default applies.
7330  *
7331  * Note: it might seem that this should happen at the end of Phase 2, so
7332  * that the effects of subsequent subcommands can be taken into account.
7333  * It's intentional that we do it now, though. The new column should be
7334  * filled according to what is said in the ADD COLUMN subcommand, so that
7335  * the effects are the same as if this subcommand had been run by itself
7336  * and the later subcommands had been issued in new ALTER TABLE commands.
7337  *
7338  * We can skip this entirely for relations without storage, since Phase 3
7339  * is certainly not going to touch them. System attributes don't have
7340  * interesting defaults, either.
7341  */
7342  if (RELKIND_HAS_STORAGE(relkind))
7343  {
7344  /*
7345  * For an identity column, we can't use build_column_default(),
7346  * because the sequence ownership isn't set yet. So do it manually.
7347  */
7348  if (colDef->identity)
7349  {
7351 
7352  nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7353  nve->typeId = attribute->atttypid;
7354 
7355  defval = (Expr *) nve;
7356 
7357  /* must do a rewrite for identity columns */
7359  }
7360  else
7361  defval = (Expr *) build_column_default(rel, attribute->attnum);
7362 
7363  if (!defval && DomainHasConstraints(attribute->atttypid))
7364  {
7365  Oid baseTypeId;
7366  int32 baseTypeMod;
7367  Oid baseTypeColl;
7368 
7369  baseTypeMod = attribute->atttypmod;
7370  baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7371  baseTypeColl = get_typcollation(baseTypeId);
7372  defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7373  defval = (Expr *) coerce_to_target_type(NULL,
7374  (Node *) defval,
7375  baseTypeId,
7376  attribute->atttypid,
7377  attribute->atttypmod,
7380  -1);
7381  if (defval == NULL) /* should not happen */
7382  elog(ERROR, "failed to coerce base type to domain");
7383  }
7384 
7385  if (defval)
7386  {
7388 
7390  newval->attnum = attribute->attnum;
7391  newval->expr = expression_planner(defval);
7392  newval->is_generated = (colDef->generated != '\0');
7393 
7394  tab->newvals = lappend(tab->newvals, newval);
7395  }
7396 
7397  if (DomainHasConstraints(attribute->atttypid))
7399 
7400  if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
7401  {
7402  /*
7403  * If the new column is NOT NULL, and there is no missing value,
7404  * tell Phase 3 it needs to check for NULLs.
7405  */
7406  tab->verify_new_notnull |= colDef->is_not_null;
7407  }
7408  }
7409 
7410  /*
7411  * Add needed dependency entries for the new column.
7412  */
7413  add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7414  add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7415 
7416  /*
7417  * Propagate to children as appropriate. Unlike most other ALTER
7418  * routines, we have to do this one level of recursion at a time; we can't
7419  * use find_all_inheritors to do it in one pass.
7420  */
7421  children =
7423 
7424  /*
7425  * If we are told not to recurse, there had better not be any child
7426  * tables; else the addition would put them out of step.
7427  */
7428  if (children && !recurse)
7429  ereport(ERROR,
7430  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7431  errmsg("column must be added to child tables too")));
7432 
7433  /* Children should see column as singly inherited */
7434  if (!recursing)
7435  {
7436  childcmd = copyObject(*cmd);
7437  colDef = castNode(ColumnDef, childcmd->def);
7438  colDef->inhcount = 1;
7439  colDef->is_local = false;
7440  }
7441  else
7442  childcmd = *cmd; /* no need to copy again */
7443 
7444  foreach(child, children)
7445  {
7446  Oid childrelid = lfirst_oid(child);
7447  Relation childrel;
7448  AlteredTableInfo *childtab;
7449 
7450  /* find_inheritance_children already got lock */
7451  childrel = table_open(childrelid, NoLock);
7452  CheckTableNotInUse(childrel, "ALTER TABLE");
7453 
7454  /* Find or create work queue entry for this table */
7455  childtab = ATGetQueueEntry(wqueue, childrel);
7456 
7457  /* Recurse to child; return value is ignored */
7458  ATExecAddColumn(wqueue, childtab, childrel,
7459  &childcmd, recurse, true,
7460  lockmode, cur_pass, context);
7461 
7462  table_close(childrel, NoLock);
7463  }
7464 
7465  ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7466  return address;
7467 }
#define AT_REWRITE_DEFAULT_VAL
Definition: event_trigger.h:35
#define newval
void CheckAttributeType(const char *attname, Oid atttypid, Oid attcollation, List *containing_rowtypes, int flags)
Definition: heap.c:548
void InsertPgAttributeTuples(Relation pg_attribute_rel, TupleDesc tupdesc, Oid new_rel_oid, const FormExtraData_pg_attribute tupdesc_extra[], CatalogIndexState indstate)
Definition: heap.c:702
#define MaxHeapAttributeNumber
Definition: htup_details.h:48
Oid get_typcollation(Oid typid)
Definition: lsyscache.c:3056
char * get_collation_name(Oid colloid)
Definition: lsyscache.c:1035
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition: lsyscache.c:2538
Const * makeNullConst(Oid consttype, int32 consttypmod, Oid constcollid)
Definition: makefuncs.c:339
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition: namespace.h:80
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:173
#define ObjectAddressSubSet(addr, class_id, object_id, object_sub_id)
Definition: objectaddress.h:33
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:78
void typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName, Oid *typeid_p, int32 *typmod_p)
Definition: parse_type.c:310
Oid GetColumnDefCollation(ParseState *pstate, const ColumnDef *coldef, Oid typeOid)
Definition: parse_type.c:540
#define list_make1_oid(x1)
Definition: pg_list.h:242
Expr * expression_planner(Expr *expr)
Definition: planner.c:6457
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:706
@ COERCION_ASSIGNMENT
Definition: primnodes.h:685
Node * build_column_default(Relation rel, int attrno)
bool verify_new_notnull
Definition: tablecmds.c:188
bool is_not_null
Definition: parsenodes.h:731
char identity
Definition: parsenodes.h:737
RangeVar * identitySequence
Definition: parsenodes.h:738
int inhcount
Definition: parsenodes.h:729
char * colname
Definition: parsenodes.h:726
TypeName * typeName
Definition: parsenodes.h:727
char generated
Definition: parsenodes.h:740
Node * raw_default
Definition: parsenodes.h:735
bool is_local
Definition: parsenodes.h:730
Node * raw_default
Definition: heap.h:30
AttrNumber attnum
Definition: heap.h:29
char generated
Definition: heap.h:32
bool missingMode
Definition: heap.h:31
TupleDesc rd_att
Definition: rel.h:112
HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname)
Definition: syscache.c:382
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
Definition: tablecmds.c:7527
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition: tablecmds.c:7545
TupleDesc BuildDescForRelation(const List *columns)
Definition: tablecmds.c:1307
static AlterTableCmd * ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5630
static bool check_for_column_name_collision(Relation rel, const char *colname, bool if_not_exists)
Definition: tablecmds.c:7474
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:7081

References add_column_collation_dependency(), add_column_datatype_dependency(), AddRelationNewConstraints(), Assert, AT_REWRITE_DEFAULT_VAL, ATGetQueueEntry(), ATParseTransformCmd(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_TABLE, RawColumnDefault::attnum, build_column_default(), BuildDescForRelation(), castNode, CatalogTupleUpdate(), check_for_column_name_collision(), check_stack_depth(), CheckAttributeType(), CheckTableNotInUse(), COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, ColumnDef::colname, CommandCounterIncrement(), context, copyObject, AlterTableCmd::def, DomainHasConstraints(), elog, ereport, errcode(), errdetail(), errmsg(), ERROR, expression_planner(), find_inheritance_children(), RawColumnDefault::generated, ColumnDef::generated, get_collation_name(), get_typcollation(), getBaseTypeAndTypmod(), GetColumnDefCollation(), 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, RawColumnDefault::missingMode, NameStr, newval, AlteredTableInfo::newvals, NIL, NoLock, NOTICE, ObjectAddressSubSet, ObjectIdGetDatum(), palloc(), palloc0(), RangeVarGetRelid, RawColumnDefault::raw_default, ColumnDef::raw_default, RelationData::rd_att, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, AlteredTableInfo::rewrite, RowExclusiveLock, SearchSysCacheCopy1, SearchSysCacheCopyAttName(), NextValueExpr::seqid, HeapTupleData::t_self, table_close(), table_open(), TupleDescAttr, NextValueExpr::typeId, ColumnDef::typeName, typenameTypeIdAndMod(), and AlteredTableInfo::verify_new_notnull.

Referenced by ATExecCmd().

◆ ATExecAddConstraint()

static ObjectAddress ATExecAddConstraint ( List **  wqueue,
AlteredTableInfo tab,
Relation  rel,
Constraint newConstraint,
bool  recurse,
bool  is_readd,
LOCKMODE  lockmode 
)
static

Definition at line 9612 of file tablecmds.c.

9615 {
9617 
9618  Assert(IsA(newConstraint, Constraint));
9619 
9620  /*
9621  * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9622  * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9623  * parse_utilcmd.c).
9624  */
9625  switch (newConstraint->contype)
9626  {
9627  case CONSTR_CHECK:
9628  case CONSTR_NOTNULL:
9629  address =
9630  ATAddCheckNNConstraint(wqueue, tab, rel,
9631  newConstraint, recurse, false, is_readd,
9632  lockmode);
9633  break;
9634 
9635  case CONSTR_FOREIGN:
9636 
9637  /*
9638  * Assign or validate constraint name
9639  */
9640  if (newConstraint->conname)
9641  {
9643  RelationGetRelid(rel),
9644  newConstraint->conname))
9645  ereport(ERROR,
9647  errmsg("constraint \"%s\" for relation \"%s\" already exists",
9648  newConstraint->conname,
9649  RelationGetRelationName(rel))));
9650  }
9651  else
9652  newConstraint->conname =
9655  "fkey",
9656  RelationGetNamespace(rel),
9657  NIL);
9658 
9659  address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9660  newConstraint,
9661  recurse, false,
9662  lockmode);
9663  break;
9664 
9665  default:
9666  elog(ERROR, "unrecognized constraint type: %d",
9667  (int) newConstraint->contype);
9668  }
9669 
9670  return address;
9671 }
@ CONSTR_CHECK
Definition: parsenodes.h:2713
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:9886

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

8203 {
8204  Relation attrelation;
8205  HeapTuple tuple;
8206  Form_pg_attribute attTup;
8208  ObjectAddress address;
8209  ColumnDef *cdef = castNode(ColumnDef, def);
8210  bool ispartitioned;
8211 
8212  ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8213  if (ispartitioned && !recurse)
8214  ereport(ERROR,
8215  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8216  errmsg("cannot add identity to a column of only the partitioned table"),
8217  errhint("Do not specify the ONLY keyword.")));
8218 
8219  if (rel->rd_rel->relispartition && !recursing)
8220  ereport(ERROR,
8221  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8222  errmsg("cannot add identity to a column of a partition"));
8223 
8224  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8225 
8226  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8227  if (!HeapTupleIsValid(tuple))
8228  ereport(ERROR,
8229  (errcode(ERRCODE_UNDEFINED_COLUMN),
8230  errmsg("column \"%s\" of relation \"%s\" does not exist",
8231  colName, RelationGetRelationName(rel))));
8232  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8233  attnum = attTup->attnum;
8234 
8235  /* Can't alter a system attribute */
8236  if (attnum <= 0)
8237  ereport(ERROR,
8238  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8239  errmsg("cannot alter system column \"%s\"",
8240  colName)));
8241 
8242  /*
8243  * Creating a column as identity implies NOT NULL, so adding the identity
8244  * to an existing column that is not NOT NULL would create a state that
8245  * cannot be reproduced without contortions.
8246  */
8247  if (!attTup->attnotnull)
8248  ereport(ERROR,
8249  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8250  errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8251  colName, RelationGetRelationName(rel))));
8252 
8253  if (attTup->attidentity)
8254  ereport(ERROR,
8255  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8256  errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8257  colName, RelationGetRelationName(rel))));
8258 
8259  if (attTup->atthasdef)
8260  ereport(ERROR,
8261  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8262  errmsg("column \"%s\" of relation \"%s\" already has a default value",
8263  colName, RelationGetRelationName(rel))));
8264 
8265  attTup->attidentity = cdef->identity;
8266  CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8267 
8268  InvokeObjectPostAlterHook(RelationRelationId,
8269  RelationGetRelid(rel),
8270  attTup->attnum);
8271  ObjectAddressSubSet(address, RelationRelationId,
8272  RelationGetRelid(rel), attnum);
8273  heap_freetuple(tuple);
8274 
8275  table_close(attrelation, RowExclusiveLock);
8276 
8277  /*
8278  * Recurse to propagate the identity column to partitions. Identity is
8279  * not inherited in regular inheritance children.
8280  */
8281  if (recurse && ispartitioned)
8282  {
8283  List *children;
8284  ListCell *lc;
8285 
8286  children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8287 
8288  foreach(lc, children)
8289  {
8290  Relation childrel;
8291 
8292  childrel = table_open(lfirst_oid(lc), NoLock);
8293  ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8294  table_close(childrel, NoLock);
8295  }
8296  }
8297 
8298  return address;
8299 }
int errhint(const char *fmt,...)
Definition: elog.c:1319
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8201

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

Referenced by ATExecCmd().

◆ ATExecAddIndex()

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

Definition at line 9436 of file tablecmds.c.

9438 {
9439  bool check_rights;
9440  bool skip_build;
9441  bool quiet;
9442  ObjectAddress address;
9443 
9444  Assert(IsA(stmt, IndexStmt));
9445  Assert(!stmt->concurrent);
9446 
9447  /* The IndexStmt has already been through transformIndexStmt */
9448  Assert(stmt->transformed);
9449 
9450  /* suppress schema rights check when rebuilding existing index */
9451  check_rights = !is_rebuild;
9452  /* skip index build if phase 3 will do it or we're reusing an old one */
9453  skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9454  /* suppress notices when rebuilding existing index */
9455  quiet = is_rebuild;
9456 
9457  address = DefineIndex(RelationGetRelid(rel),
9458  stmt,
9459  InvalidOid, /* no predefined OID */
9460  InvalidOid, /* no parent index */
9461  InvalidOid, /* no parent constraint */
9462  -1, /* total_parts unknown */
9463  true, /* is_alter_table */
9464  check_rights,
9465  false, /* check_not_in_use - we did it already */
9466  skip_build,
9467  quiet);
9468 
9469  /*
9470  * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9471  * new index instead of building from scratch. Restore associated fields.
9472  * This may store InvalidSubTransactionId in both fields, in which case
9473  * relcache.c will assume it can rebuild the relcache entry. Hence, do
9474  * this after the CCI that made catalog rows visible to any rebuild. The
9475  * DROP of the old edition of this index will have scheduled the storage
9476  * for deletion at commit, so cancel that pending deletion.
9477  */
9478  if (RelFileNumberIsValid(stmt->oldNumber))
9479  {
9480  Relation irel = index_open(address.objectId, NoLock);
9481 
9482  irel->rd_createSubid = stmt->oldCreateSubid;
9483  irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9484  RelationPreserveStorage(irel->rd_locator, true);
9485  index_close(irel, NoLock);
9486  }
9487 
9488  return address;
9489 }
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
ObjectAddress DefineIndex(Oid tableId, IndexStmt *stmt, Oid indexRelationId, Oid parentIndexId, Oid parentConstraintId, int total_parts, bool is_alter_table, bool check_rights, bool check_not_in_use, bool skip_build, bool quiet)
Definition: indexcmds.c:535
#define RelFileNumberIsValid(relnumber)
Definition: relpath.h:27
void RelationPreserveStorage(RelFileLocator rlocator, bool atCommit)
Definition: storage.c:251
SubTransactionId rd_firstRelfilelocatorSubid
Definition: rel.h:106
SubTransactionId rd_createSubid
Definition: rel.h:103
RelFileLocator rd_locator
Definition: rel.h:57

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

Referenced by ATExecCmd().

◆ ATExecAddIndexConstraint()

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

Definition at line 9520 of file tablecmds.c.

9522 {
9523  Oid index_oid = stmt->indexOid;
9524  Relation indexRel;
9525  char *indexName;
9526  IndexInfo *indexInfo;
9527  char *constraintName;
9528  char constraintType;
9529  ObjectAddress address;
9530  bits16 flags;
9531 
9532  Assert(IsA(stmt, IndexStmt));
9533  Assert(OidIsValid(index_oid));
9534  Assert(stmt->isconstraint);
9535 
9536  /*
9537  * Doing this on partitioned tables is not a simple feature to implement,
9538  * so let's punt for now.
9539  */
9540  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9541  ereport(ERROR,
9542  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9543  errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9544 
9545  indexRel = index_open(index_oid, AccessShareLock);
9546 
9547  indexName = pstrdup(RelationGetRelationName(indexRel));
9548 
9549  indexInfo = BuildIndexInfo(indexRel);
9550 
9551  /* this should have been checked at parse time */
9552  if (!indexInfo->ii_Unique)
9553  elog(ERROR, "index \"%s\" is not unique", indexName);
9554 
9555  /*
9556  * Determine name to assign to constraint. We require a constraint to
9557  * have the same name as the underlying index; therefore, use the index's
9558  * existing name as the default constraint name, and if the user
9559  * explicitly gives some other name for the constraint, rename the index
9560  * to match.
9561  */
9562  constraintName = stmt->idxname;
9563  if (constraintName == NULL)
9564  constraintName = indexName;
9565  else if (strcmp(constraintName, indexName) != 0)
9566  {
9567  ereport(NOTICE,
9568  (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9569  indexName, constraintName)));
9570  RenameRelationInternal(index_oid, constraintName, false, true);
9571  }
9572 
9573  /* Extra checks needed if making primary key */
9574  if (stmt->primary)
9575  index_check_primary_key(rel, indexInfo, true, stmt);
9576 
9577  /* Note we currently don't support EXCLUSION constraints here */
9578  if (stmt->primary)
9579  constraintType = CONSTRAINT_PRIMARY;
9580  else
9581  constraintType = CONSTRAINT_UNIQUE;
9582 
9583  /* Create the catalog entries for the constraint */
9586  (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9587  (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9588  (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9589 
9590  address = index_constraint_create(rel,
9591  index_oid,
9592  InvalidOid,
9593  indexInfo,
9594  constraintName,
9595  constraintType,
9596  flags,
9598  false); /* is_internal */
9599 
9600  index_close(indexRel, NoLock);
9601 
9602  return address;
9603 }
uint16 bits16
Definition: c.h:514
void index_check_primary_key(Relation heapRel, const IndexInfo *indexInfo, bool is_alter_table, const IndexStmt *stmt)
Definition: index.c:201
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:1881
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:2407
#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
char * pstrdup(const char *in)
Definition: mcxt.c:1695
bool ii_Unique
Definition: execnodes.h:197
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:4203

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

16167 {
16168  Relation parent_rel;
16169  List *children;
16170  ObjectAddress address;
16171  const char *trigger_name;
16172 
16173  /*
16174  * A self-exclusive lock is needed here. See the similar case in
16175  * MergeAttributes() for a full explanation.
16176  */
16177  parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
16178 
16179  /*
16180  * Must be owner of both parent and child -- child was checked by
16181  * ATSimplePermissions call in ATPrepCmd
16182  */
16184 
16185  /* Permanent rels cannot inherit from temporary ones */
16186  if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16187  child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16188  ereport(ERROR,
16189  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16190  errmsg("cannot inherit from temporary relation \"%s\"",
16191  RelationGetRelationName(parent_rel))));
16192 
16193  /* If parent rel is temp, it must belong to this session */
16194  if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16195  !parent_rel->rd_islocaltemp)
16196  ereport(ERROR,
16197  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16198  errmsg("cannot inherit from temporary relation of another session")));
16199 
16200  /* Ditto for the child */
16201  if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16202  !child_rel->rd_islocaltemp)
16203  ereport(ERROR,
16204  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16205  errmsg("cannot inherit to temporary relation of another session")));
16206 
16207  /* Prevent partitioned tables from becoming inheritance parents */
16208  if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16209  ereport(ERROR,
16210  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16211  errmsg("cannot inherit from partitioned table \"%s\"",
16212  parent->relname)));
16213 
16214  /* Likewise for partitions */
16215  if (parent_rel->rd_rel->relispartition)
16216  ereport(ERROR,
16217  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16218  errmsg("cannot inherit from a partition")));
16219 
16220  /*
16221  * Prevent circularity by seeing if proposed parent inherits from child.
16222  * (In particular, this disallows making a rel inherit from itself.)
16223  *
16224  * This is not completely bulletproof because of race conditions: in
16225  * multi-level inheritance trees, someone else could concurrently be
16226  * making another inheritance link that closes the loop but does not join
16227  * either of the rels we have locked. Preventing that seems to require
16228  * exclusive locks on the entire inheritance tree, which is a cure worse
16229  * than the disease. find_all_inheritors() will cope with circularity
16230  * anyway, so don't sweat it too much.
16231  *
16232  * We use weakest lock we can on child's children, namely AccessShareLock.
16233  */
16234  children = find_all_inheritors(RelationGetRelid(child_rel),
16235  AccessShareLock, NULL);
16236 
16237  if (list_member_oid(children, RelationGetRelid(parent_rel)))
16238  ereport(ERROR,
16239  (errcode(ERRCODE_DUPLICATE_TABLE),
16240  errmsg("circular inheritance not allowed"),
16241  errdetail("\"%s\" is already a child of \"%s\".",
16242  parent->relname,
16243  RelationGetRelationName(child_rel))));
16244 
16245  /*
16246  * If child_rel has row-level triggers with transition tables, we
16247  * currently don't allow it to become an inheritance child. See also
16248  * prohibitions in ATExecAttachPartition() and CreateTrigger().
16249  */
16250  trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
16251  if (trigger_name != NULL)
16252  ereport(ERROR,
16253  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16254  errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16255  trigger_name, RelationGetRelationName(child_rel)),
16256  errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16257 
16258  /* OK to create inheritance */
16259  CreateInheritance(child_rel, parent_rel, false);
16260 
16261  /*
16262  * If parent_rel has a primary key, then child_rel has not-null
16263  * constraints that make these columns as non nullable. Make those
16264  * constraints as inherited.
16265  */
16266  ATInheritAdjustNotNulls(parent_rel, child_rel, 1);
16267 
16268  ObjectAddressSet(address, RelationRelationId,
16269  RelationGetRelid(parent_rel));
16270 
16271  /* keep our lock on the parent relation until commit */
16272  table_close(parent_rel, NoLock);
16273 
16274  return address;
16275 }
char * relname
Definition: primnodes.h:82
TriggerDesc * trigdesc
Definition: rel.h:117
static void ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel, int inhcount)
Definition: tablecmds.c:17058
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:16285
const char * FindTriggerIncompatibleWithInheritance(TriggerDesc *trigdesc)
Definition: trigger.c:2272

References AccessShareLock, AT_AddInherit, ATInheritAdjustNotNulls(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_TABLE, CreateInheritance(), ereport, errcode(), errdetail(), errmsg(), ERROR, find_all_inheritors(), FindTriggerIncompatibleWithInheritance(), list_member_oid(), NoLock, ObjectAddressSet, RelationData::rd_islocaltemp, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RangeVar::relname, ShareUpdateExclusiveLock, table_close(), table_openrv(), and RelationData::trigdesc.

Referenced by ATExecCmd().

◆ ATExecAddOf()

static ObjectAddress ATExecAddOf ( Relation  rel,
const TypeName ofTypename,
LOCKMODE  lockmode 
)
static

Definition at line 17155 of file tablecmds.c.

17156 {
17157  Oid relid = RelationGetRelid(rel);
17158  Type typetuple;
17159  Form_pg_type typeform;
17160  Oid typeid;
17161  Relation inheritsRelation,
17162  relationRelation;
17163  SysScanDesc scan;
17164  ScanKeyData key;
17165  AttrNumber table_attno,
17166  type_attno;
17167  TupleDesc typeTupleDesc,
17168  tableTupleDesc;
17169  ObjectAddress tableobj,
17170  typeobj;
17171  HeapTuple classtuple;
17172 
17173  /* Validate the type. */
17174  typetuple = typenameType(NULL, ofTypename, NULL);
17175  check_of_type(typetuple);
17176  typeform = (Form_pg_type) GETSTRUCT(typetuple);
17177  typeid = typeform->oid;
17178 
17179  /* Fail if the table has any inheritance parents. */
17180  inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
17181  ScanKeyInit(&key,
17182  Anum_pg_inherits_inhrelid,
17183  BTEqualStrategyNumber, F_OIDEQ,
17184  ObjectIdGetDatum(relid));
17185  scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
17186  true, NULL, 1, &key);
17188  ereport(ERROR,
17189  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17190  errmsg("typed tables cannot inherit")));
17191  systable_endscan(scan);
17192  table_close(inheritsRelation, AccessShareLock);
17193 
17194  /*
17195  * Check the tuple descriptors for compatibility. Unlike inheritance, we
17196  * require that the order also match. However, attnotnull need not match.
17197  */
17198  typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
17199  tableTupleDesc = RelationGetDescr(rel);
17200  table_attno = 1;
17201  for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
17202  {
17203  Form_pg_attribute type_attr,
17204  table_attr;
17205  const char *type_attname,
17206  *table_attname;
17207 
17208  /* Get the next non-dropped type attribute. */
17209  type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
17210  if (type_attr->attisdropped)
17211  continue;
17212  type_attname = NameStr(type_attr->attname);
17213 
17214  /* Get the next non-dropped table attribute. */
17215  do
17216  {
17217  if (table_attno > tableTupleDesc->natts)
17218  ereport(ERROR,
17219  (errcode(ERRCODE_DATATYPE_MISMATCH),
17220  errmsg("table is missing column \"%s\"",
17221  type_attname)));
17222  table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
17223  table_attno++;
17224  } while (table_attr->attisdropped);
17225  table_attname = NameStr(table_attr->attname);
17226 
17227  /* Compare name. */
17228  if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
17229  ereport(ERROR,
17230  (errcode(ERRCODE_DATATYPE_MISMATCH),
17231  errmsg("table has column \"%s\" where type requires \"%s\"",
17232  table_attname, type_attname)));
17233 
17234  /* Compare type. */
17235  if (table_attr->atttypid != type_attr->atttypid ||
17236  table_attr->atttypmod != type_attr->atttypmod ||
17237  table_attr->attcollation != type_attr->attcollation)
17238  ereport(ERROR,
17239  (errcode(ERRCODE_DATATYPE_MISMATCH),
17240  errmsg("table \"%s\" has different type for column \"%s\"",
17241  RelationGetRelationName(rel), type_attname)));
17242  }
17243  ReleaseTupleDesc(typeTupleDesc);
17244 
17245  /* Any remaining columns at the end of the table had better be dropped. */
17246  for (; table_attno <= tableTupleDesc->natts; table_attno++)
17247  {
17248  Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
17249  table_attno - 1);
17250 
17251  if (!table_attr->attisdropped)
17252  ereport(ERROR,
17253  (errcode(ERRCODE_DATATYPE_MISMATCH),
17254  errmsg("table has extra column \"%s\"",
17255  NameStr(table_attr->attname))));
17256  }
17257 
17258  /* If the table was already typed, drop the existing dependency. */
17259  if (rel->rd_rel->reloftype)
17260  drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17262 
17263  /* Record a dependency on the new type. */
17264  tableobj.classId = RelationRelationId;
17265  tableobj.objectId = relid;
17266  tableobj.objectSubId = 0;
17267  typeobj.classId = TypeRelationId;
17268  typeobj.objectId = typeid;
17269  typeobj.objectSubId = 0;
17270  recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
17271 
17272  /* Update pg_class.reloftype */
17273  relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17274  classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17275  if (!HeapTupleIsValid(classtuple))
17276  elog(ERROR, "cache lookup failed for relation %u", relid);
17277  ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
17278  CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
17279 
17280  InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17281 
17282  heap_freetuple(classtuple);
17283  table_close(relationRelation, RowExclusiveLock);
17284 
17285  ReleaseSysCache(typetuple);
17286 
17287  return typeobj;
17288 }
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:17103
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:7014
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:122
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1833

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

9501 {
9502  ObjectAddress address;
9503 
9505 
9506  /* The CreateStatsStmt has already been through transformStatsStmt */
9507  Assert(stmt->transformed);
9508 
9509  address = CreateStatistics(stmt);
9510 
9511  return address;
9512 }
ObjectAddress CreateStatistics(CreateStatsStmt *stmt)
Definition: statscmds.c:62

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

Referenced by ATExecCmd().

◆ ATExecAlterColumnGenericOptions()

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

Definition at line 14865 of file tablecmds.c.

14869 {
14870  Relation ftrel;
14871  Relation attrel;
14872  ForeignServer *server;
14873  ForeignDataWrapper *fdw;
14874  HeapTuple tuple;
14875  HeapTuple newtuple;
14876  bool isnull;
14877  Datum repl_val[Natts_pg_attribute];
14878  bool repl_null[Natts_pg_attribute];
14879  bool repl_repl[Natts_pg_attribute];
14880  Datum datum;
14881  Form_pg_foreign_table fttableform;
14882  Form_pg_attribute atttableform;
14884  ObjectAddress address;
14885 
14886  if (options == NIL)
14887  return InvalidObjectAddress;
14888 
14889  /* First, determine FDW validator associated to the foreign table. */
14890  ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14891  tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14892  if (!HeapTupleIsValid(tuple))
14893  ereport(ERROR,
14894  (errcode(ERRCODE_UNDEFINED_OBJECT),
14895  errmsg("foreign table \"%s\" does not exist",
14896  RelationGetRelationName(rel))));
14897  fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14898  server = GetForeignServer(fttableform->ftserver);
14899  fdw = GetForeignDataWrapper(server->fdwid);
14900 
14901  table_close(ftrel, AccessShareLock);
14902  ReleaseSysCache(tuple);
14903 
14904  attrel = table_open(AttributeRelationId, RowExclusiveLock);
14905  tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14906  if (!HeapTupleIsValid(tuple))
14907  ereport(ERROR,
14908  (errcode(ERRCODE_UNDEFINED_COLUMN),
14909  errmsg("column \"%s\" of relation \"%s\" does not exist",
14910  colName, RelationGetRelationName(rel))));
14911 
14912  /* Prevent them from altering a system attribute */
14913  atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14914  attnum = atttableform->attnum;
14915  if (attnum <= 0)
14916  ereport(ERROR,
14917  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14918  errmsg("cannot alter system column \"%s\"", colName)));
14919 
14920 
14921  /* Initialize buffers for new tuple values */
14922  memset(repl_val, 0, sizeof(repl_val));
14923  memset(repl_null, false, sizeof(repl_null));
14924  memset(repl_repl, false, sizeof(repl_repl));
14925 
14926  /* Extract the current options */
14927  datum = SysCacheGetAttr(ATTNAME,
14928  tuple,
14929  Anum_pg_attribute_attfdwoptions,
14930  &isnull);
14931  if (isnull)
14932  datum = PointerGetDatum(NULL);
14933 
14934  /* Transform the options */
14935  datum = transformGenericOptions(AttributeRelationId,
14936  datum,
14937  options,
14938  fdw->fdwvalidator);
14939 
14940  if (PointerIsValid(DatumGetPointer(datum)))
14941  repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
14942  else
14943  repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
14944 
14945  repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
14946 
14947  /* Everything looks good - update the tuple */
14948 
14949  newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
14950  repl_val, repl_null, repl_repl);
14951 
14952  CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
14953 
14954  InvokeObjectPostAlterHook(RelationRelationId,
14955  RelationGetRelid(rel),
14956  atttableform->attnum);
14957  ObjectAddressSubSet(address, RelationRelationId,
14958  RelationGetRelid(rel), attnum);
14959 
14960  ReleaseSysCache(tuple);
14961 
14962  table_close(attrel, RowExclusiveLock);
14963 
14964  heap_freetuple(newtuple);
14965 
14966  return address;
14967 }
#define PointerIsValid(pointer)
Definition: c.h:763
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition: foreign.c:36
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:110
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
Definition: foreigncmds.c:110
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition: heaptuple.c:1209
FormData_pg_foreign_table * Form_pg_foreign_table
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
Oid rd_id
Definition: rel.h:113
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:479
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition: syscache.c:359

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

Referenced by ATExecCmd().

◆ ATExecAlterColumnType()

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

Definition at line 13709 of file tablecmds.c.

13711 {
13712  char *colName = cmd->name;
13713  ColumnDef *def = (ColumnDef *) cmd->def;
13714  TypeName *typeName = def->typeName;
13715  HeapTuple heapTup;
13716  Form_pg_attribute attTup,
13717  attOldTup;
13719  HeapTuple typeTuple;
13720  Form_pg_type tform;
13721  Oid targettype;
13722  int32 targettypmod;
13723  Oid targetcollid;
13724  Node *defaultexpr;
13725  Relation attrelation;
13726  Relation depRel;
13727  ScanKeyData key[3];
13728  SysScanDesc scan;
13729  HeapTuple depTup;
13730  ObjectAddress address;
13731 
13732  /*
13733  * Clear all the missing values if we're rewriting the table, since this
13734  * renders them pointless.
13735  */
13736  if (tab->rewrite)
13737  {
13738  Relation newrel;
13739 
13740  newrel = table_open(RelationGetRelid(rel), NoLock);
13741  RelationClearMissing(newrel);
13742  relation_close(newrel, NoLock);
13743  /* make sure we don't conflict with later attribute modifications */
13745  }
13746 
13747  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13748 
13749  /* Look up the target column */
13750  heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13751  if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13752  ereport(ERROR,
13753  (errcode(ERRCODE_UNDEFINED_COLUMN),
13754  errmsg("column \"%s\" of relation \"%s\" does not exist",
13755  colName, RelationGetRelationName(rel))));
13756  attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13757  attnum = attTup->attnum;
13758  attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13759 
13760  /* Check for multiple ALTER TYPE on same column --- can't cope */
13761  if (attTup->atttypid != attOldTup->atttypid ||
13762  attTup->atttypmod != attOldTup->atttypmod)
13763  ereport(ERROR,
13764  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13765  errmsg("cannot alter type of column \"%s\" twice",
13766  colName)));
13767 
13768  /* Look up the target type (should not fail, since prep found it) */
13769  typeTuple = typenameType(NULL, typeName, &targettypmod);
13770  tform = (Form_pg_type) GETSTRUCT(typeTuple);
13771  targettype = tform->oid;
13772  /* And the collation */
13773  targetcollid = GetColumnDefCollation(NULL, def, targettype);
13774 
13775  /*
13776  * If there is a default expression for the column, get it and ensure we
13777  * can coerce it to the new datatype. (We must do this before changing
13778  * the column type, because build_column_default itself will try to
13779  * coerce, and will not issue the error message we want if it fails.)
13780  *
13781  * We remove any implicit coercion steps at the top level of the old
13782  * default expression; this has been agreed to satisfy the principle of
13783  * least surprise. (The conversion to the new column type should act like
13784  * it started from what the user sees as the stored expression, and the
13785  * implicit coercions aren't going to be shown.)
13786  */
13787  if (attTup->atthasdef)
13788  {
13789  defaultexpr = build_column_default(rel, attnum);
13790  Assert(defaultexpr);
13791  defaultexpr = strip_implicit_coercions(defaultexpr);
13792  defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13793  defaultexpr, exprType(defaultexpr),
13794  targettype, targettypmod,
13797  -1);
13798  if (defaultexpr == NULL)
13799  {
13800  if (attTup->attgenerated)
13801  ereport(ERROR,
13802  (errcode(ERRCODE_DATATYPE_MISMATCH),
13803  errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13804  colName, format_type_be(targettype))));
13805  else
13806  ereport(ERROR,
13807  (errcode(ERRCODE_DATATYPE_MISMATCH),
13808  errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13809  colName, format_type_be(targettype))));
13810  }
13811  }
13812  else
13813  defaultexpr = NULL;
13814 
13815  /*
13816  * Find everything that depends on the column (constraints, indexes, etc),
13817  * and record enough information to let us recreate the objects.
13818  *
13819  * The actual recreation does not happen here, but only after we have
13820  * performed all the individual ALTER TYPE operations. We have to save
13821  * the info before executing ALTER TYPE, though, else the deparser will
13822  * get confused.
13823  */
13825 
13826  /*
13827  * Now scan for dependencies of this column on other things. The only
13828  * things we should find are the dependency on the column datatype and
13829  * possibly a collation dependency. Those can be removed.
13830  */
13831  depRel = table_open(DependRelationId, RowExclusiveLock);
13832 
13833  ScanKeyInit(&key[0],
13834  Anum_pg_depend_classid,
13835  BTEqualStrategyNumber, F_OIDEQ,
13836  ObjectIdGetDatum(RelationRelationId));
13837  ScanKeyInit(&key[1],
13838  Anum_pg_depend_objid,
13839  BTEqualStrategyNumber, F_OIDEQ,
13841  ScanKeyInit(&key[2],
13842  Anum_pg_depend_objsubid,
13843  BTEqualStrategyNumber, F_INT4EQ,
13845 
13846  scan = systable_beginscan(depRel, DependDependerIndexId, true,
13847  NULL, 3, key);
13848 
13849  while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13850  {
13851  Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13852  ObjectAddress foundObject;
13853 
13854  foundObject.classId = foundDep->refclassid;
13855  foundObject.objectId = foundDep->refobjid;
13856  foundObject.objectSubId = foundDep->refobjsubid;
13857 
13858  if (foundDep->deptype != DEPENDENCY_NORMAL)
13859  elog(ERROR, "found unexpected dependency type '%c'",
13860  foundDep->deptype);
13861  if (!(foundDep->refclassid == TypeRelationId &&
13862  foundDep->refobjid == attTup->atttypid) &&
13863  !(foundDep->refclassid == CollationRelationId &&
13864  foundDep->refobjid == attTup->attcollation))
13865  elog(ERROR, "found unexpected dependency for column: %s",
13866  getObjectDescription(&foundObject, false));
13867 
13868  CatalogTupleDelete(depRel, &depTup->t_self);
13869  }
13870 
13871  systable_endscan(scan);
13872 
13873  table_close(depRel, RowExclusiveLock);
13874 
13875  /*
13876  * Here we go --- change the recorded column type and collation. (Note
13877  * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13878  * fix up the missing value if any.
13879  */
13880  if (attTup->atthasmissing)
13881  {
13882  Datum missingval;
13883  bool missingNull;
13884 
13885  /* if rewrite is true the missing value should already be cleared */
13886  Assert(tab->rewrite == 0);
13887 
13888  /* Get the missing value datum */
13889  missingval = heap_getattr(heapTup,
13890  Anum_pg_attribute_attmissingval,
13891  attrelation->rd_att,
13892  &missingNull);
13893 
13894  /* if it's a null array there is nothing to do */
13895 
13896  if (!missingNull)
13897  {
13898  /*
13899  * Get the datum out of the array and repack it in a new array
13900  * built with the new type data. We assume that since the table
13901  * doesn't need rewriting, the actual Datum doesn't need to be
13902  * changed, only the array metadata.
13903  */
13904 
13905  int one = 1;
13906  bool isNull;
13907  Datum valuesAtt[Natts_pg_attribute] = {0};
13908  bool nullsAtt[Natts_pg_attribute] = {0};
13909  bool replacesAtt[Natts_pg_attribute] = {0};
13910  HeapTuple newTup;
13911 
13912  missingval = array_get_element(missingval,
13913  1,
13914  &one,
13915  0,
13916  attTup->attlen,
13917  attTup->attbyval,
13918  attTup->attalign,
13919  &isNull);
13920  missingval = PointerGetDatum(construct_array(&missingval,
13921  1,
13922  targettype,
13923  tform->typlen,
13924  tform->typbyval,
13925  tform->typalign));
13926 
13927  valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
13928  replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13929  nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13930 
13931  newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13932  valuesAtt, nullsAtt, replacesAtt);
13933  heap_freetuple(heapTup);
13934  heapTup = newTup;
13935  attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13936  }
13937  }
13938 
13939  attTup->atttypid = targettype;
13940  attTup->atttypmod = targettypmod;
13941  attTup->attcollation = targetcollid;
13942  if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
13943  ereport(ERROR,
13944  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13945  errmsg("too many array dimensions"));
13946  attTup->attndims = list_length(typeName->arrayBounds);
13947  attTup->attlen = tform->typlen;
13948  attTup->attbyval = tform->typbyval;
13949  attTup->attalign = tform->typalign;
13950  attTup->attstorage = tform->typstorage;
13951  attTup->attcompression = InvalidCompressionMethod;
13952 
13953  ReleaseSysCache(typeTuple);
13954 
13955  CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13956 
13957  table_close(attrelation, RowExclusiveLock);
13958 
13959  /* Install dependencies on new datatype and collation */
13962 
13963  /*
13964  * Drop any pg_statistic entry for the column, since it's now wrong type
13965  */
13967 
13968  InvokeObjectPostAlterHook(RelationRelationId,
13969  RelationGetRelid(rel), attnum);
13970 
13971  /*
13972  * Update the default, if present, by brute force --- remove and re-add
13973  * the default. Probably unsafe to take shortcuts, since the new version
13974  * may well have additional dependencies. (It's okay to do this now,
13975  * rather than after other ALTER TYPE commands, since the default won't
13976  * depend on other column types.)
13977  */
13978  if (defaultexpr)
13979  {
13980  /*
13981  * If it's a GENERATED default, drop its dependency records, in
13982  * particular its INTERNAL dependency on the column, which would
13983  * otherwise cause dependency.c to refuse to perform the deletion.
13984  */
13985  if (attTup->attgenerated)
13986  {
13987  Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
13988 
13989  if (!OidIsValid(attrdefoid))
13990  elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
13991  RelationGetRelid(rel), attnum);
13992  (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
13993  }
13994 
13995  /*
13996  * Make updates-so-far visible, particularly the new pg_attribute row
13997  * which will be updated again.
13998  */
14000 
14001  /*
14002  * We use RESTRICT here for safety, but at present we do not expect
14003  * anything to depend on the default.
14004  */
14006  true);
14007 
14008  StoreAttrDefault(rel, attnum, defaultexpr, true, false);
14009  }
14010 
14011  ObjectAddressSubSet(address, RelationRelationId,
14012  RelationGetRelid(rel), attnum);
14013 
14014  /* Cleanup */
14015  heap_freetuple(heapTup);
14016 
14017  return address;
14018 }
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3354
Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
Definition: arrayfuncs.c:1820
#define PG_INT16_MAX
Definition: c.h:586
void RelationClearMissing(Relation rel)
Definition: heap.c:1937
void RemoveStatistics(Oid relid, AttrNumber attnum)
Definition: heap.c:3282
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:792
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
Node * strip_implicit_coercions(Node *node)
Definition: nodeFuncs.c:700
char * getObjectDescription(const ObjectAddress *object, bool missing_ok)
@ DROP_RESTRICT
Definition: parsenodes.h:2336
Oid GetAttrDefaultOid(Oid relid, AttrNumber attnum)
Definition: pg_attrdef.c:339
Oid StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr, bool is_internal, bool add_column_mode)
Definition: pg_attrdef.c:46
void RemoveAttrDefault(Oid relid, AttrNumber attnum, DropBehavior behavior, bool complain, bool internal)
Definition: pg_attrdef.c:213
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:300
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:212
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
Definition: tablecmds.c:14026
#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 ( Relation  rel,
AlterTableCmd cmd,
bool  recurse,
bool  recursing,
LOCKMODE  lockmode 
)
static

Definition at line 11712 of file tablecmds.c.

11714 {
11715  Constraint *cmdcon;
11716  Relation conrel;
11717  Relation tgrel;
11718  SysScanDesc scan;
11719  ScanKeyData skey[3];
11720  HeapTuple contuple;
11721  Form_pg_constraint currcon;
11722  ObjectAddress address;
11723  List *otherrelids = NIL;
11724  ListCell *lc;
11725 
11726  cmdcon = castNode(Constraint, cmd->def);
11727 
11728  conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11729  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11730 
11731  /*
11732  * Find and check the target constraint
11733  */
11734  ScanKeyInit(&skey[0],
11735  Anum_pg_constraint_conrelid,
11736  BTEqualStrategyNumber, F_OIDEQ,
11738  ScanKeyInit(&skey[1],
11739  Anum_pg_constraint_contypid,
11740  BTEqualStrategyNumber, F_OIDEQ,
11742  ScanKeyInit(&skey[2],
11743  Anum_pg_constraint_conname,
11744  BTEqualStrategyNumber, F_NAMEEQ,
11745  CStringGetDatum(cmdcon->conname));
11746  scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11747  true, NULL, 3, skey);
11748 
11749  /* There can be at most one matching row */
11750  if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11751  ereport(ERROR,
11752  (errcode(ERRCODE_UNDEFINED_OBJECT),
11753  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11754  cmdcon->conname, RelationGetRelationName(rel))));
11755 
11756  currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11757  if (currcon->contype != CONSTRAINT_FOREIGN)
11758  ereport(ERROR,
11759  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11760  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11761  cmdcon->conname, RelationGetRelationName(rel))));
11762 
11763  /*
11764  * If it's not the topmost constraint, raise an error.
11765  *
11766  * Altering a non-topmost constraint leaves some triggers untouched, since
11767  * they are not directly connected to this constraint; also, pg_dump would
11768  * ignore the deferrability status of the individual constraint, since it
11769  * only dumps topmost constraints. Avoid these problems by refusing this
11770  * operation and telling the user to alter the parent constraint instead.
11771  */
11772  if (OidIsValid(currcon->conparentid))
11773  {
11774  HeapTuple tp;
11775  Oid parent = currcon->conparentid;
11776  char *ancestorname = NULL;
11777  char *ancestortable = NULL;
11778 
11779  /* Loop to find the topmost constraint */
11780  while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11781  {
11783 
11784  /* If no parent, this is the constraint we want */
11785  if (!OidIsValid(contup->conparentid))
11786  {
11787  ancestorname = pstrdup(NameStr(contup->conname));
11788  ancestortable = get_rel_name(contup->conrelid);
11789  ReleaseSysCache(tp);
11790  break;
11791  }
11792 
11793  parent = contup->conparentid;
11794  ReleaseSysCache(tp);
11795  }
11796 
11797  ereport(ERROR,
11798  (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11799  cmdcon->conname, RelationGetRelationName(rel)),
11800  ancestorname && ancestortable ?
11801  errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11802  cmdcon->conname, ancestorname, ancestortable) : 0,
11803  errhint("You may alter the constraint it derives from instead.")));
11804  }
11805 
11806  /*
11807  * Do the actual catalog work. We can skip changing if already in the
11808  * desired state, but not if a partitioned table: partitions need to be
11809  * processed regardless, in case they had the constraint locally changed.
11810  */
11811  address = InvalidObjectAddress;
11812  if (currcon->condeferrable != cmdcon->deferrable ||
11813  currcon->condeferred != cmdcon->initdeferred ||
11814  rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11815  {
11816  if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11817  &otherrelids, lockmode))
11818  ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11819  }
11820 
11821  /*
11822  * ATExecAlterConstrRecurse already invalidated relcache for the relations
11823  * having the constraint itself; here we also invalidate for relations
11824  * that have any triggers that are part of the constraint.
11825  */
11826  foreach(lc, otherrelids)
11828 
11829  systable_endscan(scan);
11830 
11831  table_close(tgrel, RowExclusiveLock);
11832  table_close(conrel, RowExclusiveLock);
11833 
11834  return address;
11835 }
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1419
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:11849

References ATExecAlterConstrRecurse(), BTEqualStrategyNumber, CacheInvalidateRelcacheByRelid(), castNode, Constraint::conname, CStringGetDatum(), AlterTableCmd::def, Constraint::deferrable, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, get_rel_name(), GETSTRUCT, HeapTupleIsValid, Constraint::initdeferred, InvalidObjectAddress, InvalidOid, lfirst_oid, NameStr, NIL, 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().

◆ ATExecAlterConstrRecurse()

static bool ATExecAlterConstrRecurse ( Constraint cmdcon,
Relation  conrel,
Relation  tgrel,
Relation  rel,
HeapTuple  contuple,
List **  otherrelids,
LOCKMODE  lockmode 
)
static

Definition at line 11849 of file tablecmds.c.

11852 {
11853  Form_pg_constraint currcon;
11854  Oid conoid;
11855  Oid refrelid;
11856  bool changed = false;
11857 
11858  /* since this function recurses, it could be driven to stack overflow */
11860 
11861  currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11862  conoid = currcon->oid;
11863  refrelid = currcon->confrelid;
11864 
11865  /*
11866  * Update pg_constraint with the flags from cmdcon.
11867  *
11868  * If called to modify a constraint that's already in the desired state,
11869  * silently do nothing.
11870  */
11871  if (currcon->condeferrable != cmdcon->deferrable ||
11872  currcon->condeferred != cmdcon->initdeferred)
11873  {
11874  HeapTuple copyTuple;
11875  Form_pg_constraint copy_con;
11876  HeapTuple tgtuple;
11877  ScanKeyData tgkey;
11878  SysScanDesc tgscan;
11879 
11880  copyTuple = heap_copytuple(contuple);
11881  copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11882  copy_con->condeferrable = cmdcon->deferrable;
11883  copy_con->condeferred = cmdcon->initdeferred;
11884  CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
11885 
11886  InvokeObjectPostAlterHook(ConstraintRelationId,
11887  conoid, 0);
11888 
11889  heap_freetuple(copyTuple);
11890  changed = true;
11891 
11892  /* Make new constraint flags visible to others */
11894 
11895  /*
11896  * Now we need to update the multiple entries in pg_trigger that
11897  * implement the constraint.
11898  */
11899  ScanKeyInit(&tgkey,
11900  Anum_pg_trigger_tgconstraint,
11901  BTEqualStrategyNumber, F_OIDEQ,
11902  ObjectIdGetDatum(conoid));
11903  tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11904  NULL, 1, &tgkey);
11905  while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11906  {
11907  Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11908  Form_pg_trigger copy_tg;
11909  HeapTuple tgCopyTuple;
11910 
11911  /*
11912  * Remember OIDs of other relation(s) involved in FK constraint.
11913  * (Note: it's likely that we could skip forcing a relcache inval
11914  * for other rels that don't have a trigger whose properties
11915  * change, but let's be conservative.)
11916  */
11917  if (tgform->tgrelid != RelationGetRelid(rel))
11918  *otherrelids = list_append_unique_oid(*otherrelids,
11919  tgform->tgrelid);
11920 
11921  /*
11922  * Update deferrability of RI_FKey_noaction_del,
11923  * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11924  * triggers, but not others; see createForeignKeyActionTriggers
11925  * and CreateFKCheckTrigger.
11926  */
11927  if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11928  tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11929  tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11930  tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11931  continue;
11932 
11933  tgCopyTuple = heap_copytuple(tgtuple);
11934  copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11935 
11936  copy_tg->tgdeferrable = cmdcon->deferrable;
11937  copy_tg->tginitdeferred = cmdcon->initdeferred;
11938  CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11939 
11940  InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11941 
11942  heap_freetuple(tgCopyTuple);
11943  }
11944 
11945  systable_endscan(tgscan);
11946  }
11947 
11948  /*
11949  * If the table at either end of the constraint is partitioned, we need to
11950  * recurse and handle every constraint that is a child of this one.
11951  *
11952  * (This assumes that the recurse flag is forcibly set for partitioned
11953  * tables, and not set for legacy inheritance, though we don't check for
11954  * that here.)
11955  */
11956  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11957  get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11958  {
11959  ScanKeyData pkey;
11960  SysScanDesc pscan;
11961  HeapTuple childtup;
11962 
11963  ScanKeyInit(&pkey,
11964  Anum_pg_constraint_conparentid,
11965  BTEqualStrategyNumber, F_OIDEQ,
11966  ObjectIdGetDatum(conoid));
11967 
11968  pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11969  true, NULL, 1, &pkey);
11970 
11971  while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11972  {
11973  Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
11974  Relation childrel;
11975 
11976  childrel = table_open(childcon->conrelid, lockmode);
11977  ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11978  otherrelids, lockmode);
11979  table_close(childrel, NoLock);
11980  }
11981 
11982  systable_endscan(pscan);
11983  }
11984 
11985  return changed;
11986 }
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:776
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1360
List * list_append_unique_oid(List *list, Oid datum)
Definition: list.c:1380
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:80

References BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleUpdate(), check_stack_depth(), Constraint::deferrable, get_rel_relkind(), GETSTRUCT, heap_copytuple(), heap_freetuple(), HeapTupleIsValid, Constraint::initdeferred, InvokeObjectPostAlterHook, list_append_unique_oid(), NoLock, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetRelid, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecAlterConstraint().

◆ ATExecAttachPartition()

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

Definition at line 19177 of file tablecmds.c.

19179 {
19180  Relation attachrel,
19181  catalog;
19182  List *attachrel_children;
19183  List *partConstraint;
19184  SysScanDesc scan;
19185  ScanKeyData skey;
19186  AttrNumber attno;
19187  int natts;
19188  TupleDesc tupleDesc;
19189  ObjectAddress address;
19190  const char *trigger_name;
19191  Oid defaultPartOid;
19192  List *partBoundConstraint;
19193  ParseState *pstate = make_parsestate(NULL);
19194 
19195  pstate->p_sourcetext = context->queryString;
19196 
19197  /*
19198  * We must lock the default partition if one exists, because attaching a
19199  * new partition will change its partition constraint.
19200  */
19201  defaultPartOid =
19203  if (OidIsValid(defaultPartOid))
19204  LockRelationOid(defaultPartOid, AccessExclusiveLock);
19205 
19206  attachrel = table_openrv(cmd->name, AccessExclusiveLock);
19207 
19208  /*
19209  * XXX I think it'd be a good idea to grab locks on all tables referenced
19210  * by FKs at this point also.
19211  */
19212 
19213  /*
19214  * Must be owner of both parent and source table -- parent was checked by
19215  * ATSimplePermissions call in ATPrepCmd
19216  */
19218 
19219  /* A partition can only have one parent */
19220  if (attachrel->rd_rel->relispartition)
19221  ereport(ERROR,
19222  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19223  errmsg("\"%s\" is already a partition",
19224  RelationGetRelationName(attachrel))));
19225 
19226  if (OidIsValid(attachrel->rd_rel->reloftype))
19227  ereport(ERROR,
19228  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19229  errmsg("cannot attach a typed table as partition")));
19230 
19231  /*
19232  * Table being attached should not already be part of inheritance; either
19233  * as a child table...
19234  */
19235  catalog = table_open(InheritsRelationId, AccessShareLock);
19236  ScanKeyInit(&skey,
19237  Anum_pg_inherits_inhrelid,
19238  BTEqualStrategyNumber, F_OIDEQ,
19239  ObjectIdGetDatum(RelationGetRelid(attachrel)));
19240  scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
19241  NULL, 1, &skey);
19243  ereport(ERROR,
19244  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19245  errmsg("cannot attach inheritance child as partition")));
19246  systable_endscan(scan);
19247 
19248  /* ...or as a parent table (except the case when it is partitioned) */
19249  ScanKeyInit(&skey,
19250  Anum_pg_inherits_inhparent,
19251  BTEqualStrategyNumber, F_OIDEQ,
19252  ObjectIdGetDatum(RelationGetRelid(attachrel)));
19253  scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
19254  1, &skey);
19255  if (HeapTupleIsValid(systable_getnext(scan)) &&
19256  attachrel->rd_rel->relkind == RELKIND_RELATION)
19257  ereport(ERROR,
19258  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19259  errmsg("cannot attach inheritance parent as partition")));
19260  systable_endscan(scan);
19261  table_close(catalog, AccessShareLock);
19262 
19263  /*
19264  * Prevent circularity by seeing if rel is a partition of attachrel. (In
19265  * particular, this disallows making a rel a partition of itself.)
19266  *
19267  * We do that by checking if rel is a member of the list of attachrel's
19268  * partitions provided the latter is partitioned at all. We want to avoid
19269  * having to construct this list again, so we request the strongest lock
19270  * on all partitions. We need the strongest lock, because we may decide
19271  * to scan them if we find out that the table being attached (or its leaf
19272  * partitions) may contain rows that violate the partition constraint. If
19273  * the table has a constraint that would prevent such rows, which by
19274  * definition is present in all the partitions, we need not scan the
19275  * table, nor its partitions. But we cannot risk a deadlock by taking a
19276  * weaker lock now and the stronger one only when needed.
19277  */
19278  attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
19279  AccessExclusiveLock, NULL);
19280  if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
19281  ereport(ERROR,
19282  (errcode(ERRCODE_DUPLICATE_TABLE),
19283  errmsg("circular inheritance not allowed"),
19284  errdetail("\"%s\" is already a child of \"%s\".",
19286  RelationGetRelationName(attachrel))));
19287 
19288  /* If the parent is permanent, so must be all of its partitions. */
19289  if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
19290  attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
19291  ereport(ERROR,
19292  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19293  errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19294  RelationGetRelationName(rel))));
19295 
19296  /* Temp parent cannot have a partition that is itself not a temp */
19297  if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19298  attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
19299  ereport(ERROR,
19300  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19301  errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19302  RelationGetRelationName(rel))));
19303 
19304  /* If the parent is temp, it must belong to this session */
19305  if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19306  !rel->rd_islocaltemp)
19307  ereport(ERROR,
19308  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19309  errmsg("cannot attach as partition of temporary relation of another session")));
19310 
19311  /* Ditto for the partition */
19312  if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19313  !attachrel->rd_islocaltemp)
19314  ereport(ERROR,
19315  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19316  errmsg("cannot attach temporary relation of another session as partition")));
19317 
19318  /*
19319  * Check if attachrel has any identity columns or any columns that aren't
19320  * in the parent.
19321  */
19322  tupleDesc = RelationGetDescr(attachrel);
19323  natts = tupleDesc->natts;
19324  for (attno = 1; attno <= natts; attno++)
19325  {
19326  Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
19327  char *attributeName = NameStr(attribute->attname);
19328 
19329  /* Ignore dropped */
19330  if (attribute->attisdropped)
19331  continue;
19332 
19333  if (attribute->attidentity)
19334  ereport(ERROR,
19335  errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19336  errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19337  RelationGetRelationName(attachrel), attributeName),
19338  errdetail("The new partition may not contain an identity column."));
19339 
19340  /* Try to find the column in parent (matching on column name) */
19341  if (!SearchSysCacheExists2(ATTNAME,
19343  CStringGetDatum(attributeName)))
19344  ereport(ERROR,
19345  (errcode(ERRCODE_DATATYPE_MISMATCH),
19346  errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19347  RelationGetRelationName(attachrel), attributeName,
19349  errdetail("The new partition may contain only the columns present in parent.")));
19350  }
19351 
19352  /*
19353  * If child_rel has row-level triggers with transition tables, we
19354  * currently don't allow it to become a partition. See also prohibitions
19355  * in ATExecAddInherit() and CreateTrigger().
19356  */
19357  trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
19358  if (trigger_name != NULL)
19359  ereport(ERROR,
19360  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19361  errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19362  trigger_name, RelationGetRelationName(attachrel)),
19363  errdetail("ROW triggers with transition tables are not supported on partitions.")));
19364 
19365  /*
19366  * Check that the new partition's bound is valid and does not overlap any
19367  * of existing partitions of the parent - note that it does not return on
19368  * error.
19369  */
19371  cmd->bound, pstate);
19372 
19373  /* Attach a new partition to the partitioned table. */
19374  attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
19375 
19376  /*
19377  * Generate partition constraint from the partition bound specification.
19378  * If the parent itself is a partition, make sure to include its
19379  * constraint as well.
19380  */
19381  partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
19382  partConstraint = list_concat(partBoundConstraint,
19384 
19385  /* Skip validation if there are no constraints to validate. */
19386  if (partConstraint)
19387  {
19388  /*
19389  * Run the partition quals through const-simplification similar to
19390  * check constraints. We skip canonicalize_qual, though, because
19391  * partition quals should be in canonical form already.
19392  */
19393  partConstraint =
19394  (List *) eval_const_expressions(NULL,
19395  (Node *) partConstraint);
19396 
19397  /* XXX this sure looks wrong */
19398  partConstraint = list_make1(make_ands_explicit(partConstraint));
19399 
19400  /*
19401  * Adjust the generated constraint to match this partition's attribute
19402  * numbers.
19403  */
19404  partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
19405  rel);
19406 
19407  /* Validate partition constraints against the table being attached. */
19408  QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
19409  false);
19410  }
19411 
19412  /*
19413  * If we're attaching a partition other than the default partition and a
19414  * default one exists, then that partition's partition constraint changes,
19415  * so add an entry to the work queue to validate it, too. (We must not do
19416  * this when the partition being attached is the default one; we already
19417  * did it above!)
19418  */
19419  if (OidIsValid(defaultPartOid))
19420  {
19421  Relation defaultrel;
19422  List *defPartConstraint;
19423 
19424  Assert(!cmd->bound->is_default);
19425 
19426  /* we already hold a lock on the default partition */
19427  defaultrel = table_open(defaultPartOid, NoLock);
19428  defPartConstraint =
19429  get_proposed_default_constraint(partBoundConstraint);
19430 
19431  /*
19432  * Map the Vars in the constraint expression from rel's attnos to
19433  * defaultrel's.
19434  */
19435  defPartConstraint =
19436  map_partition_varattnos(defPartConstraint,
19437  1, defaultrel, rel);
19438  QueuePartitionConstraintValidation(wqueue, defaultrel,
19439  defPartConstraint, true);
19440 
19441  /* keep our lock until commit. */
19442  table_close(defaultrel, NoLock);
19443  }
19444 
19445  ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
19446 
19447  /*
19448  * If the partition we just attached is partitioned itself, invalidate
19449  * relcache for all descendent partitions too to ensure that their
19450  * rd_partcheck expression trees are rebuilt; partitions already locked at
19451  * the beginning of this function.
19452  */
19453  if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19454  {
19455  ListCell *l;
19456 
19457  foreach(l, attachrel_children)
19458  {
19460  }
19461  }
19462 
19463  /* keep our lock until commit */
19464  table_close(attachrel, NoLock);
19465 
19466  return address;
19467 }
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2254
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:726
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:39
void check_new_partition_bound(char *relname, Relation parent, PartitionBoundSpec *spec, ParseState *pstate)
Definition: partbounds.c:2896
List * get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:249
List * RelationGetPartitionQual(Relation rel)
Definition: partcache.c:277
Oid get_default_oid_from_partdesc(PartitionDesc partdesc)
Definition: partdesc.c:459
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 * p_sourcetext
Definition: parse_node.h:193
PartitionBoundSpec * bound
Definition: parsenodes.h:958
RangeVar * name
Definition: parsenodes.h:957
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition: syscache.h:97
static void attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
Definition: tablecmds.c:19150
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
Definition: tablecmds.c:19073

References AccessExclusiveLock, AccessShareLock, Assert, AT_AttachPartition, ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_TABLE, attachPartitionTable(), PartitionCmd::bound, BTEqualStrategyNumber, CacheInvalidateRelcacheByRelid(), check_new_partition_bound(), context, 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(), 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, QueuePartitionConstraintValidation(), RelationData::rd_islocaltemp, RelationData::rd_rel, 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 20452 of file tablecmds.c.

20453 {
20454  Relation partIdx;
20455  Relation partTbl;
20456  Relation parentTbl;
20457  ObjectAddress address;
20458  Oid partIdxId;
20459  Oid currParent;
20461 
20462  /*
20463  * We need to obtain lock on the index 'name' to modify it, but we also
20464  * need to read its owning table's tuple descriptor -- so we need to lock
20465  * both. To avoid deadlocks, obtain lock on the table before doing so on
20466  * the index. Furthermore, we need to examine the parent table of the
20467  * partition, so lock that one too.
20468  */
20469  state.partitionOid = InvalidOid;
20470  state.parentTblOid = parentIdx->rd_index->indrelid;
20471  state.lockedParentTbl = false;
20472  partIdxId =
20475  (void *) &state);
20476  /* Not there? */
20477  if (!OidIsValid(partIdxId))
20478  ereport(ERROR,
20479  (errcode(ERRCODE_UNDEFINED_OBJECT),
20480  errmsg("index \"%s\" does not exist", name->relname)));
20481 
20482  /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20483  partIdx = relation_open(partIdxId, AccessExclusiveLock);
20484 
20485  /* we already hold locks on both tables, so this is safe: */
20486  parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
20487  partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
20488 
20489  ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
20490 
20491  /* Silently do nothing if already in the right state */
20492  currParent = partIdx->rd_rel->relispartition ?
20493  get_partition_parent(partIdxId, false) : InvalidOid;
20494  if (currParent != RelationGetRelid(parentIdx))
20495  {
20496  IndexInfo *childInfo;
20497  IndexInfo *parentInfo;
20498  AttrMap *attmap;
20499  bool found;
20500  int i;
20501  PartitionDesc partDesc;
20502  Oid constraintOid,
20503  cldConstrId = InvalidOid;
20504 
20505  /*
20506  * If this partition already has an index attached, refuse the
20507  * operation.
20508  */
20509  refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
20510 
20511  if (OidIsValid(currParent))
20512  ereport(ERROR,
20513  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20514  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20515  RelationGetRelationName(partIdx),
20516  RelationGetRelationName(parentIdx)),
20517  errdetail("Index \"%s\" is already attached to another index.",
20518  RelationGetRelationName(partIdx))));
20519 
20520  /* Make sure it indexes a partition of the other index's table */
20521  partDesc = RelationGetPartitionDesc(parentTbl, true);
20522  found = false;
20523  for (i = 0; i < partDesc->nparts; i++)
20524  {
20525  if (partDesc->oids[i] == state.partitionOid)
20526  {
20527  found = true;
20528  break;
20529  }
20530  }
20531  if (!found)
20532  ereport(ERROR,
20533  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20534  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20535  RelationGetRelationName(partIdx),
20536  RelationGetRelationName(parentIdx)),
20537  errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20538  RelationGetRelationName(partIdx),
20539  RelationGetRelationName(parentTbl))));
20540 
20541  /* Ensure the indexes are compatible */
20542  childInfo = BuildIndexInfo(partIdx);
20543  parentInfo = BuildIndexInfo(parentIdx);
20544  attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
20545  RelationGetDescr(parentTbl),
20546  false);
20547  if (!CompareIndexInfo(childInfo, parentInfo,
20548  partIdx->rd_indcollation,
20549  parentIdx->rd_indcollation,
20550  partIdx->rd_opfamily,
20551  parentIdx->rd_opfamily,
20552  attmap))
20553  ereport(ERROR,
20554  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20555  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20556  RelationGetRelationName(partIdx),
20557  RelationGetRelationName(parentIdx)),
20558  errdetail("The index definitions do not match.")));
20559 
20560  /*
20561  * If there is a constraint in the parent, make sure there is one in
20562  * the child too.
20563  */
20564  constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
20565  RelationGetRelid(parentIdx));
20566 
20567  if (OidIsValid(constraintOid))
20568  {
20569  cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
20570  partIdxId);
20571  if (!OidIsValid(cldConstrId))
20572  ereport(ERROR,
20573  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20574  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20575  RelationGetRelationName(partIdx),
20576  RelationGetRelationName(parentIdx)),
20577  errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20578  RelationGetRelationName(parentIdx),
20579  RelationGetRelationName(parentTbl),
20580  RelationGetRelationName(partIdx))));
20581  }
20582 
20583  /*
20584  * If it's a primary key, make sure the columns in the partition are
20585  * NOT NULL.
20586  */
20587  if (parentIdx->rd_index->indisprimary)
20588  verifyPartitionIndexNotNull(childInfo, partTbl);
20589 
20590  /* All good -- do it */
20591  IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
20592  if (OidIsValid(constraintOid))
20593  ConstraintSetParentConstraint(cldConstrId, constraintOid,
20594  RelationGetRelid(partTbl));
20595 
20596  free_attrmap(attmap);
20597 
20598  validatePartitionedIndex(parentIdx, parentTbl);
20599  }
20600 
20601  relation_close(parentTbl, AccessShareLock);
20602  /* keep these locks till commit */
20603  relation_close(partTbl, NoLock);
20604  relation_close(partIdx, NoLock);
20605 
20606  return address;
20607 }
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:2514
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition: indexcmds.c:4396
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition: partition.c:53
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
Form_pg_index rd_index
Definition: rel.h:192
Oid * rd_opfamily
Definition: rel.h:207
Oid * rd_indcollation
Definition: rel.h:217
Definition: regguts.h:323
static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx)
Definition: tablecmds.c:20739
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:20398
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
Definition: tablecmds.c:20637
static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
Definition: tablecmds.c:20614
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 14982 of file tablecmds.c.

14983 {
14984  Relation target_rel;
14985  Relation class_rel;
14986  HeapTuple tuple;
14987  Form_pg_class tuple_class;
14988 
14989  /*
14990  * Get exclusive lock till end of transaction on the target table. Use
14991  * relation_open so that we can work on indexes and sequences.
14992  */
14993  target_rel = relation_open(relationOid, lockmode);
14994 
14995  /* Get its pg_class tuple, too */
14996  class_rel = table_open(RelationRelationId, RowExclusiveLock);
14997 
14998  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
14999  if (!HeapTupleIsValid(tuple))
15000  elog(ERROR, "cache lookup failed for relation %u", relationOid);
15001  tuple_class = (Form_pg_class) GETSTRUCT(tuple);
15002 
15003  /* Can we change the ownership of this tuple? */
15004  switch (tuple_class->relkind)
15005  {
15006  case RELKIND_RELATION:
15007  case RELKIND_VIEW:
15008  case RELKIND_MATVIEW:
15009  case RELKIND_FOREIGN_TABLE:
15010  case RELKIND_PARTITIONED_TABLE:
15011  /* ok to change owner */
15012  break;
15013  case RELKIND_INDEX:
15014  if (!recursing)
15015  {
15016  /*
15017  * Because ALTER INDEX OWNER used to be allowed, and in fact
15018  * is generated by old versions of pg_dump, we give a warning
15019  * and do nothing rather than erroring out. Also, to avoid
15020  * unnecessary chatter while restoring those old dumps, say
15021  * nothing at all if the command would be a no-op anyway.
15022  */
15023  if (tuple_class->relowner != newOwnerId)
15024  ereport(WARNING,
15025  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15026  errmsg("cannot change owner of index \"%s\"",
15027  NameStr(tuple_class->relname)),
15028  errhint("Change the ownership of the index's table instead.")));
15029  /* quick hack to exit via the no-op path */
15030  newOwnerId = tuple_class->relowner;
15031  }
15032  break;
15033  case RELKIND_PARTITIONED_INDEX:
15034  if (recursing)
15035  break;
15036  ereport(ERROR,
15037  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15038  errmsg("cannot change owner of index \"%s\"",
15039  NameStr(tuple_class->relname)),
15040  errhint("Change the ownership of the index's table instead.")));
15041  break;
15042  case RELKIND_SEQUENCE:
15043  if (!recursing &&
15044  tuple_class->relowner != newOwnerId)
15045  {
15046  /* if it's an owned sequence, disallow changing it by itself */
15047  Oid tableId;
15048  int32 colId;
15049 
15050  if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
15051  sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
15052  ereport(ERROR,
15053  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15054  errmsg("cannot change owner of sequence \"%s\"",
15055  NameStr(tuple_class->relname)),
15056  errdetail("Sequence \"%s\" is linked to table \"%s\".",
15057  NameStr(tuple_class->relname),
15058  get_rel_name(tableId))));
15059  }
15060  break;
15061  case RELKIND_COMPOSITE_TYPE:
15062  if (recursing)
15063  break;
15064  ereport(ERROR,
15065  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15066  errmsg("\"%s\" is a composite type",
15067  NameStr(tuple_class->relname)),
15068  /* translator: %s is an SQL ALTER command */
15069  errhint("Use %s instead.",
15070  "ALTER TYPE")));
15071  break;
15072  case RELKIND_TOASTVALUE:
15073  if (recursing)
15074  break;
15075  /* FALL THRU */
15076  default:
15077  ereport(ERROR,
15078  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15079  errmsg("cannot change owner of relation \"%s\"",
15080  NameStr(tuple_class->relname)),
15081  errdetail_relkind_not_supported(tuple_class->relkind)));
15082  }
15083 
15084  /*
15085  * If the new owner is the same as the existing owner, consider the
15086  * command to have succeeded. This is for dump restoration purposes.
15087  */
15088  if (tuple_class->relowner != newOwnerId)
15089  {
15090  Datum repl_val[Natts_pg_class];
15091  bool repl_null[Natts_pg_class];
15092  bool repl_repl[Natts_pg_class];
15093  Acl *newAcl;
15094  Datum aclDatum;
15095  bool isNull;
15096  HeapTuple newtuple;
15097 
15098  /* skip permission checks when recursing to index or toast table */
15099  if (!recursing)
15100  {
15101  /* Superusers can always do it */
15102  if (!superuser())
15103  {
15104  Oid namespaceOid = tuple_class->relnamespace;
15105  AclResult aclresult;
15106 
15107  /* Otherwise, must be owner of the existing object */
15108  if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
15110  RelationGetRelationName(target_rel));
15111 
15112  /* Must be able to become new owner */
15113  check_can_set_role(GetUserId(), newOwnerId);
15114 
15115  /* New owner must have CREATE privilege on namespace */
15116  aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
15117  ACL_CREATE);
15118  if (aclresult != ACLCHECK_OK)
15119  aclcheck_error(aclresult, OBJECT_SCHEMA,
15120  get_namespace_name(namespaceOid));
15121  }
15122  }
15123 
15124  memset(repl_null, false, sizeof(repl_null));
15125  memset(repl_repl, false, sizeof(repl_repl));
15126 
15127  repl_repl[Anum_pg_class_relowner - 1] = true;
15128  repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
15129 
15130  /*
15131  * Determine the modified ACL for the new owner. This is only
15132  * necessary when the ACL is non-null.
15133  */
15134  aclDatum = SysCacheGetAttr(RELOID, tuple,
15135  Anum_pg_class_relacl,
15136  &isNull);
15137  if (!isNull)
15138  {
15139  newAcl = aclnewowner(DatumGetAclP(aclDatum),
15140  tuple_class->relowner, newOwnerId);
15141  repl_repl[Anum_pg_class_relacl - 1] = true;
15142  repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
15143  }
15144 
15145  newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
15146 
15147  CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
15148 
15149  heap_freetuple(newtuple);
15150 
15151  /*
15152  * We must similarly update any per-column ACLs to reflect the new
15153  * owner; for neatness reasons that's split out as a subroutine.
15154  */
15155  change_owner_fix_column_acls(relationOid,
15156  tuple_class->relowner,
15157  newOwnerId);
15158 
15159  /*
15160  * Update owner dependency reference, if any. A composite type has
15161  * none, because it's tracked for the pg_type entry instead of here;
15162  * indexes and TOAST tables don't have their own entries either.
15163  */
15164  if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
15165  tuple_class->relkind != RELKIND_INDEX &&
15166  tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
15167  tuple_class->relkind != RELKIND_TOASTVALUE)
15168  changeDependencyOnOwner(RelationRelationId, relationOid,
15169  newOwnerId);
15170 
15171  /*
15172  * Also change the ownership of the table's row type, if it has one
15173  */
15174  if (OidIsValid(tuple_class->reltype))
15175  AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
15176 
15177  /*
15178  * If we are operating on a table or materialized view, also change
15179  * the ownership of any indexes and sequences that belong to the
15180  * relation, as well as its toast table (if it has one).
15181  */
15182  if (tuple_class->relkind == RELKIND_RELATION ||
15183  tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
15184  tuple_class->relkind == RELKIND_MATVIEW ||
15185  tuple_class->relkind == RELKIND_TOASTVALUE)
15186  {
15187  List *index_oid_list;
15188  ListCell *i;
15189 
15190  /* Find all the indexes belonging to this relation */
15191  index_oid_list = RelationGetIndexList(target_rel);
15192 
15193  /* For each index, recursively change its ownership */
15194  foreach(i, index_oid_list)
15195  ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
15196 
15197  list_free(index_oid_list);
15198  }
15199 
15200  /* If it has a toast table, recurse to change its ownership */
15201  if (tuple_class->reltoastrelid != InvalidOid)
15202  ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
15203  true, lockmode);
15204 
15205  /* If it has dependent sequences, recurse to change them too */
15206  change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
15207  }
15208 
15209  InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
15210 
15211  ReleaseSysCache(tuple);
15212  table_close(class_rel, RowExclusiveLock);
15213  relation_close(target_rel, NoLock);
15214 }
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition: acl.c:1096
void check_can_set_role(Oid member, Oid role)
Definition: acl.c:5185
#define DatumGetAclP(X)
Definition: acl.h:120
#define WARNING
Definition: elog.h:36
@ OBJECT_SCHEMA
Definition: parsenodes.h:2299
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
Definition: pg_shdepend.c:308
bool superuser(void)
Definition: superuser.c:46
void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:14982
static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
Definition: tablecmds.c:15288
static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
Definition: tablecmds.c:15223
void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
Definition: typecmds.c:3989

References ACL_CREATE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, aclnewowner(), AlterTypeOwnerInternal(), 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(), ATExecCmd(), change_owner_recurse_to_sequences(), and shdepReassignOwned().

◆ ATExecClusterOn()

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

Definition at line 15357 of file tablecmds.c.

15358 {
15359  Oid indexOid;
15360  ObjectAddress address;
15361 
15362  indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15363 
15364  if (!OidIsValid(indexOid))
15365  ereport(ERROR,
15366  (errcode(ERRCODE_UNDEFINED_OBJECT),
15367  errmsg("index \"%s\" for table \"%s\" does not exist",
15368  indexName, RelationGetRelationName(rel))));
15369 
15370  /* Check index is valid to cluster on */
15371  check_index_is_clusterable(rel, indexOid, lockmode);
15372 
15373  /* And do the work */
15374  mark_index_clustered(rel, indexOid, false);
15375 
15376  ObjectAddressSet(address,
15377  RelationRelationId, indexOid);
15378 
15379  return address;
15380 }
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition: cluster.c:500
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition: cluster.c:560

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

5281 {
5283  Relation rel = tab->rel;
5284 
5285  switch (cmd->subtype)
5286  {
5287  case AT_AddColumn: /* ADD COLUMN */
5288  case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5289  address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5290  cmd->recurse, false,
5291  lockmode, cur_pass, context);
5292  break;
5293  case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5294  address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5295  break;
5296  case AT_CookedColumnDefault: /* add a pre-cooked default */
5297  address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5298  break;
5299  case AT_AddIdentity:
5300  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5301  cur_pass, context);
5302  Assert(cmd != NULL);
5303  address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5304  break;
5305  case AT_SetIdentity:
5306  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5307  cur_pass, context);
5308  Assert(cmd != NULL);
5309  address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5310  break;
5311  case AT_DropIdentity:
5312  address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5313  break;
5314  case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5315  address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5316  break;
5317  case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5318  address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5319  cmd->recurse, false, NULL, lockmode);
5320  break;
5321  case AT_SetAttNotNull: /* set pg_attribute.attnotnull */
5322  address = ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
5323  break;
5324  case AT_SetExpression:
5325  address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5326  break;
5327  case AT_DropExpression:
5328  address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5329  break;
5330  case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5331  address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5332  break;
5333  case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5334  address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5335  break;
5336  case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5337  address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5338  break;
5339  case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5340  address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5341  break;
5342  case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5343  address = ATExecSetCompression(rel, cmd->name, cmd->def,
5344  lockmode);
5345  break;
5346  case AT_DropColumn: /* DROP COLUMN */
5347  address = ATExecDropColumn(wqueue, rel, cmd->name,
5348  cmd->behavior, cmd->recurse, false,
5349  cmd->missing_ok, lockmode,
5350  NULL);
5351  break;
5352  case AT_AddIndex: /* ADD INDEX */
5353  address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5354  lockmode);
5355  break;
5356  case AT_ReAddIndex: /* ADD INDEX */
5357  address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5358  lockmode);
5359  break;
5360  case AT_ReAddStatistics: /* ADD STATISTICS */
5361  address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5362  true, lockmode);
5363  break;
5364  case AT_AddConstraint: /* ADD CONSTRAINT */
5365  /* Transform the command only during initial examination */
5366  if (cur_pass == AT_PASS_ADD_CONSTR)
5367  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5368  cmd->recurse, lockmode,
5369  cur_pass, context);
5370  /* Depending on constraint type, might be no more work to do now */
5371  if (cmd != NULL)
5372  address =
5373  ATExecAddConstraint(wqueue, tab, rel,
5374  (Constraint *) cmd->def,
5375  cmd->recurse, false, lockmode);
5376  break;
5377  case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5378  address =
5379  ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5380  true, true, lockmode);
5381  break;
5382  case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5383  * constraint */
5384  address =
5385  AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5386  ((AlterDomainStmt *) cmd->def)->def,
5387  NULL);
5388  break;
5389  case AT_ReAddComment: /* Re-add existing comment */
5390  address = CommentObject((CommentStmt *) cmd->def);
5391  break;
5392  case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5393  address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5394  lockmode);
5395  break;
5396  case AT_AlterConstraint: /* ALTER CONSTRAINT */
5397  address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5398  break;
5399  case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5400  address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5401  false, lockmode);
5402  break;
5403  case AT_DropConstraint: /* DROP CONSTRAINT */
5404  ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5405  cmd->recurse,
5406  cmd->missing_ok, lockmode);
5407  break;
5408  case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5409  /* parse transformation was done earlier */
5410  address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5411  break;
5412  case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5413  address =
5415  (List *) cmd->def, lockmode);
5416  break;
5417  case AT_ChangeOwner: /* ALTER OWNER */
5419  get_rolespec_oid(cmd->newowner, false),
5420  false, lockmode);
5421  break;
5422  case AT_ClusterOn: /* CLUSTER ON */
5423  address = ATExecClusterOn(rel, cmd->name, lockmode);
5424  break;
5425  case AT_DropCluster: /* SET WITHOUT CLUSTER */
5426  ATExecDropCluster(rel, lockmode);
5427  break;
5428  case AT_SetLogged: /* SET LOGGED */
5429  case AT_SetUnLogged: /* SET UNLOGGED */
5430  break;
5431  case AT_DropOids: /* SET WITHOUT OIDS */
5432  /* nothing to do here, oid columns don't exist anymore */
5433  break;
5434  case AT_SetAccessMethod: /* SET ACCESS METHOD */
5435 
5436  /*
5437  * Only do this for partitioned tables, for which this is just a
5438  * catalog change. Tables with storage are handled by Phase 3.
5439  */
5440  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5441  tab->chgAccessMethod)
5443  break;
5444  case AT_SetTableSpace: /* SET TABLESPACE */
5445 
5446  /*
5447  * Only do this for partitioned tables and indexes, for which this
5448  * is just a catalog change. Other relation types which have
5449  * storage are handled by Phase 3.
5450  */
5451  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5452  rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5454 
5455  break;
5456  case AT_SetRelOptions: /* SET (...) */
5457  case AT_ResetRelOptions: /* RESET (...) */
5458  case AT_ReplaceRelOptions: /* replace entire option list */
5459  ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5460  break;
5461  case AT_EnableTrig: /* ENABLE TRIGGER name */
5462  ATExecEnableDisableTrigger(rel, cmd->name,
5463  TRIGGER_FIRES_ON_ORIGIN, false,
5464  cmd->recurse,
5465  lockmode);
5466  break;
5467  case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5468  ATExecEnableDisableTrigger(rel, cmd->name,
5469  TRIGGER_FIRES_ALWAYS, false,
5470  cmd->recurse,
5471  lockmode);
5472  break;
5473  case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5474  ATExecEnableDisableTrigger(rel, cmd->name,
5475  TRIGGER_FIRES_ON_REPLICA, false,
5476  cmd->recurse,
5477  lockmode);
5478  break;
5479  case AT_DisableTrig: /* DISABLE TRIGGER name */
5480  ATExecEnableDisableTrigger(rel, cmd->name,
5481  TRIGGER_DISABLED, false,
5482  cmd->recurse,
5483  lockmode);
5484  break;
5485  case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5486  ATExecEnableDisableTrigger(rel, NULL,
5487  TRIGGER_FIRES_ON_ORIGIN, false,
5488  cmd->recurse,
5489  lockmode);
5490  break;
5491  case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5492  ATExecEnableDisableTrigger(rel, NULL,
5493  TRIGGER_DISABLED, false,
5494  cmd->recurse,
5495  lockmode);
5496  break;
5497  case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5498  ATExecEnableDisableTrigger(rel, NULL,
5500  cmd->recurse,
5501  lockmode);
5502  break;
5503  case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5504  ATExecEnableDisableTrigger(rel, NULL,
5505  TRIGGER_DISABLED, true,
5506  cmd->recurse,
5507  lockmode);
5508  break;
5509 
5510  case AT_EnableRule: /* ENABLE RULE name */
5511  ATExecEnableDisableRule(rel, cmd->name,
5512  RULE_FIRES_ON_ORIGIN, lockmode);
5513  break;
5514  case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5515  ATExecEnableDisableRule(rel, cmd->name,
5516  RULE_FIRES_ALWAYS, lockmode);
5517  break;
5518  case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5519  ATExecEnableDisableRule(rel, cmd->name,
5520  RULE_FIRES_ON_REPLICA, lockmode);
5521  break;
5522  case AT_DisableRule: /* DISABLE RULE name */
5523  ATExecEnableDisableRule(rel, cmd->name,
5524  RULE_DISABLED, lockmode);
5525  break;
5526 
5527  case AT_AddInherit:
5528  address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5529  break;
5530  case AT_DropInherit:
5531  address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5532  break;
5533  case AT_AddOf:
5534  address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5535  break;
5536  case AT_DropOf:
5537  ATExecDropOf(rel, lockmode);
5538  break;
5539  case AT_ReplicaIdentity:
5540  ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5541  break;
5542  case AT_EnableRowSecurity:
5543  ATExecSetRowSecurity(rel, true);
5544  break;
5545  case AT_DisableRowSecurity:
5546  ATExecSetRowSecurity(rel, false);
5547  break;
5548  case AT_ForceRowSecurity:
5549  ATExecForceNoForceRowSecurity(rel, true);
5550  break;
5551  case AT_NoForceRowSecurity:
5552  ATExecForceNoForceRowSecurity(rel, false);
5553  break;
5554  case AT_GenericOptions:
5555  ATExecGenericOptions(rel, (List *) cmd->def);
5556  break;
5557  case AT_AttachPartition:
5558  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5559  cur_pass, context);
5560  Assert(cmd != NULL);
5561  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5562  address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5563  context);
5564  else
5565  address = ATExecAttachPartitionIdx(wqueue, rel,
5566  ((PartitionCmd *) cmd->def)->name);
5567  break;
5568  case AT_DetachPartition:
5569  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5570  cur_pass, context);
5571  Assert(cmd != NULL);
5572  /* ATPrepCmd ensures it must be a table */
5573  Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5574  address = ATExecDetachPartition(wqueue, tab, rel,
5575  ((PartitionCmd *) cmd->def)->name,
5576  ((PartitionCmd *) cmd->def)->concurrent);
5577  break;
5579  address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5580  break;
5581  case AT_SplitPartition:
5582  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5583  cur_pass, context);
5584  Assert(cmd != NULL);
5585  Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5586  ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5587  context);
5588  break;
5589  case AT_MergePartitions:
5590  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5591  cur_pass, context);
5592  Assert(cmd != NULL);
5593  Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5594  ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5595  context);
5596  break;
5597  default: /* oops */
5598  elog(ERROR, "unrecognized alter table type: %d",
5599  (int) cmd->subtype);
5600  break;
5601  }
5602 
5603  /*
5604  * Report the subcommand to interested event triggers.
5605  */
5606  if (cmd)
5607  EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5608 
5609  /*
5610  * Bump the command counter to ensure the next subcommand in the sequence
5611  * can see the changes so far
5612  */
5614 }
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition: acl.c:5448
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:2441
DropBehavior behavior
Definition: parsenodes.h:2444
bool chgAccessMethod
Definition: tablecmds.c:190
Relation rel
Definition: tablecmds.c:180
static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName, List *options, LOCKMODE lockmode)
Definition: tablecmds.c:14865
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:16166
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:17297
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:8697
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition: tablecmds.c:8086
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8424
static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:12000
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:7570
static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel, char *constrname, char *colName, bool recurse, bool recursing, List **readyRels, LOCKMODE lockmode)
Definition: tablecmds.c:7816
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9612
static ObjectAddress ATExecSetAttNotNull(List **wqueue, Relation rel, const char *colName, LOCKMODE lockmode)
Definition: tablecmds.c:8020
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
Definition: tablecmds.c:19839
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:17567
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:16109
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethod)
Definition: tablecmds.c:15435
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
Definition: tablecmds.c:8923
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:9156
static void ATExecSetRowSecurity(Relation rel, bool rls)
Definition: tablecmds.c:17537
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition: tablecmds.c:8172
static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:21473
static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
Definition: tablecmds.c:15555
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8307
static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:11712
static void ATExecGenericOptions(Relation rel, List *options)
Definition: tablecmds.c:17596
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8788
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode)
Definition: tablecmds.c:13709
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:15389
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
Definition: tablecmds.c:8538
static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
Definition: tablecmds.c:17155
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:16724
static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:9520
static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:17429
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:20249
static ObjectAddress ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:9065
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:19177
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:12873
static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9436
static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:17677
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:15851
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:20452
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:21242
static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9499
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
Definition: tablecmds.c:15357
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:16127
#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:2897

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_SetAttNotNull, 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(), ATExecSetAttNotNull(), ATExecSetCompression(), ATExecSetExpression(), ATExecSetIdentity(), ATExecSetNotNull(), ATExecSetOptions(), ATExecSetRelOptions(), ATExecSetRowSecurity(), ATExecSetStatistics(), ATExecSetStorage(), ATExecSetTableSpaceNoStorage(), ATExecSplitPartition(), ATExecValidateConstraint(), ATParseTransformCmd(), AlterTableCmd::behavior, AlteredTableInfo::chgAccessMethod, CommandCounterIncrement(), CommentObject(), context, 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 8086 of file tablecmds.c.

8088 {
8089  TupleDesc tupdesc = RelationGetDescr(rel);
8091  ObjectAddress address;
8092 
8093  /*
8094  * get the number of the attribute
8095  */
8096  attnum = get_attnum(RelationGetRelid(rel), colName);
8097  if (attnum == InvalidAttrNumber)
8098  ereport(ERROR,
8099  (errcode(ERRCODE_UNDEFINED_COLUMN),
8100  errmsg("column \"%s\" of relation \"%s\" does not exist",
8101  colName, RelationGetRelationName(rel))));
8102 
8103  /* Prevent them from altering a system attribute */
8104  if (attnum <= 0)
8105  ereport(ERROR,
8106  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8107  errmsg("cannot alter system column \"%s\"",
8108  colName)));
8109 
8110  if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8111  ereport(ERROR,
8112  (errcode(ERRCODE_SYNTAX_ERROR),
8113  errmsg("column \"%s\" of relation \"%s\" is an identity column",
8114  colName, RelationGetRelationName(rel)),
8115  /* translator: %s is an SQL ALTER command */
8116  newDefault ? 0 : errhint("Use %s instead.",
8117  "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8118 
8119  if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8120  ereport(ERROR,
8121  (errcode(ERRCODE_SYNTAX_ERROR),
8122  errmsg("column \"%s\" of relation \"%s\" is a generated column",
8123  colName, RelationGetRelationName(rel)),
8124  newDefault ?
8125  /* translator: %s is an SQL ALTER command */
8126  errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8127  (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8128  errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8129 
8130  /*
8131  * Remove any old default for the column. We use RESTRICT here for
8132  * safety, but at present we do not expect anything to depend on the
8133  * default.
8134  *
8135  * We treat removing the existing default as an internal operation when it
8136  * is preparatory to adding a new default, but as a user-initiated
8137  * operation when the user asked for a drop.
8138  */
8140  newDefault != NULL);
8141 
8142  if (newDefault)
8143  {
8144  /* SET DEFAULT */
8145  RawColumnDefault *rawEnt;
8146 
8147  rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8148  rawEnt->attnum = attnum;
8149  rawEnt->raw_default = newDefault;
8150  rawEnt->missingMode = false;
8151  rawEnt->generated = '\0';
8152 
8153  /*
8154  * This function is intended for CREATE TABLE, so it processes a
8155  * _list_ of defaults, but we just do one.
8156  */
8158  false, true, false, NULL);
8159  }
8160 
8161  ObjectAddressSubSet(address, RelationRelationId,
8162  RelationGetRelid(rel), attnum);
8163  return address;
8164 }
#define InvalidAttrNumber
Definition: attnum.h:23
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:858

References AddRelationNewConstraints(), RawColumnDefault::attnum, attnum, DROP_RESTRICT, ereport, errcode(), errhint(), errmsg(), ERROR, RawColumnDefault::generated, get_attnum(), InvalidAttrNumber, list_make1, RawColumnDefault::missingMode, NIL, ObjectAddressSubSet, palloc(), RawColumnDefault::raw_default, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RemoveAttrDefault(), and TupleDescAttr.

Referenced by ATExecCmd().

◆ ATExecCookedColumnDefault()

static ObjectAddress ATExecCookedColumnDefault ( Relation  rel,
AttrNumber  attnum,
Node newDefault 
)
static

Definition at line 8172 of file tablecmds.c.

8174 {
8175  ObjectAddress address;
8176 
8177  /* We assume no checking is required */
8178 
8179  /*
8180  * Remove any old default for the column. We use RESTRICT here for
8181  * safety, but at present we do not expect anything to depend on the
8182  * default. (In ordinary cases, there could not be a default in place
8183  * anyway, but it's possible when combining LIKE with inheritance.)
8184  */
8186  true);
8187 
8188  (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
8189 
8190  ObjectAddressSubSet(address, RelationRelationId,
8191  RelationGetRelid(rel), attnum);
8192  return address;
8193 }

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

19841 {
19842  Relation partRel;
19843  ObjectAddress address;
19844  Oid defaultPartOid;
19845 
19846  /*
19847  * We must lock the default partition, because detaching this partition
19848  * will change its partition constraint.
19849  */
19850  defaultPartOid =
19852  if (OidIsValid(defaultPartOid))
19853  {
19854  /*
19855  * Concurrent detaching when a default partition exists is not
19856  * supported. The main problem is that the default partition
19857  * constraint would change. And there's a definitional problem: what
19858  * should happen to the tuples that are being inserted that belong to
19859  * the partition being detached? Putting them on the partition being
19860  * detached would be wrong, since they'd become "lost" after the
19861  * detaching completes but we cannot put them in the default partition
19862  * either until we alter its partition constraint.
19863  *
19864  * I think we could solve this problem if we effected the constraint
19865  * change before committing the first transaction. But the lock would
19866  * have to remain AEL and it would cause concurrent query planning to
19867  * be blocked, so changing it that way would be even worse.
19868  */
19869  if (concurrent)
19870  ereport(ERROR,
19871  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19872  errmsg("cannot detach partitions concurrently when a default partition exists")));
19873  LockRelationOid(defaultPartOid, AccessExclusiveLock);
19874  }
19875 
19876  /*
19877  * In concurrent mode, the partition is locked with share-update-exclusive
19878  * in the first transaction. This allows concurrent transactions to be
19879  * doing DML to the partition.
19880  */
19881  partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19883 
19884  /*
19885  * Check inheritance conditions and either delete the pg_inherits row (in
19886  * non-concurrent mode) or just set the inhdetachpending flag.
19887  */
19888  if (!concurrent)
19889  RemoveInheritance(partRel, rel, false);
19890  else
19891  MarkInheritDetached(partRel, rel);
19892 
19893  /*
19894  * Ensure that foreign keys still hold after this detach. This keeps
19895  * locks on the referencing tables, which prevents concurrent transactions
19896  * from adding rows that we wouldn't see. For this to work in concurrent
19897  * mode, it is critical that the partition appears as no longer attached
19898  * for the RI queries as soon as the first transaction commits.
19899  */
19901 
19902  /*
19903  * Concurrent mode has to work harder; first we add a new constraint to
19904  * the partition that matches the partition constraint. Then we close our
19905  * existing transaction, and in a new one wait for all processes to catch
19906  * up on the catalog updates we've done so far; at that point we can
19907  * complete the operation.
19908  */
19909  if (concurrent)
19910  {
19911  Oid partrelid,
19912  parentrelid;
19913  LOCKTAG tag;
19914  char *parentrelname;
19915  char *partrelname;
19916 
19917  /*
19918  * Add a new constraint to the partition being detached, which
19919  * supplants the partition constraint (unless there is one already).
19920  */
19921  DetachAddConstraintIfNeeded(wqueue, partRel);
19922 
19923  /*
19924  * We're almost done now; the only traces that remain are the
19925  * pg_inherits tuple and the partition's relpartbounds. Before we can
19926  * remove those, we need to wait until all transactions that know that
19927  * this is a partition are gone.
19928  */
19929 
19930  /*
19931  * Remember relation OIDs to re-acquire them later; and relation names
19932  * too, for error messages if something is dropped in between.
19933  */
19934  partrelid = RelationGetRelid(partRel);
19935  parentrelid = RelationGetRelid(rel);
19936  parentrelname = MemoryContextStrdup(PortalContext,
19938  partrelname = MemoryContextStrdup(PortalContext,
19939  RelationGetRelationName(partRel));
19940 
19941  /* Invalidate relcache entries for the parent -- must be before close */
19943 
19944  table_close(partRel, NoLock);
19945  table_close(rel, NoLock);
19946  tab->rel = NULL;
19947 
19948  /* Make updated catalog entry visible */
19951 
19953 
19954  /*
19955  * Now wait. This ensures that all queries that were planned
19956  * including the partition are finished before we remove the rest of
19957  * catalog entries. We don't need or indeed want to acquire this
19958  * lock, though -- that would block later queries.
19959  *
19960  * We don't need to concern ourselves with waiting for a lock on the
19961  * partition itself, since we will acquire AccessExclusiveLock below.
19962  */
19963  SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
19965 
19966  /*
19967  * Now acquire locks in both relations again. Note they may have been
19968  * removed in the meantime, so care is required.
19969  */
19970  rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
19971  partRel = try_relation_open(partrelid, AccessExclusiveLock);
19972 
19973  /* If the relations aren't there, something bad happened; bail out */
19974  if (rel == NULL)
19975  {
19976  if (partRel != NULL) /* shouldn't happen */
19977  elog(WARNING, "dangling partition \"%s\" remains, can't fix",
19978  partrelname);
19979  ereport(ERROR,
19980  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19981  errmsg("partitioned table \"%s\" was removed concurrently",
19982  parentrelname)));
19983  }
19984  if (partRel == NULL)
19985  ereport(ERROR,
19986  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19987  errmsg("partition \"%s\" was removed concurrently", partrelname)));
19988 
19989  tab->rel = rel;
19990  }
19991 
19992  /* Do the final part of detaching */
19993  DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
19994 
19995  ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19996 
19997  /* keep our lock until commit */
19998  table_close(partRel, NoLock);
19999 
20000  return address;
20001 }
Oid MyDatabaseId
Definition: globals.c:91
void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
Definition: lmgr.c:907
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition: lock.h:181
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:1682
MemoryContext PortalContext
Definition: mcxt.c:158
void PopActiveSnapshot(void)
Definition: snapmgr.c:743
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:88
Definition: lock.h:165
static void MarkInheritDetached(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:16778
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:16861
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:20010
static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
Definition: tablecmds.c:20284
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:20814
void StartTransactionCommand(void)
Definition: xact.c:2995
void CommitTransactionCommand(void)
Definition: xact.c:3093

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

Referenced by ATExecCmd().

◆ ATExecDetachPartitionFinalize()

static ObjectAddress ATExecDetachPartitionFinalize ( Relation  rel,
RangeVar name 
)
static

Definition at line 20249 of file tablecmds.c.

20250 {
20251  Relation partRel;
20252  ObjectAddress address;
20253  Snapshot snap = GetActiveSnapshot();
20254 
20256 
20257  /*
20258  * Wait until existing snapshots are gone. This is important if the
20259  * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20260  * user could immediately run DETACH FINALIZE without actually waiting for
20261  * existing transactions. We must not complete the detach action until
20262  * all such queries are complete (otherwise we would present them with an
20263  * inconsistent view of catalogs).
20264  */
20265  WaitForOlderSnapshots(snap->xmin, false);
20266 
20267  DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20268 
20269  ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20270 
20271  table_close(partRel, NoLock);
20272 
20273  return address;
20274 }
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:428
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:770
TransactionId xmin
Definition: snapshot.h:157

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

15390 {
15391  mark_index_clustered(rel, InvalidOid, false);
15392 }

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

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

References add_exact_object_address(), Assert, AT_DropColumn, ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_TABLE, attnum, bms_make_singleton(), CatalogTupleUpdate(), check_stack_depth(), CheckTableNotInUse(), 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().

◆ ATExecDropConstraint()

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

Definition at line 12873 of file tablecmds.c.

12876 {
12877  Relation conrel;
12878  SysScanDesc scan;
12879  ScanKeyData skey[3];
12880  HeapTuple tuple;
12881  bool found = false;
12882 
12883  conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12884 
12885  /*
12886  * Find and drop the target constraint
12887  */
12888  ScanKeyInit(&skey[0],
12889  Anum_pg_constraint_conrelid,
12890  BTEqualStrategyNumber, F_OIDEQ,
12892  ScanKeyInit(&skey[1],
12893  Anum_pg_constraint_contypid,
12894  BTEqualStrategyNumber, F_OIDEQ,
12896  ScanKeyInit(&skey[2],
12897  Anum_pg_constraint_conname,
12898  BTEqualStrategyNumber, F_NAMEEQ,
12899  CStringGetDatum(constrName));
12900  scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12901  true, NULL, 3, skey);
12902 
12903  /* There can be at most one matching row */
12904  if (HeapTupleIsValid(tuple = systable_getnext(scan)))
12905  {
12906  List *readyRels = NIL;
12907 
12908  dropconstraint_internal(rel, tuple, behavior, recurse, false,
12909  missing_ok, &readyRels, lockmode);
12910  found = true;
12911  }
12912 
12913  systable_endscan(scan);
12914 
12915  if (!found)
12916  {
12917  if (!missing_ok)
12918  ereport(ERROR,
12919  errcode(ERRCODE_UNDEFINED_OBJECT),
12920  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12921  constrName, RelationGetRelationName(rel)));
12922  else
12923  ereport(NOTICE,
12924  errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12925  constrName, RelationGetRelationName(rel)));
12926  }
12927 
12928  table_close(conrel, RowExclusiveLock);
12929 }
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, List **readyRels, LOCKMODE lockmode)
Definition: tablecmds.c:12940

References BTEqualStrategyNumber, CStringGetDatum(), dropconstraint_internal(), ereport, errcode(), errmsg(), ERROR, HeapTupleIsValid, InvalidOid, NIL, 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 8697 of file tablecmds.c.

8698 {
8699  HeapTuple tuple;
8700  Form_pg_attribute attTup;
8702  Relation attrelation;
8703  Oid attrdefoid;
8704  ObjectAddress address;
8705 
8706  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8707  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8708  if (!HeapTupleIsValid(tuple))
8709  ereport(ERROR,
8710  (errcode(ERRCODE_UNDEFINED_COLUMN),
8711  errmsg("column \"%s\" of relation \"%s\" does not exist",
8712  colName, RelationGetRelationName(rel))));
8713 
8714  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8715  attnum = attTup->attnum;
8716 
8717  if (attnum <= 0)
8718  ereport(ERROR,
8719  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8720  errmsg("cannot alter system column \"%s\"",
8721  colName)));
8722 
8723  if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8724  {
8725  if (!missing_ok)
8726  ereport(ERROR,
8727  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8728  errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8729  colName, RelationGetRelationName(rel))));
8730  else
8731  {
8732  ereport(NOTICE,
8733  (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8734  colName, RelationGetRelationName(rel))));
8735  heap_freetuple(tuple);
8736  table_close(attrelation, RowExclusiveLock);
8737  return InvalidObjectAddress;
8738  }
8739  }
8740 
8741  /*
8742  * Mark the column as no longer generated. (The atthasdef flag needs to
8743  * get cleared too, but RemoveAttrDefault will handle that.)
8744  */
8745  attTup->attgenerated = '\0';
8746  CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8747 
8748  InvokeObjectPostAlterHook(RelationRelationId,
8749  RelationGetRelid(rel),
8750  attnum);
8751  heap_freetuple(tuple);
8752 
8753  table_close(attrelation, RowExclusiveLock);
8754 
8755  /*
8756  * Drop the dependency records of the GENERATED expression, in particular
8757  * its INTERNAL dependency on the column, which would otherwise cause
8758  * dependency.c to refuse to perform the deletion.
8759  */
8760  attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8761  if (!OidIsValid(attrdefoid))
8762  elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8763  RelationGetRelid(rel), attnum);
8764  (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8765 
8766  /* Make above changes visible */
8768 
8769  /*
8770  * Get rid of the GENERATED expression itself. We use RESTRICT here for
8771  * safety, but at present we do not expect anything to depend on the
8772  * default.
8773  */
8775  false, false);
8776 
8777  ObjectAddressSubSet(address, RelationRelationId,
8778  RelationGetRelid(rel), attnum);
8779  return address;
8780 }

References attnum, CatalogTupleUpdate(), CommandCounterIncrement(), deleteDependencyRecordsFor(), DROP_RESTRICT, elog, ereport, errcode(), 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 8424 of file tablecmds.c.

8426 {
8427  HeapTuple tuple;
8428  Form_pg_attribute attTup;
8430  Relation attrelation;
8431  ObjectAddress address;
8432  Oid seqid;
8433  ObjectAddress seqaddress;
8434  bool ispartitioned;
8435 
8436  ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8437  if (ispartitioned && !recurse)
8438  ereport(ERROR,
8439  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8440  errmsg("cannot drop identity from a column of only the partitioned table"),
8441  errhint("Do not specify the ONLY keyword.")));
8442 
8443  if (rel->rd_rel->relispartition && !recursing)
8444  ereport(ERROR,
8445  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8446  errmsg("cannot drop identity from a column of a partition"));
8447 
8448  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8449  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8450  if (!HeapTupleIsValid(tuple))
8451  ereport(ERROR,
8452  (errcode(ERRCODE_UNDEFINED_COLUMN),
8453  errmsg("column \"%s\" of relation \"%s\" does not exist",
8454  colName, RelationGetRelationName(rel))));
8455 
8456  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8457  attnum = attTup->attnum;
8458 
8459  if (attnum <= 0)
8460  ereport(ERROR,
8461  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8462  errmsg("cannot alter system column \"%s\"",
8463  colName)));
8464 
8465  if (!attTup->attidentity)
8466  {
8467  if (!missing_ok)
8468  ereport(ERROR,
8469  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8470  errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8471  colName, RelationGetRelationName(rel))));
8472  else
8473  {
8474  ereport(NOTICE,
8475  (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8476  colName, RelationGetRelationName(rel))));
8477  heap_freetuple(tuple);
8478  table_close(attrelation, RowExclusiveLock);
8479  return InvalidObjectAddress;
8480  }
8481  }
8482 
8483  attTup->attidentity = '\0';
8484  CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8485 
8486  InvokeObjectPostAlterHook(RelationRelationId,
8487  RelationGetRelid(rel),
8488  attTup->attnum);
8489  ObjectAddressSubSet(address, RelationRelationId,
8490  RelationGetRelid(rel), attnum);
8491  heap_freetuple(tuple);
8492 
8493  table_close(attrelation, RowExclusiveLock);
8494 
8495  /*
8496  * Recurse to drop the identity from column in partitions. Identity is
8497  * not inherited in regular inheritance children so ignore them.
8498  */
8499  if (recurse && ispartitioned)
8500  {
8501  List *children;
8502  ListCell *lc;
8503 
8504  children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8505 
8506  foreach(lc, children)
8507  {
8508  Relation childrel;
8509 
8510  childrel = table_open(lfirst_oid(lc), NoLock);
8511  ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8512  table_close(childrel, NoLock);
8513  }
8514  }
8515 
8516  if (!recursing)
8517  {
8518  /* drop the internal sequence */
8519  seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
8520  deleteDependencyRecordsForClass(RelationRelationId, seqid,
8521  RelationRelationId, DEPENDENCY_INTERNAL);
8523  seqaddress.classId = RelationRelationId;
8524  seqaddress.objectId = seqid;
8525  seqaddress.objectSubId = 0;
8527  }
8528 
8529  return address;
8530 }
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:273
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:85
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:350
Oid getIdentitySequence(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:944

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

◆ ATExecDropInherit()

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

Definition at line 16724 of file tablecmds.c.

16725 {
16726  ObjectAddress address;
16727  Relation parent_rel;
16728 
16729  if (rel->rd_rel->relispartition)
16730  ereport(ERROR,
16731  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16732  errmsg("cannot change inheritance of a partition")));
16733 
16734  /*
16735  * AccessShareLock on the parent is probably enough, seeing that DROP
16736  * TABLE doesn't lock parent tables at all. We need some lock since we'll
16737  * be inspecting the parent's schema.
16738  */
16739  parent_rel = table_openrv(parent, AccessShareLock);
16740 
16741  /*
16742  * We don't bother to check ownership of the parent table --- ownership of
16743  * the child is presumed enough rights.
16744  */
16745 
16746  /* Off to RemoveInheritance() where most of the work happens */
16747  RemoveInheritance(rel, parent_rel, false);
16748 
16749  /*
16750  * If parent_rel has a primary key, then child_rel has not-null
16751  * constraints that make these columns as non nullable. Mark those
16752  * constraints as no longer inherited by this parent.
16753  */
16754  ATInheritAdjustNotNulls(parent_rel, rel, -1);
16755 
16756  /*
16757  * If the parent has a primary key, then we decrement counts for all NOT
16758  * NULL constraints
16759  */
16760 
16761  ObjectAddressSet(address, RelationRelationId,
16762  RelationGetRelid(parent_rel));
16763 
16764  /* keep our lock on the parent relation until commit */
16765  table_close(parent_rel, NoLock);
16766 
16767  return address;
16768 }

References AccessShareLock, ATInheritAdjustNotNulls(), 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 7570 of file tablecmds.c.

7572 {
7573  HeapTuple tuple;
7574  HeapTuple conTup;
7575  Form_pg_attribute attTup;
7577  Relation attr_rel;
7578  ObjectAddress address;
7579  List *readyRels;
7580 
7581  /*
7582  * lookup the attribute
7583  */
7584  attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7585 
7586  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7587  if (!HeapTupleIsValid(tuple))
7588  ereport(ERROR,
7589  (errcode(ERRCODE_UNDEFINED_COLUMN),
7590  errmsg("column \"%s\" of relation \"%s\" does not exist",
7591  colName, RelationGetRelationName(rel))));
7592  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7593  attnum = attTup->attnum;
7594  ObjectAddressSubSet(address, RelationRelationId,
7595  RelationGetRelid(rel), attnum);
7596 
7597  /* If the column is already nullable there's nothing to do. */
7598  if (!attTup->attnotnull)
7599  {
7600  table_close(attr_rel, RowExclusiveLock);
7601  return InvalidObjectAddress;
7602  }
7603 
7604  /* Prevent them from altering a system attribute */
7605  if (attnum <= 0)
7606  ereport(ERROR,
7607  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7608  errmsg("cannot alter system column \"%s\"",
7609  colName)));
7610 
7611  if (attTup->attidentity)
7612  ereport(ERROR,
7613  (errcode(ERRCODE_SYNTAX_ERROR),
7614  errmsg("column \"%s\" of relation \"%s\" is an identity column",
7615  colName, RelationGetRelationName(rel))));
7616 
7617  /*
7618  * It's not OK to remove a constraint only for the parent and leave it in
7619  * the children, so disallow that.
7620  */
7621  if (!recurse)
7622  {
7623  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7624  {
7625  PartitionDesc partdesc;
7626 
7627  partdesc = RelationGetPartitionDesc(rel, true);
7628 
7629  if (partdesc->nparts > 0)
7630  ereport(ERROR,
7631  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7632  errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7633  errhint("Do not specify the ONLY keyword."));
7634  }
7635  else if (rel->rd_rel->relhassubclass &&
7637  {
7638  ereport(ERROR,
7639  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7640  errmsg("not-null constraint on column \"%s\" must be removed in child tables too",
7641  colName),
7642  errhint("Do not specify the ONLY keyword."));
7643  }
7644  }
7645 
7646  /*
7647  * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7648  */
7649  if (rel->rd_rel->relispartition)
7650  {
7651  Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7652  Relation parent = table_open(parentId, AccessShareLock);
7653  TupleDesc tupDesc = RelationGetDescr(parent);
7654  AttrNumber parent_attnum;
7655 
7656  parent_attnum = get_attnum(parentId, colName);
7657  if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7658  ereport(ERROR,
7659  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7660  errmsg("column \"%s\" is marked NOT NULL in parent table",
7661  colName)));
7662  table_close(parent, AccessShareLock);
7663  }
7664 
7665  /*
7666  * Find the constraint that makes this column NOT NULL, and drop it if we
7667  * see one. dropconstraint_internal() will do necessary consistency
7668  * checking. If there isn't one, there are two possibilities: either the
7669  * column is marked attnotnull because it's part of the primary key, and
7670  * then we just throw an appropriate error; or it's a leftover marking
7671  * that we can remove. However, before doing the latter, to avoid
7672  * breaking consistency any further, prevent this if the column is part of
7673  * the replica identity.
7674  */
7675  conTup = findNotNullConstraint(RelationGetRelid(rel), colName);
7676  if (conTup == NULL)
7677  {
7678  Bitmapset *pkcols;
7679  Bitmapset *ircols;
7680 
7681  /*
7682  * If the column is in a primary key, throw a specific error message.
7683  */
7686  pkcols))
7687  ereport(ERROR,
7688  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7689  errmsg("column \"%s\" is in a primary key", colName));
7690 
7691  /* Also throw an error if the column is in the replica identity */
7694  ereport(ERROR,
7695  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7696  errmsg("column \"%s\" is in index used as replica identity",
7697  get_attname(RelationGetRelid(rel), attnum, false)));
7698 
7699  /* Otherwise, just remove the attnotnull marking and do nothing else. */
7700  attTup->attnotnull = false;
7701  CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7702  }
7703  else
7704  {
7705  /* The normal case: we have a pg_constraint row, remove it */
7706  readyRels = NIL;
7707  dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7708  false, &readyRels, lockmode);
7709 
7710  heap_freetuple(conTup);
7711  }
7712 
7713  InvokeObjectPostAlterHook(RelationRelationId,
7714  RelationGetRelid(rel), attnum);
7715 
7716  table_close(attr_rel, RowExclusiveLock);
7717 
7718  return address;
7719 }
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:827
bool attnotnull
Definition: pg_attribute.h:130
HeapTuple findNotNullConstraint(Oid relid, const char *colname)
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition: relcache.c:5231
@ INDEX_ATTR_BITMAP_PRIMARY_KEY
Definition: relcache.h:62
@ INDEX_ATTR_BITMAP_IDENTITY_KEY
Definition: relcache.h:63

References AccessShareLock, attnotnull, attnum, bms_is_member(), CatalogTupleUpdate(), DROP_RESTRICT, dropconstraint_internal(), ereport, errcode(), errhint(), errmsg(), ERROR, find_inheritance_children(), findNotNullConstraint(), FirstLowInvalidHeapAttributeNumber, get_attname(), get_attnum(), get_partition_parent(), GETSTRUCT, heap_freetuple(), HeapTupleIsValid, INDEX_ATTR_BITMAP_IDENTITY_KEY, INDEX_ATTR_BITMAP_PRIMARY_KEY, InvalidObjectAddress, InvokeObjectPostAlterHook, NIL, NoLock, PartitionDescData::nparts, ObjectAddressSubSet, RelationData::rd_rel, RelationGetDescr, RelationGetIndexAttrBitmap(), RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), table_open(), and TupleDescAttr.

Referenced by ATExecCmd().

◆ ATExecDropOf()

static void ATExecDropOf ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 17297 of file tablecmds.c.

17298 {
17299  Oid relid = RelationGetRelid(rel);
17300  Relation relationRelation;
17301  HeapTuple tuple;
17302 
17303  if (!OidIsValid(rel->rd_rel->reloftype))
17304  ereport(ERROR,
17305  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17306  errmsg("\"%s\" is not a typed table",
17307  RelationGetRelationName(rel))));
17308 
17309  /*
17310  * We don't bother to check ownership of the type --- ownership of the
17311  * table is presumed enough rights. No lock required on the type, either.
17312  */
17313 
17314  drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17316 
17317  /* Clear pg_class.reloftype */
17318  relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17319  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17320  if (!HeapTupleIsValid(tuple))
17321  elog(ERROR, "cache lookup failed for relation %u", relid);
17322  ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17323  CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17324 
17325  InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17326 
17327  heap_freetuple(tuple);
17328  table_close(relationRelation, RowExclusiveLock);
17329 }

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

16129 {
16130  EnableDisableRule(rel, rulename, fires_when);
16131 
16132  InvokeObjectPostAlterHook(RelationRelationId,
16133  RelationGetRelid(rel), 0);
16134 }
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 16109 of file tablecmds.c.

16112 {
16113  EnableDisableTrigger(rel, trigname, InvalidOid,
16114  fires_when, skip_system, recurse,
16115  lockmode);
16116 
16117  InvokeObjectPostAlterHook(RelationRelationId,
16118  RelationGetRelid(rel), 0);
16119 }
void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: trigger.c:1721

References EnableDisableTrigger(), InvalidOid, InvokeObjectPostAlterHook, and RelationGetRelid.

Referenced by ATExecCmd().

◆ ATExecForceNoForceRowSecurity()

static void ATExecForceNoForceRowSecurity ( Relation  rel,
bool  force_rls 
)
static

Definition at line 17567 of file tablecmds.c.

17568 {
17569  Relation pg_class;
17570  Oid relid;
17571  HeapTuple tuple;
17572 
17573  relid = RelationGetRelid(rel);
17574 
17575  pg_class = table_open(RelationRelationId, RowExclusiveLock);
17576 
17577  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17578 
17579  if (!HeapTupleIsValid(tuple))
17580  elog(ERROR, "cache lookup failed for relation %u", relid);
17581 
17582  ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
17583  CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17584 
17585  InvokeObjectPostAlterHook(RelationRelationId,
17586  RelationGetRelid(rel), 0);
17587 
17588  table_close(pg_class, RowExclusiveLock);
17589  heap_freetuple(tuple);
17590 }

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

17597 {
17598  Relation ftrel;
17599  ForeignServer *server;
17600  ForeignDataWrapper *fdw;
17601  HeapTuple tuple;
17602  bool isnull;
17603  Datum repl_val[Natts_pg_foreign_table];
17604  bool repl_null[Natts_pg_foreign_table];
17605  bool repl_repl[Natts_pg_foreign_table];
17606  Datum datum;
17607  Form_pg_foreign_table tableform;
17608 
17609  if (options == NIL)
17610  return;
17611 
17612  ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
17613 
17614  tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
17615  ObjectIdGetDatum(rel->rd_id));
17616  if (!HeapTupleIsValid(tuple))
17617  ereport(ERROR,
17618  (errcode(ERRCODE_UNDEFINED_OBJECT),
17619  errmsg("foreign table \"%s\" does not exist",
17620  RelationGetRelationName(rel))));
17621  tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
17622  server = GetForeignServer(tableform->ftserver);
17623  fdw = GetForeignDataWrapper(server->fdwid);
17624 
17625  memset(repl_val, 0, sizeof(repl_val));
17626  memset(repl_null, false, sizeof(repl_null));
17627  memset(repl_repl, false, sizeof(repl_repl));
17628 
17629  /* Extract the current options */
17630  datum = SysCacheGetAttr(FOREIGNTABLEREL,
17631  tuple,
17632  Anum_pg_foreign_table_ftoptions,
17633  &isnull);
17634  if (isnull)
17635  datum = PointerGetDatum(NULL);
17636 
17637  /* Transform the options */
17638  datum = transformGenericOptions(ForeignTableRelationId,
17639  datum,
17640  options,
17641  fdw->fdwvalidator);
17642 
17643  if (PointerIsValid(DatumGetPointer(datum)))
17644  repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
17645  else
17646  repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
17647 
17648  repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
17649 
17650  /* Everything looks good - update the tuple */
17651 
17652  tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
17653  repl_val, repl_null, repl_repl);
17654 
17655  CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17656 
17657  /*
17658  * Invalidate relcache so that all sessions will refresh any cached plans
17659  * that might depend on the old options.
17660  */
17662 
17663  InvokeObjectPostAlterHook(ForeignTableRelationId,
17664  RelationGetRelid(rel), 0);
17665 
17666  table_close(ftrel, RowExclusiveLock);
17667 
17668  heap_freetuple(tuple);
17669 }

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

Referenced by ATExecCmd().

◆ ATExecMergePartitions()

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

Definition at line 21473 of file tablecmds.c.

21475 {
21476  Relation newPartRel;
21477  ListCell *listptr;
21478  List *mergingPartitionsList = NIL;
21479  Oid defaultPartOid;
21480  char tmpRelName[NAMEDATALEN];
21481  RangeVar *mergePartName = cmd->name;
21482  bool isSameName = false;
21483 
21484  /*
21485  * Lock all merged partitions, check them and create list with partitions
21486  * contexts.
21487  */
21488  foreach(listptr, cmd->partlist)
21489  {
21490  RangeVar *name = (RangeVar *) lfirst(listptr);
21491  Relation mergingPartition;
21492 
21493  /*
21494  * We are going to detach and remove this partition: need to use
21495  * exclusive lock for preventing DML-queries to the partition.
21496  */
21497  mergingPartition = table_openrv(name, AccessExclusiveLock);
21498 
21499  /*
21500  * Checking that two partitions have the same name was before, in
21501  * function transformPartitionCmdForMerge().
21502  */
21503  if (equal(name, cmd->name))
21504  /* One new partition can have the same name as merged partition. */
21505  isSameName = true;
21506 
21507  /* Store a next merging partition into the list. */
21508  mergingPartitionsList = lappend(mergingPartitionsList,
21509  mergingPartition);
21510  }
21511 
21512  /* Detach all merged partitions. */
21513  defaultPartOid =
21515  foreach(listptr, mergingPartitionsList)
21516  {
21517  Relation mergingPartition = (Relation) lfirst(listptr);
21518 
21519  /* Remove the pg_inherits row first. */
21520  RemoveInheritance(mergingPartition, rel, false);
21521  /* Do the final part of detaching. */
21522  DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid);
21523  }
21524 
21525  /* Create table for new partition, use partitioned table as model. */
21526  if (isSameName)
21527  {
21528  /* Create partition table with generated temporary name. */
21529  sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
21531  tmpRelName, -1);
21532  }
21533  createPartitionTable(mergePartName,
21535  RelationGetRelationName(rel), -1),
21536  context);
21537 
21538  /*
21539  * Open the new partition and acquire exclusive lock on it. This will
21540  * stop all the operations with partitioned table. This might seem
21541  * excessive, but this is the way we make sure nobody is planning queries
21542  * involving merging partitions.
21543  */
21544  newPartRel = table_openrv(mergePartName, AccessExclusiveLock);
21545 
21546  /* Copy data from merged partitions to new partition. */
21547  moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);
21548 
21549  /*
21550  * Attach a new partition to the partitioned table. wqueue = NULL:
21551  * verification for each cloned constraint is not need.
21552  */
21553  attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
21554 
21555  /* Unlock and drop merged partitions. */
21556  foreach(listptr, mergingPartitionsList)
21557  {
21558  ObjectAddress object;
21559  Relation mergingPartition = (Relation) lfirst(listptr);
21560 
21561  /* Get relation id before table_close() call. */
21562  object.objectId = RelationGetRelid(mergingPartition);
21563  object.classId = RelationRelationId;
21564  object.objectSubId = 0;
21565 
21566  /* Keep the lock until commit. */
21567  table_close(mergingPartition, NoLock);
21568 
21569  performDeletion(&object, DROP_RESTRICT, 0);
21570  }
21571  list_free(mergingPartitionsList);
21572 
21573  /* Rename new partition if it is needed. */
21574  if (isSameName)
21575  {
21576  /*
21577  * We must bump the command counter to make the new partition tuple
21578  * visible for rename.
21579  */
21581  /* Rename partition. */
21583  cmd->name->relname, false, false);
21584  }
21585  /* Keep the lock until commit. */
21586  table_close(newPartRel, NoLock);
21587 }
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
int MyProcPid
Definition: globals.c:45
#define sprintf
Definition: port.h:240
struct RelationData * Relation
Definition: relcache.h:27
List * partlist
Definition: parsenodes.h:959
static void createPartitionTable(RangeVar *newPartName, RangeVar *modelRelName, AlterTableUtilityContext *context)
Definition: tablecmds.c:21192
static void moveMergedTablesRows(Relation rel, List *mergingPartitionsList, Relation newPartRel)
Definition: tablecmds.c:21376

References AccessExclusiveLock, attachPartitionTable(), PartitionCmd::bound, CommandCounterIncrement(), context, createPartitionTable(), DetachPartitionFinalize(), DROP_RESTRICT, equal(), get_default_oid_from_partdesc(), get_namespace_name(), lappend(), lfirst, list_free(), makeRangeVar(), moveMergedTablesRows(), MyProcPid, name, PartitionCmd::name, NAMEDATALEN, NIL, NoLock, PartitionCmd::partlist, performDeletion(), RelationGetNamespace, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, RangeVar::relname, RemoveInheritance(), RenameRelationInternal(), sprintf, table_close(), and table_openrv().

Referenced by ATExecCmd().

◆ ATExecReplicaIdentity()

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

Definition at line 17429 of file tablecmds.c.

17430 {
17431  Oid indexOid;
17432  Relation indexRel;
17433  int key;
17434 
17435  if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17436  {
17437  relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17438  return;
17439  }
17440  else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17441  {
17442  relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17443  return;
17444  }
17445  else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17446  {
17447  relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17448  return;
17449  }
17450  else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17451  {
17452  /* fallthrough */ ;
17453  }
17454  else
17455  elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17456 
17457  /* Check that the index exists */
17458  indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17459  if (!OidIsValid(indexOid))
17460  ereport(ERROR,
17461  (errcode(ERRCODE_UNDEFINED_OBJECT),
17462  errmsg("index \"%s\" for table \"%s\" does not exist",
17463  stmt->name, RelationGetRelationName(rel))));
17464 
17465  indexRel = index_open(indexOid, ShareLock);
17466 
17467  /* Check that the index is on the relation we're altering. */
17468  if (indexRel->rd_index == NULL ||
17469  indexRel->rd_index->indrelid != RelationGetRelid(rel))
17470  ereport(ERROR,
17471  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17472  errmsg("\"%s\" is not an index for table \"%s\"",
17473  RelationGetRelationName(indexRel),
17474  RelationGetRelationName(rel))));
17475  /* The AM must support uniqueness, and the index must in fact be unique. */
17476  if (!indexRel->rd_indam->amcanunique ||
17477  !indexRel->rd_index->indisunique)
17478  ereport(ERROR,
17479  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17480  errmsg("cannot use non-unique index \"%s\" as replica identity",
17481  RelationGetRelationName(indexRel))));
17482  /* Deferred indexes are not guaranteed to be always unique. */
17483  if (!indexRel->rd_index->indimmediate)
17484  ereport(ERROR,
17485  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17486  errmsg("cannot use non-immediate index \"%s\" as replica identity",
17487  RelationGetRelationName(indexRel))));
17488  /* Expression indexes aren't supported. */
17489  if (RelationGetIndexExpressions(indexRel) != NIL)
17490  ereport(ERROR,
17491  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17492  errmsg("cannot use expression index \"%s\" as replica identity",
17493  RelationGetRelationName(indexRel))));
17494  /* Predicate indexes aren't supported. */
17495  if (RelationGetIndexPredicate(indexRel) != NIL)
17496  ereport(ERROR,
17497  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17498  errmsg("cannot use partial index \"%s\" as replica identity",
17499  RelationGetRelationName(indexRel))));
17500 
17501  /* Check index for nullable columns. */
17502  for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17503  {
17504  int16 attno = indexRel->rd_index->indkey.values[key];
17505  Form_pg_attribute attr;
17506 
17507  /*
17508  * Reject any other system columns. (Going forward, we'll disallow
17509  * indexes containing such columns in the first place, but they might
17510  * exist in older branches.)
17511  */
17512  if (attno <= 0)
17513  ereport(ERROR,
17514  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17515  errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17516  RelationGetRelationName(indexRel), attno)));
17517 
17518  attr = TupleDescAttr(rel->rd_att, attno - 1);
17519  if (!attr->attnotnull)
17520  ereport(ERROR,
17521  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17522  errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17523  RelationGetRelationName(indexRel),
17524  NameStr(attr->attname))));
17525  }
17526 
17527  /* This index is suitable for use as a replica identity. Mark it. */
17528  relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17529 
17530  index_close(indexRel, NoLock);
17531 }
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:524
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5138
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5025
bool amcanunique
Definition: amapi.h:230
struct IndexAmRoutine * rd_indam
Definition: rel.h:206
static void relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, bool is_internal)
Definition: tablecmds.c:17341

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  newAccessMethod 
)
static

Definition at line 15435 of file tablecmds.c.

15436 {
15437  Relation pg_class;
15438  Oid oldAccessMethodId;
15439  HeapTuple tuple;
15440  Form_pg_class rd_rel;
15441  Oid reloid = RelationGetRelid(rel);
15442 
15443  /*
15444  * Shouldn't be called on relations having storage; these are processed in
15445  * phase 3.
15446  */
15447  Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15448 
15449  /* Get a modifiable copy of the relation's pg_class row. */
15450  pg_class = table_open(RelationRelationId, RowExclusiveLock);
15451 
15452  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15453  if (!HeapTupleIsValid(tuple))
15454  elog(ERROR, "cache lookup failed for relation %u", reloid);
15455  rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15456 
15457  /* Update the pg_class row. */
15458  oldAccessMethodId = rd_rel->relam;
15459  rd_rel->relam = newAccessMethodId;
15460 
15461  /* Leave if no update required */
15462  if (rd_rel->relam == oldAccessMethodId)
15463  {
15464  heap_freetuple(tuple);
15465  table_close(pg_class, RowExclusiveLock);
15466  return;
15467  }
15468 
15469  CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15470 
15471  /*
15472  * Update the dependency on the new access method. No dependency is added
15473  * if the new access method is InvalidOid (default case). Be very careful
15474  * that this has to compare the previous value stored in pg_class with the
15475  * new one.
15476  */
15477  if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15478  {
15479  ObjectAddress relobj,
15480  referenced;
15481 
15482  /*
15483  * New access method is defined and there was no dependency
15484  * previously, so record a new one.
15485  */
15486  ObjectAddressSet(relobj, RelationRelationId, reloid);
15487  ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15488  recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15489  }
15490  else if (OidIsValid(oldAccessMethodId) &&
15491  !OidIsValid(rd_rel->relam))
15492  {
15493  /*
15494  * There was an access method defined, and no new one, so just remove
15495  * the existing dependency.
15496  */
15497  deleteDependencyRecordsForClass(RelationRelationId, reloid,
15498  AccessMethodRelationId,
15500  }
15501  else
15502  {
15503  Assert(OidIsValid(oldAccessMethodId) &&
15504  OidIsValid(rd_rel->relam));
15505 
15506  /* Both are valid, so update the dependency */
15507  changeDependencyFor(RelationRelationId, reloid,
15508  AccessMethodRelationId,
15509  oldAccessMethodId, rd_rel->relam);
15510  }
15511 
15512  /* make the relam and dependency changes visible */
15514 
15515  InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15516 
15517  heap_freetuple(tuple);
15518  table_close(pg_class, RowExclusiveLock);
15519 }

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

◆ ATExecSetAttNotNull()

static ObjectAddress ATExecSetAttNotNull ( List **  wqueue,
Relation  rel,
const char *  colName,
LOCKMODE  lockmode 
)
static

Definition at line 8020 of file tablecmds.c.

8022 {
8025 
8026  attnum = get_attnum(RelationGetRelid(rel), colName);
8027  if (attnum == InvalidAttrNumber)
8028  ereport(ERROR,
8029  errcode(ERRCODE_UNDEFINED_COLUMN),
8030  errmsg("column \"%s\" of relation \"%s\" does not exist",
8031  colName, RelationGetRelationName(rel)));
8032 
8033  /*
8034  * Make the change, if necessary, and only if so report the column as
8035  * changed
8036  */
8037  if (set_attnotnull(wqueue, rel, attnum, false, lockmode))
8038  ObjectAddressSubSet(address, RelationRelationId,
8039  RelationGetRelid(rel), attnum);
8040 
8041  return address;
8042 }

References attnum, ereport, errcode(), errmsg(), ERROR, get_attnum(), InvalidAttrNumber, InvalidObjectAddress, ObjectAddressSubSet, RelationGetRelationName, RelationGetRelid, and set_attnotnull().

Referenced by ATExecCmd().

◆ ATExecSetCompression()

static ObjectAddress ATExecSetCompression ( Relation  rel,
const char *  column,
Node newValue,
LOCKMODE  lockmode 
)
static

Definition at line 17677 of file tablecmds.c.

17681 {
17682  Relation attrel;
17683  HeapTuple tuple;
17684  Form_pg_attribute atttableform;
17686  char *compression;
17687  char cmethod;
17688  ObjectAddress address;
17689 
17690  compression = strVal(newValue);
17691 
17692  attrel = table_open(AttributeRelationId, RowExclusiveLock);
17693 
17694  /* copy the cache entry so we can scribble on it below */
17695  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
17696  if (!HeapTupleIsValid(tuple))
17697  ereport(ERROR,
17698  (errcode(ERRCODE_UNDEFINED_COLUMN),
17699  errmsg("column \"%s\" of relation \"%s\" does not exist",
17700  column, RelationGetRelationName(rel))));
17701 
17702  /* prevent them from altering a system attribute */
17703  atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
17704  attnum = atttableform->attnum;
17705  if (attnum <= 0)
17706  ereport(ERROR,
17707  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17708  errmsg("cannot alter system column \"%s\"", column)));
17709 
17710  /*
17711  * Check that column type is compressible, then get the attribute
17712  * compression method code
17713  */
17714  cmethod = GetAttributeCompression(atttableform->atttypid, compression);
17715 
17716  /* update pg_attribute entry */
17717  atttableform->attcompression = cmethod;
17718  CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
17719 
17720  InvokeObjectPostAlterHook(RelationRelationId,
17721  RelationGetRelid(rel),
17722  attnum);
17723 
17724  /*
17725  * Apply the change to indexes as well (only for simple index columns,
17726  * matching behavior of index.c ConstructTupleDescriptor()).
17727  */
17728  SetIndexStorageProperties(rel, attrel, attnum,
17729  false, 0,
17730  true, cmethod,
17731  lockmode);
17732 
17733  heap_freetuple(tuple);
17734 
17735  table_close(attrel, RowExclusiveLock);
17736 
17737  /* make changes visible */
17739 
17740  ObjectAddressSubSet(address, RelationRelationId,
17741  RelationGetRelid(rel), attnum);
17742  return address;
17743 }
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:20863
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:9002

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

8540 {
8541  HeapTuple tuple;
8542  Form_pg_attribute attTup;
8544  Oid attrdefoid;
8545  ObjectAddress address;
8546  Expr *defval;
8548  RawColumnDefault *rawEnt;
8549 
8550  tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8551  if (!HeapTupleIsValid(tuple))
8552  ereport(ERROR,
8553  (errcode(ERRCODE_UNDEFINED_COLUMN),
8554  errmsg("column \"%s\" of relation \"%s\" does not exist",
8555  colName, RelationGetRelationName(rel))));
8556 
8557  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8558  attnum = attTup->attnum;
8559 
8560  if (attnum <= 0)
8561  ereport(ERROR,
8562  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8563  errmsg("cannot alter system column \"%s\"",
8564  colName)));
8565 
8566  if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8567  ereport(ERROR,
8568  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8569  errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8570  colName, RelationGetRelationName(rel))));
8571  ReleaseSysCache(tuple);
8572 
8573  /*
8574  * Clear all the missing values if we're rewriting the table, since this
8575  * renders them pointless.
8576  */
8577  RelationClearMissing(rel);
8578 
8579  /* make sure we don't conflict with later attribute modifications */
8581 
8582  /*
8583  * Find everything that depends on the column (constraints, indexes, etc),
8584  * and record enough information to let us recreate the objects after
8585  * rewrite.
8586  */
8588 
8589  /*
8590  * Drop the dependency records of the GENERATED expression, in particular
8591  * its INTERNAL dependency on the column, which would otherwise cause
8592  * dependency.c to refuse to perform the deletion.
8593  */
8594  attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8595  if (!OidIsValid(attrdefoid))
8596  elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8597  RelationGetRelid(rel), attnum);
8598  (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8599 
8600  /* Make above changes visible */
8602 
8603  /*
8604  * Get rid of the GENERATED expression itself. We use RESTRICT here for
8605  * safety, but at present we do not expect anything to depend on the
8606  * expression.
8607  */
8609  false, false);
8610 
8611  /* Prepare to store the new expression, in the catalogs */
8612  rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8613  rawEnt->attnum = attnum;
8614  rawEnt->raw_default = newExpr;
8615  rawEnt->missingMode = false;
8616  rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
8617 
8618  /* Store the generated expression */
8620  false, true, false, NULL);
8621 
8622  /* Make above new expression visible */
8624 
8625  /* Prepare for table rewrite */
8626  defval = (Expr *) build_column_default(rel, attnum);
8627 
8629  newval->attnum = attnum;
8630  newval->expr = expression_planner(defval);
8631  newval->is_generated = true;
8632 
8633  tab->newvals = lappend(tab->newvals, newval);
8635 
8636  /* Drop any pg_statistic entry for the column */
8638 
8639  InvokeObjectPostAlterHook(RelationRelationId,
8640  RelationGetRelid(rel), attnum);
8641 
8642  ObjectAddressSubSet(address, RelationRelationId,
8643  RelationGetRelid(rel), attnum);
8644  return address;
8645 }

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

Referenced by ATExecCmd().

◆ ATExecSetIdentity()

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

Definition at line 8307 of file tablecmds.c.

8309 {
8310  ListCell *option;
8311  DefElem *generatedEl = NULL;
8312  HeapTuple tuple;
8313  Form_pg_attribute attTup;
8315  Relation attrelation;
8316  ObjectAddress address;
8317  bool ispartitioned;
8318 
8319  ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8320  if (ispartitioned && !recurse)
8321  ereport(ERROR,
8322  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8323  errmsg("cannot change identity column of only the partitioned table"),
8324  errhint("Do not specify the ONLY keyword.")));
8325 
8326  if (rel->rd_rel->relispartition && !recursing)
8327  ereport(ERROR,
8328  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8329  errmsg("cannot change identity column of a partition"));
8330 
8331  foreach(option, castNode(List, def))
8332  {
8333  DefElem *defel = lfirst_node(DefElem, option);
8334 
8335  if (strcmp(defel->defname, "generated") == 0)
8336  {
8337  if (generatedEl)
8338  ereport(ERROR,
8339  (errcode(ERRCODE_SYNTAX_ERROR),
8340  errmsg("conflicting or redundant options")));
8341  generatedEl = defel;
8342  }
8343  else
8344  elog(ERROR, "option \"%s\" not recognized",
8345  defel->defname);
8346  }
8347 
8348  /*
8349  * Even if there is nothing to change here, we run all the checks. There
8350  * will be a subsequent ALTER SEQUENCE that relies on everything being
8351  * there.
8352  */
8353 
8354  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8355  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8356  if (!HeapTupleIsValid(tuple))
8357  ereport(ERROR,
8358  (errcode(ERRCODE_UNDEFINED_COLUMN),
8359  errmsg("column \"%s\" of relation \"%s\" does not exist",
8360  colName, RelationGetRelationName(rel))));
8361 
8362  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8363  attnum = attTup->attnum;
8364 
8365  if (attnum <= 0)
8366  ereport(ERROR,
8367  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8368  errmsg("cannot alter system column \"%s\"",
8369  colName)));
8370 
8371  if (!attTup->attidentity)
8372  ereport(ERROR,
8373  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8374  errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8375  colName, RelationGetRelationName(rel))));
8376 
8377  if (generatedEl)
8378  {
8379  attTup->attidentity = defGetInt32(generatedEl);
8380  CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8381 
8382  InvokeObjectPostAlterHook(RelationRelationId,
8383  RelationGetRelid(rel),
8384  attTup->attnum);
8385  ObjectAddressSubSet(address, RelationRelationId,
8386  RelationGetRelid(rel), attnum);
8387  }
8388  else
8389  address = InvalidObjectAddress;
8390 
8391  heap_freetuple(tuple);
8392  table_close(attrelation, RowExclusiveLock);
8393 
8394  /*
8395  * Recurse to propagate the identity change to partitions. Identity is not
8396  * inherited in regular inheritance children.
8397  */
8398  if (generatedEl && recurse && ispartitioned)
8399  {
8400  List *children;
8401  ListCell *lc;
8402 
8403  children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8404 
8405  foreach(lc, children)
8406  {
8407  Relation childrel;
8408 
8409  childrel = table_open(lfirst_oid(lc), NoLock);
8410  ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8411  table_close(childrel, NoLock);
8412  }
8413  }
8414 
8415  return address;
8416 }
int32 defGetInt32(DefElem *def)
Definition: define.c:162
char * defname
Definition: parsenodes.h:815

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

◆ ATExecSetNotNull()

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

Definition at line 7816 of file tablecmds.c.

7819 {
7820  HeapTuple tuple;
7821  Relation constr_rel;
7822  ScanKeyData skey;
7823  SysScanDesc conscan;
7825  ObjectAddress address;
7826  Constraint *constraint;
7827  CookedConstraint *ccon;
7828  List *cooked;
7829  bool is_no_inherit = false;
7830  List *ready = NIL;
7831 
7832  /* Guard against stack overflow due to overly deep inheritance tree. */
7834 
7835  /*
7836  * In cases of multiple inheritance, we might visit the same child more
7837  * than once. In the topmost call, set up a list that we fill with all
7838  * visited relations, to skip those.
7839  */
7840  if (readyRels == NULL)
7841  {
7842  Assert(!recursing);
7843  readyRels = &ready;
7844  }
7845  if (list_member_oid(*readyRels, RelationGetRelid(rel)))
7846  return InvalidObjectAddress;
7847  *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
7848 
7849  /* At top level, permission check was done in ATPrepCmd, else do it */
7850  if (recursing)
7851  {
7853  Assert(conName != NULL);
7854  }
7855 
7856  attnum = get_attnum(RelationGetRelid(rel), colName);
7857  if (attnum == InvalidAttrNumber)
7858  ereport(ERROR,
7859  (errcode(ERRCODE_UNDEFINED_COLUMN),
7860  errmsg("column \"%s\" of relation \"%s\" does not exist",
7861  colName, RelationGetRelationName(rel))));
7862 
7863  /* Prevent them from altering a system attribute */
7864  if (attnum <= 0)
7865  ereport(ERROR,
7866  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7867  errmsg("cannot alter system column \"%s\"",
7868  colName)));
7869 
7870  /* See if there's already a constraint */
7871  constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7872  ScanKeyInit(&skey,
7873  Anum_pg_constraint_conrelid,
7874  BTEqualStrategyNumber, F_OIDEQ,
7876  conscan = systable_beginscan(constr_rel, ConstraintRelidTypidNameIndexId, true,
7877  NULL, 1, &skey);
7878 
7879  while (HeapTupleIsValid(tuple = systable_getnext(conscan)))
7880  {
7881  Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7882  bool changed = false;
7883  HeapTuple copytup;
7884 
7885  if (conForm->contype != CONSTRAINT_NOTNULL)
7886  continue;
7887 
7888  if (extractNotNullColumn(tuple) != attnum)
7889  continue;
7890 
7891  copytup = heap_copytuple(tuple);
7892  conForm = (Form_pg_constraint) GETSTRUCT(copytup);
7893 
7894  /*
7895  * If we find an appropriate constraint, we're almost done, but just
7896  * need to change some properties on it: if we're recursing, increment
7897  * coninhcount; if not, set conislocal if not already set.
7898  */
7899  if (recursing)
7900  {
7901  conForm->coninhcount++;
7902  changed = true;
7903  }
7904  else if (!conForm->conislocal)
7905  {
7906  conForm->conislocal = true;
7907  changed = true;
7908  }
7909 
7910  if (changed)
7911  {
7912  CatalogTupleUpdate(constr_rel, &copytup->t_self, copytup);
7913  ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7914  }
7915 
7916  systable_endscan(conscan);
7917  table_close(constr_rel, RowExclusiveLock);
7918 
7919  if (changed)
7920  return address;
7921  else
7922  return InvalidObjectAddress;
7923  }
7924 
7925  systable_endscan(conscan);
7926  table_close(constr_rel, RowExclusiveLock);
7927 
7928  /*
7929  * If we're asked not to recurse, and children exist, raise an error for
7930  * partitioned tables. For inheritance, we act as if NO INHERIT had been
7931  * specified.
7932  */
7933  if (!recurse &&
7935  NoLock) != NIL)
7936  {
7937  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7938  ereport(ERROR,
7939  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7940  errmsg("constraint must be added to child tables too"),
7941  errhint("Do not specify the ONLY keyword."));
7942  else
7943  is_no_inherit = true;
7944  }
7945 
7946  /*
7947  * No constraint exists; we must add one. First determine a name to use,
7948  * if we haven't already.
7949  */
7950  if (!recursing)
7951  {
7952  Assert(conName == NULL);
7954  colName, "not_null",
7955  RelationGetNamespace(rel),
7956  NIL);
7957  }
7958  constraint = makeNode(Constraint);
7959  constraint->contype = CONSTR_NOTNULL;
7960  constraint->conname = conName;
7961  constraint->deferrable = false;
7962  constraint->initdeferred = false;
7963  constraint->location = -1;
7964  constraint->keys = list_make1(makeString(colName));
7965  constraint->is_no_inherit = is_no_inherit;
7966  constraint->inhcount = recursing ? 1 : 0;
7967  constraint->skip_validation = false;
7968  constraint->initially_valid = true;
7969 
7970  /* and do it */
7971  cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7972  false, !recursing, false, NULL);
7973  ccon = linitial(cooked);
7974  ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7975 
7976  InvokeObjectPostAlterHook(RelationRelationId,
7977  RelationGetRelid(rel), attnum);
7978 
7979  /*
7980  * Mark pg_attribute.attnotnull for the column. Tell that function not to
7981  * recurse, because we're going to do it here.
7982  */
7983  set_attnotnull(wqueue, rel, attnum, false, lockmode);
7984 
7985  /*
7986  * Recurse to propagate the constraint to children that don't have one.
7987  */
7988  if (recurse)
7989  {
7990  List *children;
7991  ListCell *lc;
7992 
7994  lockmode);
7995 
7996  foreach(lc, children)
7997  {
7998  Relation childrel;
7999 
8000  childrel = table_open(lfirst_oid(lc), NoLock);
8001 
8002  ATExecSetNotNull(wqueue, childrel,
8003  conName, colName, recurse, true,
8004  readyRels, lockmode);
8005 
8006  table_close(childrel, NoLock);
8007  }
8008  }
8009 
8010  return address;
8011 }
AttrNumber extractNotNullColumn(HeapTuple constrTup)
ParseLoc location
Definition: parsenodes.h:2783
List * keys
Definition: parsenodes.h:2754
String * makeString(char *str)
Definition: value.c:63

References AddRelationNewConstraints(), Assert, AT_AddConstraint, ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_TABLE, attnum, BTEqualStrategyNumber, CatalogTupleUpdate(), check_stack_depth(), ChooseConstraintName(), Constraint::conname, CookedConstraint::conoid, CONSTR_NOTNULL, Constraint::contype, Constraint::deferrable, ereport, errcode(), errhint(), errmsg(), ERROR, extractNotNullColumn(), find_inheritance_children(), get_attnum(), GETSTRUCT, heap_copytuple(), HeapTupleIsValid, Constraint::inhcount, Constraint::initdeferred, Constraint::initially_valid, InvalidAttrNumber, InvalidObjectAddress, InvokeObjectPostAlterHook, Constraint::is_no_inherit, Constraint::keys, lappend_oid(), lfirst_oid, linitial, list_make1, list_member_oid(), Constraint::location, makeNode, makeString(), NIL, NoLock, ObjectAddressSet, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), set_attnotnull(), Constraint::skip_validation, systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecSetOptions()

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

Definition at line 8923 of file tablecmds.c.

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

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

15557 {
15558  Oid relid;
15559  Relation pgclass;
15560  HeapTuple tuple;
15561  HeapTuple newtuple;
15562  Datum datum;
15563  bool isnull;
15564  Datum newOptions;
15565  Datum repl_val[Natts_pg_class];
15566  bool repl_null[Natts_pg_class];
15567  bool repl_repl[Natts_pg_class];
15568  static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
15569 
15570  if (defList == NIL && operation != AT_ReplaceRelOptions)
15571  return; /* nothing to do */
15572 
15573  pgclass = table_open(RelationRelationId, RowExclusiveLock);
15574 
15575  /* Fetch heap tuple */
15576  relid = RelationGetRelid(rel);
15577  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
15578  if (!HeapTupleIsValid(tuple))
15579  elog(ERROR, "cache lookup failed for relation %u", relid);
15580 
15581  if (operation == AT_ReplaceRelOptions)
15582  {
15583  /*
15584  * If we're supposed to replace the reloptions list, we just pretend
15585  * there were none before.
15586  */
15587  datum = (Datum) 0;
15588  isnull = true;
15589  }
15590  else
15591  {
15592  /* Get the old reloptions */
15593  datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15594  &isnull);
15595  }
15596 
15597  /* Generate new proposed reloptions (text array) */
15598  newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15599  defList, NULL, validnsps, false,
15600  operation == AT_ResetRelOptions);
15601 
15602  /* Validate */
15603  switch (rel->rd_rel->relkind)
15604  {
15605  case RELKIND_RELATION:
15606  case RELKIND_TOASTVALUE:
15607  case RELKIND_MATVIEW:
15608  (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
15609  break;
15610  case RELKIND_PARTITIONED_TABLE:
15611  (void) partitioned_table_reloptions(newOptions, true);
15612  break;
15613  case RELKIND_VIEW:
15614  (void) view_reloptions(newOptions, true);
15615  break;
15616  case RELKIND_INDEX:
15617  case RELKIND_PARTITIONED_INDEX:
15618  (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
15619  break;
15620  default:
15621  ereport(ERROR,
15622  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15623  errmsg("cannot set options for relation \"%s\"",
15625  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
15626  break;
15627  }
15628 
15629  /* Special-case validation of view options */
15630  if (rel->rd_rel->relkind == RELKIND_VIEW)
15631  {
15632  Query *view_query = get_view_query(rel);
15633  List *view_options = untransformRelOptions(newOptions);
15634  ListCell *cell;
15635  bool check_option = false;
15636 
15637  foreach(cell, view_options)
15638  {
15639  DefElem *defel = (DefElem *) lfirst(cell);
15640 
15641  if (strcmp(defel->defname, "check_option") == 0)
15642  check_option = true;
15643  }
15644 
15645  /*
15646  * If the check option is specified, look to see if the view is
15647  * actually auto-updatable or not.
15648  */
15649  if (check_option)
15650  {
15651  const char *view_updatable_error =
15652  view_query_is_auto_updatable(view_query, true);
15653 
15654  if (view_updatable_error)
15655  ereport(ERROR,
15656  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15657  errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15658  errhint("%s", _(view_updatable_error))));
15659  }
15660  }
15661 
15662  /*
15663  * All we need do here is update the pg_class row; the new options will be
15664  * propagated into relcaches during post-commit cache inval.
15665  */
15666  memset(repl_val, 0, sizeof(repl_val));
15667  memset(repl_null, false, sizeof(repl_null));
15668  memset(repl_repl, false, sizeof(repl_repl));
15669 
15670  if (newOptions != (Datum) 0)
15671  repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15672  else
15673  repl_null[Anum_pg_class_reloptions - 1] = true;
15674 
15675  repl_repl[Anum_pg_class_reloptions - 1] = true;
15676 
15677  newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15678  repl_val, repl_null, repl_repl);
15679 
15680  CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15681 
15682  InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15683 
15684  heap_freetuple(newtuple);
15685 
15686  ReleaseSysCache(tuple);
15687 
15688  /* repeat the whole exercise for the toast table, if there's one */
15689  if (OidIsValid(rel->rd_rel->reltoastrelid))
15690  {
15691  Relation toastrel;
15692  Oid toastid = rel->rd_rel->reltoastrelid;
15693 
15694  toastrel = table_open(toastid, lockmode);
15695 
15696  /* Fetch heap tuple */
15697  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15698  if (!HeapTupleIsValid(tuple))
15699  elog(ERROR, "cache lookup failed for relation %u", toastid);
15700 
15701  if (operation == AT_ReplaceRelOptions)
15702  {
15703  /*
15704  * If we're supposed to replace the reloptions list, we just
15705  * pretend there were none before.
15706  */
15707  datum = (Datum) 0;
15708  isnull = true;
15709  }
15710  else
15711  {
15712  /* Get the old reloptions */
15713  datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15714  &isnull);
15715  }
15716 
15717  newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15718  defList, "toast", validnsps, false,
15719  operation == AT_ResetRelOptions);
15720 
15721  (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15722 
15723  memset(repl_val, 0, sizeof(repl_val));
15724  memset(repl_null, false, sizeof(repl_null));
15725  memset(repl_repl, false, sizeof(repl_repl));
15726 
15727  if (newOptions != (Datum) 0)
15728  repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15729  else
15730  repl_null[Anum_pg_class_reloptions - 1] = true;
15731 
15732  repl_repl[Anum_pg_class_reloptions - 1] = true;
15733 
15734  newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15735  repl_val, repl_null, repl_repl);
15736 
15737  CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15738 
15739  InvokeObjectPostAlterHookArg(RelationRelationId,
15740  RelationGetRelid(toastrel), 0,
15741  InvalidOid, true);
15742 
15743  heap_freetuple(newtuple);
15744 
15745  ReleaseSysCache(tuple);
15746 
15747  table_close(toastrel, NoLock);
15748  }
15749 
15750  table_close(pgclass, RowExclusiveLock);
15751 }
#define _(x)
Definition: elog.c:90
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:200
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
Definition: reloptions.c:2054
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1331
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
Definition: reloptions.c:2019
bytea * view_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:1998
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:1984
#define HEAP_RELOPT_NAMESPACES
Definition: reloptions.h:61
const char * view_query_is_auto_updatable(Query *viewquery, bool check_cols)
Query * get_view_query(Relation view)
amoptions_function amoptions
Definition: amapi.h:275

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(), InvalidOid, InvokeObjectPostAlterHook, InvokeObjectPostAlterHookArg, lfirst, NIL, NoLock, ObjectIdGetDatum(), OidIsValid, partitioned_table_reloptions(), RelationData::rd_indam, RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), SysCacheGetAttr(), HeapTupleData::t_self, table_close(), table_open(), transformRelOptions(), untransformRelOptions(), view_query_is_auto_updatable(), and view_reloptions().

Referenced by ATExecCmd().

◆ ATExecSetRowSecurity()

static void ATExecSetRowSecurity ( Relation  rel,
bool  rls 
)
static

Definition at line 17537 of file tablecmds.c.

17538 {
17539  Relation pg_class;
17540  Oid relid;
17541  HeapTuple tuple;
17542 
17543  relid = RelationGetRelid(rel);
17544 
17545  /* Pull the record for this relation and update it */
17546  pg_class = table_open(RelationRelationId, RowExclusiveLock);
17547 
17548  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17549 
17550  if (!HeapTupleIsValid(tuple))
17551  elog(ERROR, "cache lookup failed for relation %u", relid);
17552 
17553  ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
17554  CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17555 
17556  InvokeObjectPostAlterHook(RelationRelationId,
17557  RelationGetRelid(rel), 0);
17558 
17559  table_close(pg_class, RowExclusiveLock);
17560  heap_freetuple(tuple);
17561 }

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

8789 {
8790  int newtarget = 0;
8791  bool newtarget_default;
8792  Relation attrelation;
8793  HeapTuple tuple,
8794  newtuple;
8795  Form_pg_attribute attrtuple;
8797  ObjectAddress address;
8798  Datum repl_val[Natts_pg_attribute];
8799  bool repl_null[Natts_pg_attribute];
8800  bool repl_repl[Natts_pg_attribute];
8801 
8802  /*
8803  * We allow referencing columns by numbers only for indexes, since table
8804  * column numbers could contain gaps if columns are later dropped.
8805  */
8806  if (rel->rd_rel->relkind != RELKIND_INDEX &&
8807  rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8808  !colName)
8809  ereport(ERROR,
8810  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8811  errmsg("cannot refer to non-index column by number")));
8812 
8813  /* -1 was used in previous versions for the default setting */
8814  if (newValue && intVal(newValue) != -1)
8815  {
8816  newtarget = intVal(newValue);
8817  newtarget_default = false;
8818  }
8819  else
8820  newtarget_default = true;
8821 
8822  if (!newtarget_default)
8823  {
8824  /*
8825  * Limit target to a sane range
8826  */
8827  if (newtarget < 0)
8828  {
8829  ereport(ERROR,
8830  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8831  errmsg("statistics target %d is too low",
8832  newtarget)));
8833  }
8834  else if (newtarget > MAX_STATISTICS_TARGET)
8835  {
8836  newtarget = MAX_STATISTICS_TARGET;
8837  ereport(WARNING,
8838  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8839  errmsg("lowering statistics target to %d",
8840  newtarget)));
8841  }
8842  }
8843 
8844  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8845 
8846  if (colName)
8847  {
8848  tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8849 
8850  if (!HeapTupleIsValid(tuple))
8851  ereport(ERROR,
8852  (errcode(ERRCODE_UNDEFINED_COLUMN),
8853  errmsg("column \"%s\" of relation \"%s\" does not exist",
8854  colName, RelationGetRelationName(rel))));
8855  }
8856  else
8857  {
8858  tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8859 
8860  if (!HeapTupleIsValid(tuple))
8861  ereport(ERROR,
8862  (errcode(ERRCODE_UNDEFINED_COLUMN),
8863  errmsg("column number %d of relation \"%s\" does not exist",
8864  colNum, RelationGetRelationName(rel))));
8865  }
8866 
8867  attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8868 
8869  attnum = attrtuple->attnum;
8870  if (attnum <= 0)
8871  ereport(ERROR,
8872  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8873  errmsg("cannot alter system column \"%s\"",
8874  colName)));
8875 
8876  if (rel->rd_rel->relkind == RELKIND_INDEX ||
8877  rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8878  {
8879  if (attnum > rel->rd_index->indnkeyatts)
8880  ereport(ERROR,
8881  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8882  errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8883  NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8884  else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8885  ereport(ERROR,
8886  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8887  errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8888  NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8889  errhint("Alter statistics on table column instead.")));
8890  }
8891 
8892  /* Build new tuple. */
8893  memset(repl_null, false, sizeof(repl_null));
8894  memset(repl_repl, false, sizeof(repl_repl));
8895  if (!newtarget_default)
8896  repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8897  else
8898  repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8899  repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8900  newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8901  repl_val, repl_null, repl_repl);
8902  CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8903 
8904  InvokeObjectPostAlterHook(RelationRelationId,
8905  RelationGetRelid(rel),
8906  attrtuple->attnum);
8907  ObjectAddressSubSet(address, RelationRelationId,
8908  RelationGetRelid(rel), attnum);
8909 
8910  heap_freetuple(newtuple);
8911 
8912  ReleaseSysCache(tuple);
8913 
8914  table_close(attrelation, RowExclusiveLock);
8915 
8916  return address;
8917 }
HeapTuple SearchSysCacheAttNum(Oid relid, int16 attnum)
Definition: syscache.c:422
#define MAX_STATISTICS_TARGET
Definition: vacuum.h:305
#define intVal(v)
Definition: value.h:79

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

Referenced by ATExecCmd().

◆ ATExecSetStorage()

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

Definition at line 9065 of file tablecmds.c.

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

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

15759 {
15760  Relation rel;
15761  Oid reltoastrelid;
15762  RelFileNumber newrelfilenumber;
15763  RelFileLocator newrlocator;
15764  List *reltoastidxids = NIL;
15765  ListCell *lc;
15766 
15767  /*
15768  * Need lock here in case we are recursing to toast table or index
15769  */
15770  rel = relation_open(tableOid, lockmode);
15771 
15772  /* Check first if relation can be moved to new tablespace */
15773  if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15774  {
15775  InvokeObjectPostAlterHook(RelationRelationId,
15776  RelationGetRelid(rel), 0);
15777  relation_close(rel, NoLock);
15778  return;
15779  }
15780 
15781  reltoastrelid = rel->rd_rel->reltoastrelid;
15782  /* Fetch the list of indexes on toast relation if necessary */
15783  if (OidIsValid(reltoastrelid))
15784  {
15785  Relation toastRel = relation_open(reltoastrelid, lockmode);
15786 
15787  reltoastidxids = RelationGetIndexList(toastRel);
15788  relation_close(toastRel, lockmode);
15789  }
15790 
15791  /*
15792  * Relfilenumbers are not unique in databases across tablespaces, so we
15793  * need to allocate a new one in the new tablespace.
15794  */
15795  newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15796  rel->rd_rel->relpersistence);
15797 
15798  /* Open old and new relation */
15799  newrlocator = rel->rd_locator;
15800  newrlocator.relNumber = newrelfilenumber;
15801  newrlocator.spcOid = newTableSpace;
15802 
15803  /* hand off to AM to actually create new rel storage and copy the data */
15804  if (rel->rd_rel->relkind == RELKIND_INDEX)
15805  {
15806  index_copy_data(rel, newrlocator);
15807  }
15808  else
15809  {
15810  Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15811  table_relation_copy_data(rel, &newrlocator);
15812  }
15813 
15814  /*
15815  * Update the pg_class row.
15816  *
15817  * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15818  * executed on pg_class or its indexes (the above copy wouldn't contain
15819  * the updated pg_class entry), but that's forbidden with
15820  * CheckRelationTableSpaceMove().
15821  */
15822  SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15823 
15824  InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15825 
15827 
15828  relation_close(rel, NoLock);
15829 
15830  /* Make sure the reltablespace change is visible */
15832 
15833  /* Move associated toast relation and/or indexes, too */
15834  if (OidIsValid(reltoastrelid))
15835  ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15836  foreach(lc, reltoastidxids)
15837  ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15838 
15839  /* Clean up */
15840  list_free(reltoastidxids);
15841 }
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition: catalog.c:500
void RelationAssumeNewRelfilelocator(Relation relation)
Definition: relcache.c:3925
Oid RelFileNumber
Definition: relpath.h:25
RelFileNumber relNumber
static void table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
Definition: tableam.h:1661
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:15758
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3629
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3686
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:16052

References Assert, 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 ATRewriteTables().

◆ ATExecSetTableSpaceNoStorage()

static void ATExecSetTableSpaceNoStorage ( Relation  rel,
Oid  newTableSpace 
)
static

Definition at line 15851 of file tablecmds.c.

15852 {
15853  /*
15854  * Shouldn't be called on relations having storage; these are processed in
15855  * phase 3.
15856  */
15857  Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15858 
15859  /* check if relation can be moved to its new tablespace */
15860  if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15861  {
15862  InvokeObjectPostAlterHook(RelationRelationId,
15863  RelationGetRelid(rel),
15864  0);
15865  return;
15866  }
15867 
15868  /* Update can be done, so change reltablespace */
15869  SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15870 
15871  InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15872 
15873  /* Make sure the reltablespace change is visible */
15875 }

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

21244 {
21245  Relation splitRel;
21246  Oid splitRelOid;
21247  char relname[NAMEDATALEN];
21248  Oid namespaceId;
21249  ListCell *listptr,
21250  *listptr2;
21251  bool isSameName = false;
21252  char tmpRelName[NAMEDATALEN];
21253  List *newPartRels = NIL;
21254  ObjectAddress object;
21255  RangeVar *parentName;
21256  Oid defaultPartOid;
21257 
21258  defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
21259 
21260  /*
21261  * We are going to detach and remove this partition: need to use exclusive
21262  * lock for preventing DML-queries to the partition.
21263  */
21264  splitRel = table_openrv(cmd->name, AccessExclusiveLock);
21265 
21266  splitRelOid = RelationGetRelid(splitRel);
21267 
21268  /* Check descriptions of new partitions. */
21269  foreach(listptr, cmd->partlist)
21270  {
21271  Oid existing_relid;
21272  SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21273 
21275 
21276  /*
21277  * Look up the namespace in which we are supposed to create the
21278  * partition, check we have permission to create there, lock it
21279  * against concurrent drop, and mark stmt->relation as
21280  * RELPERSISTENCE_TEMP if a temporary namespace is selected.
21281  */
21282  namespaceId =
21284 
21285  /*
21286  * This would fail later on anyway if the relation already exists. But
21287  * by catching it here we can emit a nicer error message.
21288  */
21289  existing_relid = get_relname_relid(relname, namespaceId);
21290  if (existing_relid == splitRelOid && !isSameName)
21291  /* One new partition can have the same name as split partition. */
21292  isSameName = true;
21293  else if (existing_relid != InvalidOid)
21294  ereport(ERROR,
21295  (errcode(ERRCODE_DUPLICATE_TABLE),
21296  errmsg("relation \"%s\" already exists", relname)));
21297  }
21298 
21299  /* Detach split partition. */
21300  RemoveInheritance(splitRel, rel, false);
21301  /* Do the final part of detaching. */
21302  DetachPartitionFinalize(rel, splitRel, false, defaultPartOid);
21303 
21304  /*
21305  * If new partition has the same name as split partition then we should
21306  * rename split partition for reusing name.
21307  */
21308  if (isSameName)
21309  {
21310  /*
21311  * We must bump the command counter to make the split partition tuple
21312  * visible for renaming.
21313  */
21315  /* Rename partition. */
21316  sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
21317  RenameRelationInternal(splitRelOid, tmpRelName, false, false);
21318 
21319  /*
21320  * We must bump the command counter to make the split partition tuple
21321  * visible after renaming.
21322  */
21324  }
21325 
21326  /* Create new partitions (like split partition), without indexes. */
21328  RelationGetRelationName(rel), -1);
21329  foreach(listptr, cmd->partlist)
21330  {
21331  SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21332  Relation newPartRel;
21333 
21334  createPartitionTable(sps->name, parentName, context);
21335 
21336  /* Open the new partition and acquire exclusive lock on it. */
21337  newPartRel = table_openrv(sps->name, AccessExclusiveLock);
21338 
21339  newPartRels = lappend(newPartRels, newPartRel);
21340  }
21341 
21342  /* Copy data from split partition to new partitions. */
21343  moveSplitTableRows(rel, splitRel, cmd->partlist, newPartRels, defaultPartOid);
21344  /* Keep the lock until commit. */
21345  table_close(splitRel, NoLock);
21346 
21347  /* Attach new partitions to partitioned table. */
21348  forboth(listptr, cmd->partlist, listptr2, newPartRels)
21349  {
21350  SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21351  Relation newPartRel = (Relation) lfirst(listptr2);
21352 
21353  /*
21354  * wqueue = NULL: verification for each cloned constraint is not
21355  * needed.
21356  */
21357  attachPartitionTable(NULL, rel, newPartRel, sps->bound);
21358  /* Keep the lock until commit. */
21359  table_close(newPartRel, NoLock);
21360  }
21361 
21362  /* Drop split partition. */
21363  object.classId = RelationRelationId;
21364  object.objectId = splitRelOid;
21365  object.objectSubId = 0;
21366  /* Probably DROP_CASCADE is not needed. */
21367  performDeletion(&object, DROP_RESTRICT, 0);
21368 }
NameData relname
Definition: pg_class.h:38
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
PartitionBoundSpec * bound
Definition: parsenodes.h:948
static void moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPartRels, Oid defaultPartOid)
Definition: tablecmds.c:20996

References AccessExclusiveLock, attachPartitionTable(), SinglePartitionSpec::bound, CommandCounterIncrement(), context, createPartitionTable(), DetachPartitionFinalize(), DROP_RESTRICT, ereport, errcode(), errmsg(), ERROR, forboth, get_default_oid_from_partdesc(), get_namespace_name(), get_relname_relid(), InvalidOid, lappend(), lfirst, makeRangeVar(), moveSplitTableRows(), MyProcPid, SinglePartitionSpec::name, PartitionCmd::name, NAMEDATALEN, NIL, NoLock, PartitionCmd::partlist, performDeletion(), RangeVarGetAndCheckCreationNamespace(), RelationGetNamespace, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, relname, RangeVar::relname, RemoveInheritance(), RenameRelationInternal(), sprintf, strlcpy(), 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 12000 of file tablecmds.c.

12002 {
12003  Relation conrel;
12004  SysScanDesc scan;
12005  ScanKeyData skey[3];
12006  HeapTuple tuple;
12007  Form_pg_constraint con;
12008  ObjectAddress address;
12009 
12010  conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12011 
12012  /*
12013  * Find and check the target constraint
12014  */
12015  ScanKeyInit(&skey[0],
12016  Anum_pg_constraint_conrelid,
12017  BTEqualStrategyNumber, F_OIDEQ,
12019  ScanKeyInit(&skey[1],
12020  Anum_pg_constraint_contypid,
12021  BTEqualStrategyNumber, F_OIDEQ,
12023  ScanKeyInit(&skey[2],
12024  Anum_pg_constraint_conname,
12025  BTEqualStrategyNumber, F_NAMEEQ,
12026  CStringGetDatum(constrName));
12027  scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12028  true, NULL, 3, skey);
12029 
12030  /* There can be at most one matching row */
12031  if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12032  ereport(ERROR,
12033  (errcode(ERRCODE_UNDEFINED_OBJECT),
12034  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12035  constrName, RelationGetRelationName(rel))));
12036 
12037  con = (Form_pg_constraint) GETSTRUCT(tuple);
12038  if (con->contype != CONSTRAINT_FOREIGN &&
12039  con->contype != CONSTRAINT_CHECK)
12040  ereport(ERROR,
12041  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12042  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12043  constrName, RelationGetRelationName(rel))));
12044 
12045  if (!con->convalidated)
12046  {
12047  AlteredTableInfo *tab;
12048  HeapTuple copyTuple;
12049  Form_pg_constraint copy_con;
12050 
12051  if (con->contype == CONSTRAINT_FOREIGN)
12052  {
12053  NewConstraint *newcon;
12054  Constraint *fkconstraint;
12055 
12056  /* Queue validation for phase 3 */
12057  fkconstraint = makeNode(Constraint);
12058  /* for now this is all we need */
12059  fkconstraint->conname = constrName;
12060 
12061  newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12062  newcon->name = constrName;
12063  newcon->contype = CONSTR_FOREIGN;
12064  newcon->refrelid = con->confrelid;
12065  newcon->refindid = con->conindid;
12066  newcon->conid = con->oid;
12067  newcon->qual = (Node *) fkconstraint;
12068 
12069  /* Find or create work queue entry for this table */
12070  tab = ATGetQueueEntry(wqueue, rel);
12071  tab->constraints = lappend(tab->constraints, newcon);
12072 
12073  /*
12074  * We disallow creating invalid foreign keys to or from
12075  * partitioned tables, so ignoring the recursion bit is okay.
12076  */
12077  }
12078  else if (con->contype == CONSTRAINT_CHECK)
12079  {
12080  List *children = NIL;
12081  ListCell *child;
12082  NewConstraint *newcon;
12083  Datum val;
12084  char *conbin;
12085 
12086  /*
12087  * If we're recursing, the parent has already done this, so skip
12088  * it. Also, if the constraint is a NO INHERIT constraint, we
12089  * shouldn't try to look for it in the children.
12090  */
12091  if (!recursing && !con->connoinherit)
12092  children = find_all_inheritors(RelationGetRelid(rel),
12093  lockmode, NULL);
12094 
12095  /*
12096  * For CHECK constraints, we must ensure that we only mark the
12097  * constraint as validated on the parent if it's already validated
12098  * on the children.
12099  *
12100  * We recurse before validating on the parent, to reduce risk of
12101  * deadlocks.
12102  */
12103  foreach(child, children)
12104  {
12105  Oid childoid = lfirst_oid(child);
12106  Relation childrel;
12107 
12108  if (childoid == RelationGetRelid(rel))
12109  continue;
12110 
12111  /*
12112  * If we are told not to recurse, there had better not be any
12113  * child tables, because we can't mark the constraint on the
12114  * parent valid unless it is valid for all child tables.
12115  */
12116  if (!recurse)
12117  ereport(ERROR,
12118  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12119  errmsg("constraint must be validated on child tables too")));
12120 
12121  /* find_all_inheritors already got lock */
12122  childrel = table_open(childoid, NoLock);
12123 
12124  ATExecValidateConstraint(wqueue, childrel, constrName, false,
12125  true, lockmode);
12126  table_close(childrel, NoLock);
12127  }
12128 
12129  /* Queue validation for phase 3 */
12130  newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12131  newcon->name = constrName;
12132  newcon->contype = CONSTR_CHECK;
12133  newcon->refrelid = InvalidOid;
12134  newcon->refindid = InvalidOid;
12135  newcon->conid = con->oid;
12136 
12137  val = SysCacheGetAttrNotNull(CONSTROID, tuple,
12138  Anum_pg_constraint_conbin);
12139  conbin = TextDatumGetCString(val);
12140  newcon->qual = (Node *) stringToNode(conbin);
12141 
12142  /* Find or create work queue entry for this table */
12143  tab = ATGetQueueEntry(wqueue, rel);
12144  tab->constraints = lappend(tab->constraints, newcon);
12145 
12146  /*
12147  * Invalidate relcache so that others see the new validated
12148  * constraint.
12149  */
12151  }
12152 
12153  /*
12154  * Now update the catalog, while we have the door open.
12155  */
12156  copyTuple = heap_copytuple(tuple);
12157  copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12158  copy_con->convalidated = true;
12159  CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12160 
12161  InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12162 
12163  heap_freetuple(copyTuple);
12164 
12165  ObjectAddressSet(address, ConstraintRelationId, con->oid);
12166  }
12167  else
12168  address = InvalidObjectAddress; /* already validated */
12169 
12170  systable_endscan(scan);
12171 
12172  table_close(conrel, RowExclusiveLock);
12173 
12174  return address;
12175 }
#define TextDatumGetCString(d)
Definition: builtins.h:98
long val
Definition: informix.c:670
void * stringToNode(const char *str)
Definition: read.c:90
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:510

References ATGetQueueEntry(), BTEqualStrategyNumber, CacheInvalidateRelcache(), CatalogTupleUpdate(), NewConstraint::conid, Constraint::conname, CONSTR_CHECK, CONSTR_FOREIGN, AlteredTableInfo::constraints, NewConstraint::contype, CStringGetDatum(), ereport, errcode(), errmsg(), ERROR, find_all_inheritors(), GETSTRUCT, heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvalidObjectAddress, InvalidOid, InvokeObjectPostAlterHook, lappend(), lfirst_oid, makeNode, NewConstraint::name, NIL, NoLock, ObjectAddressSet, ObjectIdGetDatum(), palloc0(), NewConstraint::qual, NewConstraint::refindid, NewConstraint::refrelid, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), stringToNode(), SysCacheGetAttrNotNull(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), table_open(), TextDatumGetCString, and val.

Referenced by ATExecCmd().

◆ ATGetQueueEntry()

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

Definition at line 6429 of file tablecmds.c.

6430 {
6431  Oid relid = RelationGetRelid(rel);
6432  AlteredTableInfo *tab;
6433  ListCell *ltab;
6434 
6435  foreach(ltab, *wqueue)
6436  {
6437  tab = (AlteredTableInfo *) lfirst(ltab);
6438  if (tab->relid == relid)
6439  return tab;
6440  }
6441 
6442  /*
6443  * Not there, so add it. Note that we make a copy of the relation's
6444  * existing descriptor before anything interesting can happen to it.
6445  */
6446  tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6447  tab->relid = relid;
6448  tab->rel = NULL; /* set later */
6449  tab->relkind = rel->rd_rel->relkind;
6451  tab->newAccessMethod = InvalidOid;
6452  tab->chgAccessMethod = false;
6453  tab->newTableSpace = InvalidOid;
6454  tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6455  tab->chgPersistence = false;
6456 
6457  *wqueue = lappend(*wqueue, tab);
6458 
6459  return tab;
6460 }
char newrelpersistence
Definition: tablecmds.c:195
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:173

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

Referenced by addFkRecurseReferencing(), ATAddCheckNNConstraint(), ATExecAddColumn(), ATExecValidateConstraint(), ATPostAlterTypeParse(), ATPrepCmd(), DetachAddConstraintIfNeeded(), QueuePartitionConstraintValidation(), and set_attnotnull().

◆ ATInheritAdjustNotNulls()

static void ATInheritAdjustNotNulls ( Relation  parent_rel,
Relation  child_rel,
int  inhcount 
)
static

Definition at line 17058 of file tablecmds.c.

17059 {
17060  Bitmapset *pkattnos;
17061 
17062  /* Quick exit when parent has no PK */
17063  if (!parent_rel->rd_rel->relhasindex)
17064  return;
17065 
17066  pkattnos = RelationGetIndexAttrBitmap(parent_rel,
17068  if (pkattnos != NULL)
17069  {
17070  Bitmapset *childattnums = NULL;
17071  AttrMap *attmap;
17072  int i;
17073 
17074  attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17075  RelationGetDescr(child_rel), true);
17076 
17077  i = -1;
17078  while ((i = bms_next_member(pkattnos, i)) >= 0)
17079  {
17080  childattnums = bms_add_member(childattnums,
17082  }
17083 
17084  /*
17085  * CCI is needed in case there's a NOT NULL PRIMARY KEY column in the
17086  * parent: the relevant not-null constraint in the child already had
17087  * its inhcount modified earlier.
17088  */
17090  AdjustNotNullInheritance(RelationGetRelid(child_rel), childattnums,
17091  inhcount);
17092  }
17093 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
void AdjustNotNullInheritance(Oid relid, Bitmapset *columns, int count)

References AdjustNotNullInheritance(), AttrMap::attnums, bms_add_member(), bms_next_member(), build_attrmap_by_name(), CommandCounterIncrement(), FirstLowInvalidHeapAttributeNumber, i, INDEX_ATTR_BITMAP_PRIMARY_KEY, RelationData::rd_rel, RelationGetDescr, RelationGetIndexAttrBitmap(), and RelationGetRelid.

Referenced by ATExecAddInherit(), and ATExecDropInherit().

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

5633 {
5634  AlterTableCmd *newcmd = NULL;
5636  List *beforeStmts;
5637  List *afterStmts;
5638  ListCell *lc;
5639 
5640  /* Gin up an AlterTableStmt with just this subcommand and this table */
5641  atstmt->relation =
5644  -1);
5645  atstmt->relation->inh = recurse;
5646  atstmt->cmds = list_make1(cmd);
5647  atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5648  atstmt->missing_ok = false;
5649 
5650  /* Transform the AlterTableStmt */
5652  atstmt,
5653  context->queryString,
5654  &beforeStmts,
5655  &afterStmts);
5656 
5657  /* Execute any statements that should happen before these subcommand(s) */
5658  foreach(lc, beforeStmts)
5659  {
5660  Node *stmt = (Node *) lfirst(lc);
5661 
5664  }
5665 
5666  /* Examine the transformed subcommands and schedule them appropriately */
5667  foreach(lc, atstmt->cmds)
5668  {
5670  AlterTablePass pass;
5671 
5672  /*
5673  * This switch need only cover the subcommand types that can be added
5674  * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5675  * executing the subcommand immediately, as a substitute for the
5676  * original subcommand. (Note, however, that this does cause
5677  * AT_AddConstraint subcommands to be rescheduled into later passes,
5678  * which is important for index and foreign key constraints.)
5679  *
5680  * We assume we needn't do any phase-1 checks for added subcommands.
5681  */
5682  switch (cmd2->subtype)
5683  {
5684  case AT_SetAttNotNull:
5685  ATSimpleRecursion(wqueue, rel, cmd2, recurse, lockmode, context);
5686  pass = AT_PASS_COL_ATTRS;
5687  break;
5688  case AT_AddIndex:
5689 
5690  /*
5691  * A primary key on an inheritance parent needs supporting NOT
5692  * NULL constraint on its children; enqueue commands to create
5693  * those or mark them inherited if they already exist.
5694  */
5695  ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
5696  pass = AT_PASS_ADD_INDEX;
5697  break;
5698  case AT_AddIndexConstraint:
5699  /* as above */
5700  ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
5701  pass = AT_PASS_ADD_INDEXCONSTR;
5702  break;
5703  case AT_AddConstraint:
5704  /* Recursion occurs during execution phase */
5705  if (recurse)
5706  cmd2->recurse = true;
5707  switch (castNode(Constraint, cmd2->def)->contype)
5708  {
5709  case CONSTR_PRIMARY:
5710  case CONSTR_UNIQUE:
5711  case CONSTR_EXCLUSION:
5712  pass = AT_PASS_ADD_INDEXCONSTR;
5713  break;
5714  default:
5715  pass = AT_PASS_ADD_OTHERCONSTR;
5716  break;
5717  }
5718  break;
5720  /* This command never recurses */
5721  /* No command-specific prep needed */
5722  pass = AT_PASS_MISC;
5723  break;
5724  default:
5725  pass = cur_pass;
5726  break;
5727  }
5728 
5729  if (pass < cur_pass)
5730  {
5731  /* Cannot schedule into a pass we already finished */
5732  elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5733  pass);
5734  }
5735  else if (pass > cur_pass)
5736  {
5737  /* OK, queue it up for later */
5738  tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5739  }
5740  else
5741  {
5742  /*
5743  * We should see at most one subcommand for the current pass,
5744  * which is the transformed version of the original subcommand.
5745  */
5746  if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5747  {
5748  /* Found the transformed version of our subcommand */
5749  newcmd = cmd2;
5750  }
5751  else
5752  elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5753  pass);
5754  }
5755  }
5756 
5757  /* Queue up any after-statements to happen at the end */
5758  tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5759 
5760  return newcmd;
5761 }
AlterTableStmt * transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, const char *queryString, List **beforeStmts, List **afterStmts)
RangeVar * relation
Definition: parsenodes.h:2347
ObjectType objtype
Definition: parsenodes.h:2349
List * afterStmts
Definition: tablecmds.c:187
List * subcmds[AT_NUM_PASSES]
Definition: tablecmds.c:183
bool inh
Definition: primnodes.h:85
static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9352
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6687
void ProcessUtilityForAlterTable(Node *stmt, AlterTableUtilityContext *context)
Definition: utility.c:1956

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, AT_SetAttNotNull, ATPrepAddPrimaryKey(), ATSimpleRecursion(), castNode, AlterTableStmt::cmds, CommandCounterIncrement(), CONSTR_EXCLUSION, CONSTR_PRIMARY, CONSTR_UNIQUE, context, 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(), 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 14363 of file tablecmds.c.

14364 {
14365  ObjectAddress obj;
14366  ObjectAddresses *objects;
14367  ListCell *def_item;
14368  ListCell *oid_item;
14369 
14370  /*
14371  * Collect all the constraints and indexes to drop so we can process them
14372  * in a single call. That way we don't have to worry about dependencies
14373  * among them.
14374  */
14375  objects = new_object_addresses();
14376 
14377  /*
14378  * Re-parse the index and constraint definitions, and attach them to the
14379  * appropriate work queue entries. We do this before dropping because in
14380  * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14381  * lock on the table the constraint is attached to, and we need to get
14382  * that before reparsing/dropping.
14383  *
14384  * We can't rely on the output of deparsing to tell us which relation to
14385  * operate on, because concurrent activity might have made the name
14386  * resolve differently. Instead, we've got to use the OID of the
14387  * constraint or index we're processing to figure out which relation to
14388  * operate on.
14389  */
14390  forboth(oid_item, tab->changedConstraintOids,
14391  def_item, tab->changedConstraintDefs)
14392  {
14393  Oid oldId = lfirst_oid(oid_item);
14394  HeapTuple tup;
14395  Form_pg_constraint con;
14396  Oid relid;
14397  Oid confrelid;
14398  char contype;
14399  bool conislocal;
14400 
14401  tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14402  if (!HeapTupleIsValid(tup)) /* should not happen */
14403  elog(ERROR, "cache lookup failed for constraint %u", oldId);
14404  con = (Form_pg_constraint) GETSTRUCT(tup);
14405  if (OidIsValid(con->conrelid))
14406  relid = con->conrelid;
14407  else
14408  {
14409  /* must be a domain constraint */
14410  relid = get_typ_typrelid(getBaseType(con->contypid));
14411  if (!OidIsValid(relid))
14412  elog(ERROR, "could not identify relation associated with constraint %u", oldId);
14413  }
14414  confrelid = con->confrelid;
14415  contype = con->contype;
14416  conislocal = con->conislocal;
14417  ReleaseSysCache(tup);
14418 
14419  ObjectAddressSet(obj, ConstraintRelationId, oldId);
14420  add_exact_object_address(&obj, objects);
14421 
14422  /*
14423  * If the constraint is inherited (only), we don't want to inject a
14424  * new definition here; it'll get recreated when
14425  * ATAddCheckNNConstraint recurses from adding the parent table's
14426  * constraint. But we had to carry the info this far so that we can
14427  * drop the constraint below.
14428  */
14429  if (!conislocal)
14430  continue;
14431 
14432  /*
14433  * When rebuilding an FK constraint that references the table we're
14434  * modifying, we might not yet have any lock on the FK's table, so get
14435  * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14436  * step, so there's no value in asking for anything weaker.
14437  */
14438  if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
14440 
14441  ATPostAlterTypeParse(oldId, relid, confrelid,
14442  (char *) lfirst(def_item),
14443  wqueue, lockmode, tab->rewrite);
14444  }
14445  forboth(oid_item, tab->changedIndexOids,
14446  def_item, tab->changedIndexDefs)
14447  {
14448  Oid oldId = lfirst_oid(oid_item);
14449  Oid relid;
14450 
14451  relid = IndexGetRelation(oldId, false);
14452  ATPostAlterTypeParse(oldId, relid, InvalidOid,
14453  (char *) lfirst(def_item),
14454  wqueue, lockmode, tab->rewrite);
14455 
14456  ObjectAddressSet(obj, RelationRelationId, oldId);
14457  add_exact_object_address(&obj, objects);
14458  }
14459 
14460  /* add dependencies for new statistics */
14461  forboth(oid_item, tab->changedStatisticsOids,
14462  def_item, tab->changedStatisticsDefs)
14463  {
14464  Oid oldId = lfirst_oid(oid_item);
14465  Oid relid;
14466 
14467  relid = StatisticsGetRelation(oldId, false);
14468  ATPostAlterTypeParse(oldId, relid, InvalidOid,
14469  (char *) lfirst(def_item),
14470  wqueue, lockmode, tab->rewrite);
14471 
14472  ObjectAddressSet(obj, StatisticExtRelationId, oldId);
14473  add_exact_object_address(&obj, objects);
14474  }
14475 
14476  /*
14477  * Queue up command to restore replica identity index marking
14478  */
14479  if (tab->replicaIdentityIndex)
14480  {
14483 
14484  subcmd->identity_type = REPLICA_IDENTITY_INDEX;
14485  subcmd->name = tab->replicaIdentityIndex;
14486  cmd->subtype = AT_ReplicaIdentity;
14487  cmd->def = (Node *) subcmd;
14488 
14489  /* do it after indexes and constraints */
14490  tab->subcmds[AT_PASS_OLD_CONSTR] =
14491  lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14492  }
14493 
14494  /*
14495  * Queue up command to restore marking of index used for cluster.
14496  */
14497  if (tab->clusterOnIndex)
14498  {
14500 
14501  cmd->subtype = AT_ClusterOn;
14502  cmd->name = tab->clusterOnIndex;
14503 
14504  /* do it after indexes and constraints */
14505  tab->subcmds[AT_PASS_OLD_CONSTR] =
14506  lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14507  }
14508 
14509  /*
14510  * It should be okay to use DROP_RESTRICT here, since nothing else should
14511  * be depending on these objects.
14512  */
14514 
14515  free_object_addresses(objects);
14516 
14517  /*
14518  * The objects will get recreated during subsequent passes over the work
14519  * queue.
14520  */
14521 }
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3527
Oid get_typ_typrelid(Oid typid)
Definition: lsyscache.c:2731
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition: statscmds.c:898
List * changedConstraintDefs
Definition: tablecmds.c:201
List * changedStatisticsDefs
Definition: tablecmds.c:207
char * clusterOnIndex
Definition: tablecmds.c:205
char * replicaIdentityIndex
Definition: tablecmds.c:204
List * changedStatisticsOids
Definition: tablecmds.c:206
List * changedIndexDefs
Definition: tablecmds.c:203
List * changedIndexOids
Definition: tablecmds.c:202
List * changedConstraintOids
Definition: tablecmds.c:200
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
Definition: tablecmds.c:14532

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

14534 {
14535  List *raw_parsetree_list;
14536  List *querytree_list;
14537  ListCell *list_item;
14538  Relation rel;
14539 
14540  /*
14541  * We expect that we will get only ALTER TABLE and CREATE INDEX
14542  * statements. Hence, there is no need to pass them through
14543  * parse_analyze_*() or the rewriter, but instead we need to pass them
14544  * through parse_utilcmd.c to make them ready for execution.
14545  */
14546  raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
14547  querytree_list = NIL;
14548  foreach(list_item, raw_parsetree_list)
14549  {
14550  RawStmt *rs = lfirst_node(RawStmt, list_item);
14551  Node *stmt = rs->stmt;
14552 
14553  if (IsA(stmt, IndexStmt))
14554  querytree_list = lappend(querytree_list,
14555  transformIndexStmt(oldRelId,
14556  (IndexStmt *) stmt,
14557  cmd));
14558  else if (IsA(stmt, AlterTableStmt))
14559  {
14560  List *beforeStmts;
14561  List *afterStmts;
14562 
14563  stmt = (Node *) transformAlterTableStmt(oldRelId,
14564  (AlterTableStmt *) stmt,
14565  cmd,
14566  &beforeStmts,
14567  &afterStmts);
14568  querytree_list = list_concat(querytree_list, beforeStmts);
14569  querytree_list = lappend(querytree_list, stmt);
14570  querytree_list = list_concat(querytree_list, afterStmts);
14571  }
14572  else if (IsA(stmt, CreateStatsStmt))
14573  querytree_list = lappend(querytree_list,
14574  transformStatsStmt(oldRelId,
14575  (CreateStatsStmt *) stmt,
14576  cmd));
14577  else
14578  querytree_list = lappend(querytree_list, stmt);
14579  }
14580 
14581  /* Caller should already have acquired whatever lock we need. */
14582  rel = relation_open(oldRelId, NoLock);
14583 
14584  /*
14585  * Attach each generated command to the proper place in the work queue.
14586  * Note this could result in creation of entirely new work-queue entries.
14587  *
14588  * Also note that we have to tweak the command subtypes, because it turns
14589  * out that re-creation of indexes and constraints has to act a bit
14590  * differently from initial creation.
14591  */
14592  foreach(list_item, querytree_list)
14593  {
14594  Node *stm = (Node *) lfirst(list_item);
14595  AlteredTableInfo *tab;
14596 
14597  tab = ATGetQueueEntry(wqueue, rel);
14598 
14599  if (IsA(stm, IndexStmt))
14600  {
14601  IndexStmt *stmt = (IndexStmt *) stm;
14602  AlterTableCmd *newcmd;
14603 
14604  if (!rewrite)
14605  TryReuseIndex(oldId, stmt);
14606  stmt->reset_default_tblspc = true;
14607  /* keep the index's comment */
14608  stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14609 
14610  newcmd = makeNode(AlterTableCmd);
14611  newcmd->subtype = AT_ReAddIndex;
14612  newcmd->def = (Node *) stmt;
14613  tab->subcmds[AT_PASS_OLD_INDEX] =
14614  lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14615  }
14616  else if (IsA(stm, AlterTableStmt))
14617  {
14618  AlterTableStmt *stmt = (AlterTableStmt *) stm;
14619  ListCell *lcmd;
14620 
14621  foreach(lcmd, stmt->cmds)
14622  {
14623  AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
14624 
14625  if (cmd->subtype == AT_AddIndex)
14626  {
14627  IndexStmt *indstmt;
14628  Oid indoid;
14629 
14630  indstmt = castNode(IndexStmt, cmd->def);
14631  indoid = get_constraint_index(oldId);
14632 
14633  if (!rewrite)
14634  TryReuseIndex(indoid, indstmt);
14635  /* keep any comment on the index */
14636  indstmt->idxcomment = GetComment(indoid,
14637  RelationRelationId, 0);
14638  indstmt->reset_default_tblspc = true;
14639 
14640  cmd->subtype = AT_ReAddIndex;
14641  tab->subcmds[AT_PASS_OLD_INDEX] =
14642  lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
14643 
14644  /* recreate any comment on the constraint */
14647  oldId,
14648  rel,
14649  NIL,
14650  indstmt->idxname);
14651  }
14652  else if (cmd->subtype == AT_AddConstraint)
14653  {
14654  Constraint *con = castNode(Constraint, cmd->def);
14655 
14656  con->old_pktable_oid = refRelId;
14657  /* rewriting neither side of a FK */
14658  if (con->contype == CONSTR_FOREIGN &&
14659  !rewrite && tab->rewrite == 0)
14660  TryReuseForeignKey(oldId, con);
14661  con->reset_default_tblspc = true;
14662  cmd->subtype = AT_ReAddConstraint;
14663  tab->subcmds[AT_PASS_OLD_CONSTR] =
14664  lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14665 
14666  /* recreate any comment on the constraint */
14669  oldId,
14670  rel,
14671  NIL,
14672  con->conname);
14673  }
14674  else if (cmd->subtype == AT_SetAttNotNull)
14675  {
14676  /*
14677  * We see this subtype when a primary key is created
14678  * internally, for example when it is replaced with a new
14679  * constraint (say because one of the columns changes
14680  * type); in this case we need to reinstate attnotnull,
14681  * because it was removed because of the drop of the old
14682  * PK. Schedule this subcommand to an upcoming AT pass.
14683  */
14684  cmd->subtype = AT_SetAttNotNull;
14687  }
14688  else
14689  elog(ERROR, "unexpected statement subtype: %d",
14690  (int) cmd->subtype);
14691  }
14692  }
14693  else if (IsA(stm, AlterDomainStmt))
14694  {
14696 
14697  if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14698  {
14699  Constraint *con = castNode(Constraint, stmt->def);
14701 
14703  cmd->def = (Node *) stmt;
14704  tab->subcmds[AT_PASS_OLD_CONSTR] =
14705  lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14706 
14707  /* recreate any comment on the constraint */
14710  oldId,
14711  NULL,
14712  stmt->typeName,
14713  con->conname);
14714  }
14715  else
14716  elog(ERROR, "unexpected statement subtype: %d",
14717  (int) stmt->subtype);
14718  }
14719  else if (IsA(stm, CreateStatsStmt))
14720  {
14722  AlterTableCmd *newcmd;
14723 
14724  /* keep the statistics object's comment */
14725  stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14726 
14727  newcmd = makeNode(AlterTableCmd);
14728  newcmd->subtype = AT_ReAddStatistics;
14729  newcmd->def = (Node *) stmt;
14730  tab->subcmds[AT_PASS_MISC] =
14731  lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14732  }
14733  else
14734  elog(ERROR, "unexpected statement type: %d",
14735  (int) nodeTag(stm));
14736  }
14737 
14738  relation_close(rel, NoLock);
14739 }
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:1113
#define nodeTag(nodeptr)
Definition: nodes.h:133
CreateStatsStmt * transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
@ RAW_PARSE_DEFAULT
Definition: parser.h:39
bool reset_default_tblspc
Definition: parsenodes.h:2764
bool reset_default_tblspc
Definition: parsenodes.h:3388
char * idxname
Definition: parsenodes.h:3362
char * idxcomment
Definition: parsenodes.h:3372
Node * stmt
Definition: parsenodes.h:2027
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:14796
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:14825
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:14752

References AT_AddConstraint, AT_AddIndex, AT_PASS_MISC, AT_PASS_OLD_COL_ATTRS, AT_PASS_OLD_CONSTR, AT_PASS_OLD_INDEX, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReAddIndex, AT_ReAddStatistics, AT_SetAttNotNull, ATGetQueueEntry(), castNode, Constraint::conname, CONSTR_FOREIGN, 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 7057 of file tablecmds.c.

7060 {
7061  if (rel->rd_rel->reloftype && !recursing)
7062  ereport(ERROR,
7063  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7064  errmsg("cannot add column to typed table")));
7065 
7066  if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7067  ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7068 
7069  if (recurse && !is_view)
7070  cmd->recurse = true;
7071 }
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6762

References ATTypedTableRecursion(), context, ereport, errcode(), errmsg(), ERROR, RelationData::rd_rel, and AlterTableCmd::recurse.

Referenced by ATPrepCmd().

◆ ATPrepAddInherit()

static void ATPrepAddInherit ( Relation  child_rel)
static

Definition at line 16144 of file tablecmds.c.

16145 {
16146  if (child_rel->rd_rel->reloftype)
16147  ereport(ERROR,
16148  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16149  errmsg("cannot change inheritance of typed table")));
16150 
16151  if (child_rel->rd_rel->relispartition)
16152  ereport(ERROR,
16153  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16154  errmsg("cannot change inheritance of a partition")));
16155 
16156  if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16157  ereport(ERROR,
16158  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16159  errmsg("cannot change inheritance of partitioned table")));
16160 }

References ereport, errcode(), errmsg(), ERROR, and RelationData::rd_rel.

Referenced by ATPrepCmd().

◆ ATPrepAddPrimaryKey()

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

Definition at line 9352 of file tablecmds.c.

9354 {
9355  List *children;
9356  List *newconstrs = NIL;
9357  IndexStmt *indexstmt;
9358 
9359  /* No work if not creating a primary key */
9360  if (!IsA(cmd->def, IndexStmt))
9361  return;
9362  indexstmt = castNode(IndexStmt, cmd->def);
9363  if (!indexstmt->primary)
9364  return;
9365 
9366  /* No work if no legacy inheritance children are present */
9367  if (rel->rd_rel->relkind != RELKIND_RELATION ||
9368  !rel->rd_rel->relhassubclass)
9369  return;
9370 
9371  /*
9372  * Acquire locks all the way down the hierarchy. The recursion to lower
9373  * levels occurs at execution time as necessary, so we don't need to do it
9374  * here, and we don't need the returned list either.
9375  */
9376  (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
9377 
9378  /*
9379  * Construct the list of constraints that we need to add to each child
9380  * relation.
9381  */
9382  foreach_node(IndexElem, elem, indexstmt->indexParams)
9383  {
9384  Constraint *nnconstr;
9385 
9386  Assert(elem->expr == NULL);
9387 
9388  nnconstr = makeNode(Constraint);
9389  nnconstr->contype = CONSTR_NOTNULL;
9390  nnconstr->conname = NULL; /* XXX use PK name? */
9391  nnconstr->inhcount = 1;
9392  nnconstr->deferrable = false;
9393  nnconstr->initdeferred = false;
9394  nnconstr->location = -1;
9395  nnconstr->keys = list_make1(makeString(elem->name));
9396  nnconstr->skip_validation = false;
9397  nnconstr->initially_valid = true;
9398 
9399  newconstrs = lappend(newconstrs, nnconstr);
9400  }
9401 
9402  /* Finally, add AT subcommands to add each constraint to each child. */
9404  foreach_oid(childrelid, children)
9405  {
9406  Relation childrel = table_open(childrelid, NoLock);
9408  ListCell *lc2;
9409 
9410  newcmd->subtype = AT_AddConstraint;
9411  newcmd->recurse = true;
9412 
9413  foreach(lc2, newconstrs)
9414  {
9415  /* ATPrepCmd copies newcmd, so we can scribble on it here */
9416  newcmd->def = lfirst(lc2);
9417 
9418  ATPrepCmd(wqueue, childrel, newcmd,
9419  true, false, lockmode, context);
9420  }
9421 
9422  table_close(childrel, NoLock);
9423  }
9424 }
#define foreach_node(type, var, lst)
Definition: pg_list.h:496
#define foreach_oid(var, lst)
Definition: pg_list.h:471
List * indexParams
Definition: parsenodes.h:3366
bool primary
Definition: parsenodes.h:3380

References Assert, AT_AddConstraint, ATPrepCmd(), castNode, Constraint::conname, CONSTR_NOTNULL, context, Constraint::contype, AlterTableCmd::def, Constraint::deferrable, find_all_inheritors(), find_inheritance_children(), foreach_node, foreach_oid, IndexStmt::indexParams, Constraint::inhcount, Constraint::initdeferred, Constraint::initially_valid, IsA, Constraint::keys, lappend(), lfirst, list_make1, Constraint::location, makeNode, makeString(), NIL, NoLock, IndexStmt::primary, RelationData::rd_rel, AlterTableCmd::recurse, RelationGetRelid, Constraint::skip_validation, AlterTableCmd::subtype, table_close(), and table_open().

Referenced by ATParseTransformCmd().

◆ ATPrepAlterColumnType()

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

Definition at line 13381 of file tablecmds.c.

13386 {
13387  char *colName = cmd->name;
13388  ColumnDef *def = (ColumnDef *) cmd->def;
13389  TypeName *typeName = def->typeName;
13390  Node *transform = def->cooked_default;
13391  HeapTuple tuple;
13392  Form_pg_attribute attTup;
13394  Oid targettype;
13395  int32 targettypmod;
13396  Oid targetcollid;
13398  ParseState *pstate = make_parsestate(NULL);
13399  AclResult aclresult;
13400  bool is_expr;
13401 
13402  if (rel->rd_rel->reloftype && !recursing)
13403  ereport(ERROR,
13404  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13405  errmsg("cannot alter column type of typed table")));
13406 
13407  /* lookup the attribute so we can check inheritance status */
13408  tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13409  if (!HeapTupleIsValid(tuple))
13410  ereport(ERROR,
13411  (errcode(ERRCODE_UNDEFINED_COLUMN),
13412  errmsg("column \"%s\" of relation \"%s\" does not exist",
13413  colName, RelationGetRelationName(rel))));
13414  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
13415  attnum = attTup->attnum;
13416 
13417  /* Can't alter a system attribute */
13418  if (attnum <= 0)
13419  ereport(ERROR,
13420  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13421  errmsg("cannot alter system column \"%s\"",
13422  colName)));
13423 
13424  /*
13425  * Don't alter inherited columns. At outer level, there had better not be
13426  * any inherited definition; when recursing, we assume this was checked at
13427  * the parent level (see below).
13428  */
13429  if (attTup->attinhcount > 0 && !recursing)
13430  ereport(ERROR,
13431  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13432  errmsg("cannot alter inherited column \"%s\"",
13433  colName)));
13434 
13435  /* Don't alter columns used in the partition key */
13436  if (has_partition_attrs(rel,
13438  &is_expr))
13439  ereport(ERROR,
13440  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13441  errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13442  colName, RelationGetRelationName(rel))));
13443 
13444  /* Look up the target type */
13445  typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
13446 
13447  aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
13448  if (aclresult != ACLCHECK_OK)
13449  aclcheck_error_type(aclresult, targettype);
13450 
13451  /* And the collation */
13452  targetcollid = GetColumnDefCollation(NULL, def, targettype);
13453 
13454  /* make sure datatype is legal for a column */
13455  CheckAttributeType(colName, targettype, targetcollid,
13456  list_make1_oid(rel->rd_rel->reltype),
13457  0);
13458 
13459  if (tab->relkind == RELKIND_RELATION ||
13460  tab->relkind == RELKIND_PARTITIONED_TABLE)
13461  {
13462  /*
13463  * Set up an expression to transform the old data value to the new
13464  * type. If a USING option was given, use the expression as
13465  * transformed by transformAlterTableStmt, else just take the old
13466  * value and try to coerce it. We do this first so that type
13467  * incompatibility can be detected before we waste effort, and because
13468  * we need the expression to be parsed against the original table row
13469  * type.
13470  */
13471  if (!transform)
13472  {
13473  transform = (Node *) makeVar(1, attnum,
13474  attTup->atttypid, attTup->atttypmod,
13475  attTup->attcollation,
13476  0);
13477  }
13478 
13479  transform = coerce_to_target_type(pstate,
13480  transform, exprType(transform),
13481  targettype, targettypmod,
13484  -1);
13485  if (transform == NULL)
13486  {
13487  /* error text depends on whether USING was specified or not */
13488  if (def->cooked_default != NULL)
13489  ereport(ERROR,
13490  (errcode(ERRCODE_DATATYPE_MISMATCH),
13491  errmsg("result of USING clause for column \"%s\""
13492  " cannot be cast automatically to type %s",
13493  colName, format_type_be(targettype)),
13494  errhint("You might need to add an explicit cast.")));
13495  else
13496  ereport(ERROR,
13497  (errcode(ERRCODE_DATATYPE_MISMATCH),
13498  errmsg("column \"%s\" cannot be cast automatically to type %s",
13499  colName, format_type_be(targettype)),
13500  /* translator: USING is SQL, don't translate it */
13501  errhint("You might need to specify \"USING %s::%s\".",
13502  quote_identifier(colName),
13503  format_type_with_typemod(targettype,
13504  targettypmod))));
13505  }
13506 
13507  /* Fix collations after all else */
13508  assign_expr_collations(pstate, transform);
13509 
13510  /* Plan the expr now so we can accurately assess the need to rewrite. */
13511  transform = (Node *) expression_planner((Expr *) transform);
13512 
13513  /*
13514  * Add a work queue item to make ATRewriteTable update the column
13515  * contents.
13516  */
13518  newval->attnum = attnum;
13519  newval->expr = (Expr *) transform;
13520  newval->is_generated = false;
13521 
13522  tab->newvals = lappend(tab->newvals, newval);
13523  if (ATColumnChangeRequiresRewrite(transform, attnum))
13525  }
13526  else if (transform)
13527  ereport(ERROR,
13528  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13529  errmsg("\"%s\" is not a table",
13530  RelationGetRelationName(rel))));
13531 
13532  if (!RELKIND_HAS_STORAGE(tab->relkind))
13533  {
13534  /*
13535  * For relations without storage, do this check now. Regular tables
13536  * will check it later when the table is being rewritten.
13537  */
13538  find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13539  }
13540 
13541  ReleaseSysCache(tuple);
13542 
13543  /*
13544  * Recurse manually by queueing a new command for each child, if
13545  * necessary. We cannot apply ATSimpleRecursion here because we need to
13546  * remap attribute numbers in the USING expression, if any.
13547  *
13548  * If we are told not to recurse, there had better not be any child
13549  * tables; else the alter would put them out of step.
13550  */
13551  if (recurse)
13552  {
13553  Oid relid = RelationGetRelid(rel);
13554  List *child_oids,
13555  *child_numparents;
13556  ListCell *lo,
13557  *li;
13558 
13559  child_oids = find_all_inheritors(relid, lockmode,
13560  &child_numparents);
13561 
13562  /*
13563  * find_all_inheritors does the recursive search of the inheritance
13564  * hierarchy, so all we have to do is process all of the relids in the
13565  * list that it returns.
13566  */
13567  forboth(lo, child_oids, li, child_numparents)
13568  {
13569  Oid childrelid = lfirst_oid(lo);
13570  int numparents = lfirst_int(li);
13571  Relation childrel;
13572  HeapTuple childtuple;
13573  Form_pg_attribute childattTup;
13574 
13575  if (childrelid == relid)
13576  continue;
13577 
13578  /* find_all_inheritors already got lock */
13579  childrel = relation_open(childrelid, NoLock);
13580  CheckTableNotInUse(childrel, "ALTER TABLE");
13581 
13582  /*
13583  * Verify that the child doesn't have any inherited definitions of
13584  * this column that came from outside this inheritance hierarchy.
13585  * (renameatt makes a similar test, though in a different way
13586  * because of its different recursion mechanism.)
13587  */
13588  childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
13589  colName);
13590  if (!HeapTupleIsValid(childtuple))
13591  ereport(ERROR,
13592  (errcode(ERRCODE_UNDEFINED_COLUMN),
13593  errmsg("column \"%s\" of relation \"%s\" does not exist",
13594  colName, RelationGetRelationName(childrel))));
13595  childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
13596 
13597  if (childattTup->attinhcount > numparents)
13598  ereport(ERROR,
13599  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13600  errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13601  colName, RelationGetRelationName(childrel))));
13602 
13603  ReleaseSysCache(childtuple);
13604 
13605  /*
13606  * Remap the attribute numbers. If no USING expression was
13607  * specified, there is no need for this step.
13608  */
13609  if (def->cooked_default)
13610  {
13611  AttrMap *attmap;
13612  bool found_whole_row;
13613 
13614  /* create a copy to scribble on */
13615  cmd = copyObject(cmd);
13616 
13617  attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13618  RelationGetDescr(rel),
13619  false);
13620  ((ColumnDef *) cmd->def)->cooked_default =
13622  1, 0,
13623  attmap,
13624  InvalidOid, &found_whole_row);
13625  if (found_whole_row)
13626  ereport(ERROR,
13627  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13628  errmsg("cannot convert whole-row table reference"),
13629  errdetail("USING expression contains a whole-row table reference.")));
13630  pfree(attmap);
13631  }
13632  ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
13633  relation_close(childrel, NoLock);
13634  }
13635  }
13636  else if (!recursing &&
13638  ereport(ERROR,
13639  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13640  errmsg("type of inherited column \"%s\" must be changed in child tables too",
13641  colName)));
13642 
13643  if (tab->relkind == RELKIND_COMPOSITE_TYPE)
13644  ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
13645 }
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition: aclchk.c:3007
#define AT_REWRITE_COLUMN_REWRITE
Definition: event_trigger.h:36
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)
#define ACL_USAGE
Definition: parsenodes.h:84
#define lfirst_int(lc)
Definition: pg_list.h:173
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:12623
Node * cooked_default
Definition: parsenodes.h:736
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
Definition: tablecmds.c:13662
void find_composite_type_dependencies(Oid typeOid, Relation origRelation, const char *origTypeName)
Definition: tablecmds.c:6807

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(), CheckAttributeType(), CheckTableNotInUse(), COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, context, ColumnDef::cooked_default, copyObject, AlterTableCmd::def, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, expression_planner(), exprType(), find_all_inheritors(), find_composite_type_dependencies(), find_inheritance_children(), FirstLowInvalidHeapAttributeNumber, forboth, format_type_be(), format_type_with_typemod(), GetColumnDefCollation(), GETSTRUCT, GetUserId(), has_partition_attrs(), HeapTupleIsValid, InvalidOid, lappend(), lfirst_int, lfirst_oid, list_make1_oid, make_parsestate(), makeVar(), map_variable_attnos(), AlterTableCmd::name, newval, AlteredTableInfo::newvals, NIL, NoLock, object_aclcheck(), palloc0(), pfree(), 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 bool ATPrepChangePersistence ( Relation  rel,
bool  toLogged 
)
static

Definition at line 17757 of file tablecmds.c.

17758 {
17759  Relation pg_constraint;
17760  HeapTuple tuple;
17761  SysScanDesc scan;
17762  ScanKeyData skey[1];
17763 
17764  /*
17765  * Disallow changing status for a temp table. Also verify whether we can
17766  * get away with doing nothing; in such cases we don't need to run the
17767  * checks below, either.
17768  */
17769  switch (rel->rd_rel->relpersistence)
17770  {
17771  case RELPERSISTENCE_TEMP:
17772  ereport(ERROR,
17773  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17774  errmsg("cannot change logged status of table \"%s\" because it is temporary",
17776  errtable(rel)));
17777  break;
17778  case RELPERSISTENCE_PERMANENT:
17779  if (toLogged)
17780  /* nothing to do */
17781  return false;
17782  break;
17783  case RELPERSISTENCE_UNLOGGED:
17784  if (!toLogged)
17785  /* nothing to do */
17786  return false;
17787  break;
17788  }
17789 
17790  /*
17791  * Check that the table is not part of any publication when changing to
17792  * UNLOGGED, as UNLOGGED tables can't be published.
17793  */
17794  if (!toLogged &&
17796  ereport(ERROR,
17797  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17798  errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17800  errdetail("Unlogged relations cannot be replicated.")));
17801 
17802  /*
17803  * Check existing foreign key constraints to preserve the invariant that
17804  * permanent tables cannot reference unlogged ones. Self-referencing
17805  * foreign keys can safely be ignored.
17806  */
17807  pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17808 
17809  /*
17810  * Scan conrelid if changing to permanent, else confrelid. This also
17811  * determines whether a useful index exists.
17812  */
17813  ScanKeyInit(&skey[0],
17814  toLogged ? Anum_pg_constraint_conrelid :
17815  Anum_pg_constraint_confrelid,
17816  BTEqualStrategyNumber, F_OIDEQ,
17818  scan = systable_beginscan(pg_constraint,
17819  toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
17820  true, NULL, 1, skey);
17821 
17822  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17823  {
17825 
17826  if (con->contype == CONSTRAINT_FOREIGN)
17827  {
17828  Oid foreignrelid;
17829  Relation foreignrel;
17830 
17831  /* the opposite end of what we used as scankey */
17832  foreignrelid = toLogged ? con->confrelid : con->conrelid;
17833 
17834  /* ignore if self-referencing */
17835  if (RelationGetRelid(rel) == foreignrelid)
17836  continue;
17837 
17838  foreignrel = relation_open(foreignrelid, AccessShareLock);
17839 
17840  if (toLogged)
17841  {
17842  if (!RelationIsPermanent(foreignrel))
17843  ereport(ERROR,
17844  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17845  errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17847  RelationGetRelationName(foreignrel)),
17848  errtableconstraint(rel, NameStr(con->conname))));
17849  }
17850  else
17851  {
17852  if (RelationIsPermanent(foreignrel))
17853  ereport(ERROR,
17854  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17855  errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17857  RelationGetRelationName(foreignrel)),
17858  errtableconstraint(rel, NameStr(con->conname))));
17859  }
17860 
17861  relation_close(foreignrel, AccessShareLock);
17862  }
17863  }
17864 
17865  systable_endscan(scan);
17866 
17867  table_close(pg_constraint, AccessShareLock);
17868 
17869  return true;
17870 }
List * GetRelationPublications(Oid relid)
int errtableconstraint(Relation rel, const char *conname)
Definition: relcache.c:5999
int errtable(Relation rel)
Definition: relcache.c:5945

References AccessShareLock, BTEqualStrategyNumber, ereport, errcode(), errdetail(), errmsg(), ERROR, errtable(), errtableconstraint(), GetRelationPublications(), GETSTRUCT, HeapTupleIsValid, InvalidOid, NameStr, NIL, ObjectIdGetDatum(), RelationData::rd_rel, relation_close(), relation_open(), RelationGetRelationName, RelationGetRelid, RelationIsPermanent, 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 4813 of file tablecmds.c.

4816 {
4817  AlteredTableInfo *tab;
4819 
4820  /* Find or create work queue entry for this table */
4821  tab = ATGetQueueEntry(wqueue, rel);
4822 
4823  /*
4824  * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4825  * partitions that are pending detach.
4826  */
4827  if (rel->rd_rel->relispartition &&
4830  ereport(ERROR,
4831  errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4832  errmsg("cannot alter partition \"%s\" with an incomplete detach",
4834  errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4835 
4836  /*
4837  * Copy the original subcommand for each table, so we can scribble on it.
4838  * This avoids conflicts when different child tables need to make
4839  * different parse transformations (for example, the same column may have
4840  * different column numbers in different children).
4841  */
4842  cmd = copyObject(cmd);
4843 
4844  /*
4845  * Do permissions and relkind checking, recursion to child tables if
4846  * needed, and any additional phase-1 processing needed. (But beware of
4847  * adding any processing that looks at table details that another
4848  * subcommand could change. In some cases we reject multiple subcommands
4849  * that could try to change the same state in contrary ways.)
4850  */
4851  switch (cmd->subtype)
4852  {
4853  case AT_AddColumn: /* ADD COLUMN */
4854  ATSimplePermissions(cmd->subtype, rel,
4856  ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4857  lockmode, context);
4858  /* Recursion occurs during execution phase */
4859  pass = AT_PASS_ADD_COL;
4860  break;
4861  case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4862  ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4863  ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4864  lockmode, context);
4865  /* Recursion occurs during execution phase */
4866  pass = AT_PASS_ADD_COL;
4867  break;
4868  case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4869 
4870  /*
4871  * We allow defaults on views so that INSERT into a view can have
4872  * default-ish behavior. This works because the rewriter
4873  * substitutes default values into INSERTs before it expands
4874  * rules.
4875  */
4877  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4878  /* No command-specific prep needed */
4879  pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4880  break;
4881  case AT_CookedColumnDefault: /* add a pre-cooked default */
4882  /* This is currently used only in CREATE TABLE */
4883  /* (so the permission check really isn't necessary) */
4885  /* This command never recurses */
4886  pass = AT_PASS_ADD_OTHERCONSTR;
4887  break;
4888  case AT_AddIdentity:
4890  /* Set up recursion for phase 2; no other prep needed */
4891  if (recurse)
4892  cmd->recurse = true;
4893  pass = AT_PASS_ADD_OTHERCONSTR;
4894  break;
4895  case AT_SetIdentity:
4897  /* Set up recursion for phase 2; no other prep needed */
4898  if (recurse)
4899  cmd->recurse = true;
4900  /* This should run after AddIdentity, so do it in MISC pass */
4901  pass = AT_PASS_MISC;
4902  break;
4903  case AT_DropIdentity:
4905  /* Set up recursion for phase 2; no other prep needed */
4906  if (recurse)
4907  cmd->recurse = true;
4908  pass = AT_PASS_DROP;
4909  break;
4910  case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4912  /* Set up recursion for phase 2; no other prep needed */
4913  if (recurse)
4914  cmd->recurse = true;
4915  pass = AT_PASS_DROP;
4916  break;
4917  case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4919  /* Set up recursion for phase 2; no other prep needed */
4920  if (recurse)
4921  cmd->recurse = true;
4922  pass = AT_PASS_COL_ATTRS;
4923  break;
4924  case AT_SetAttNotNull: /* set pg_attribute.attnotnull without adding
4925  * a constraint */
4927  /* Need command-specific recursion decision */
4928  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4929  pass = AT_PASS_COL_ATTRS;
4930  break;
4931  case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4933  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4934  pass = AT_PASS_SET_EXPRESSION;
4935  break;
4936  case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4938  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4939  ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4940  pass = AT_PASS_DROP;
4941  break;
4942  case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4944  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4945  /* No command-specific prep needed */
4946  pass = AT_PASS_MISC;
4947  break;
4948  case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4949  case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4951  /* This command never recurses */
4952  pass = AT_PASS_MISC;
4953  break;
4954  case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4956  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4957  /* No command-specific prep needed */
4958  pass = AT_PASS_MISC;
4959  break;
4960  case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4962  /* This command never recurses */
4963  /* No command-specific prep needed */
4964  pass = AT_PASS_MISC;
4965  break;
4966  case AT_DropColumn: /* DROP COLUMN */
4967  ATSimplePermissions(cmd->subtype, rel,
4969  ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4970  lockmode, context);
4971  /* Recursion occurs during execution phase */
4972  pass = AT_PASS_DROP;
4973  break;
4974  case AT_AddIndex: /* ADD INDEX */
4976  /* This command never recurses */
4977  /* No command-specific prep needed */
4978  pass = AT_PASS_ADD_INDEX;
4979  break;
4980  case AT_AddConstraint: /* ADD CONSTRAINT */
4982  /* Recursion occurs during execution phase */
4983  /* No command-specific prep needed except saving recurse flag */
4984  if (recurse)
4985  cmd->recurse = true;
4986  pass = AT_PASS_ADD_CONSTR;
4987  break;
4988  case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4990  /* This command never recurses */
4991  /* No command-specific prep needed */
4992  pass = AT_PASS_ADD_INDEXCONSTR;
4993  break;
4994  case AT_DropConstraint: /* DROP CONSTRAINT */
4996  ATCheckPartitionsNotInUse(rel, lockmode);
4997  /* Other recursion occurs during execution phase */
4998  /* No command-specific prep needed except saving recurse flag */
4999  if (recurse)
5000  cmd->recurse = true;
5001  pass = AT_PASS_DROP;
5002  break;
5003  case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5004  ATSimplePermissions(cmd->subtype, rel,
5006  /* See comments for ATPrepAlterColumnType */
5007  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5009  Assert(cmd != NULL);
5010  /* Performs own recursion */
5011  ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5012  lockmode, context);
5013  pass = AT_PASS_ALTER_TYPE;
5014  break;
5017  /* This command never recurses */
5018  /* No command-specific prep needed */
5019  pass = AT_PASS_MISC;
5020  break;
5021  case AT_ChangeOwner: /* ALTER OWNER */
5022  /* This command never recurses */
5023  /* No command-specific prep needed */
5024  pass = AT_PASS_MISC;
5025  break;
5026  case AT_ClusterOn: /* CLUSTER ON */
5027  case AT_DropCluster: /* SET WITHOUT CLUSTER */
5029  /* These commands never recurse */
5030  /* No command-specific prep needed */
5031  pass = AT_PASS_MISC;
5032  break;
5033  case AT_SetLogged: /* SET LOGGED */
5035  if (tab->chgPersistence)
5036  ereport(ERROR,
5037  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5038  errmsg("cannot change persistence setting twice")));
5039  tab->chgPersistence = ATPrepChangePersistence(rel, true);
5040  /* force rewrite if necessary; see comment in ATRewriteTables */
5041  if (tab->chgPersistence)
5042  {
5044  tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
5045  }
5046  pass = AT_PASS_MISC;
5047  break;
5048  case AT_SetUnLogged: /* SET UNLOGGED */
5050  if (tab->chgPersistence)
5051  ereport(ERROR,
5052  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5053  errmsg("cannot change persistence setting twice")));
5054  tab->chgPersistence = ATPrepChangePersistence(rel, false);
5055  /* force rewrite if necessary; see comment in ATRewriteTables */
5056  if (tab->chgPersistence)
5057  {
5059  tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
5060  }
5061  pass = AT_PASS_MISC;
5062  break;
5063  case AT_DropOids: /* SET WITHOUT OIDS */
5065  pass = AT_PASS_DROP;
5066  break;
5067  case AT_SetAccessMethod: /* SET ACCESS METHOD */
5069 
5070  /* check if another access method change was already requested */
5071  if (tab->chgAccessMethod)
5072  ereport(ERROR,
5073  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5074  errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5075 
5076  ATPrepSetAccessMethod(tab, rel, cmd->name);
5077  pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5078  break;
5079  case AT_SetTableSpace: /* SET TABLESPACE */
5082  /* This command never recurses */
5083  ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5084  pass = AT_PASS_MISC; /* doesn't actually matter */
5085  break;
5086  case AT_SetRelOptions: /* SET (...) */
5087  case AT_ResetRelOptions: /* RESET (...) */
5088  case AT_ReplaceRelOptions: /* reset them all, then set just these */
5090  /* This command never recurses */
5091  /* No command-specific prep needed */
5092  pass = AT_PASS_MISC;
5093  break;
5094  case AT_AddInherit: /* INHERIT */
5096  /* This command never recurses */
5097  ATPrepAddInherit(rel);
5098  pass = AT_PASS_MISC;
5099  break;
5100  case AT_DropInherit: /* NO INHERIT */
5102  /* This command never recurses */
5103  /* No command-specific prep needed */
5104  pass = AT_PASS_MISC;
5105  break;
5106  case AT_AlterConstraint: /* ALTER CONSTRAINT */
5108  /* Recursion occurs during execution phase */
5109  pass = AT_PASS_MISC;
5110  break;
5111  case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5113  /* Recursion occurs during execution phase */
5114  /* No command-specific prep needed except saving recurse flag */
5115  if (recurse)
5116  cmd->recurse = true;
5117  pass = AT_PASS_MISC;
5118  break;
5119  case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5121  pass = AT_PASS_MISC;
5122  /* This command never recurses */
5123  /* No command-specific prep needed */
5124  break;
5125  case AT_EnableTrig: /* ENABLE TRIGGER variants */
5126  case AT_EnableAlwaysTrig:
5127  case AT_EnableReplicaTrig:
5128  case AT_EnableTrigAll:
5129  case AT_EnableTrigUser:
5130  case AT_DisableTrig: /* DISABLE TRIGGER variants */
5131  case AT_DisableTrigAll:
5132  case AT_DisableTrigUser:
5134  /* Set up recursion for phase 2; no other prep needed */
5135  if (recurse)
5136  cmd->recurse = true;
5137  pass = AT_PASS_MISC;
5138  break;
5139  case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5140  case AT_EnableAlwaysRule:
5141  case AT_EnableReplicaRule:
5142  case AT_DisableRule:
5143  case AT_AddOf: /* OF */
5144  case AT_DropOf: /* NOT OF */
5145  case AT_EnableRowSecurity:
5146  case AT_DisableRowSecurity:
5147  case AT_ForceRowSecurity:
5148  case AT_NoForceRowSecurity:
5150  /* These commands never recurse */
5151  /* No command-specific prep needed */
5152  pass = AT_PASS_MISC;
5153  break;
5154  case AT_GenericOptions:
5156  /* No command-specific prep needed */
5157  pass = AT_PASS_MISC;
5158  break;
5159  case AT_AttachPartition:
5161  /* No command-specific prep needed */
5162  pass = AT_PASS_MISC;
5163  break;
5164  case AT_DetachPartition:
5166  /* No command-specific prep needed */
5167  pass = AT_PASS_MISC;
5168  break;
5171  /* No command-specific prep needed */
5172  pass = AT_PASS_MISC;
5173  break;
5174  case AT_SplitPartition:
5176  /* No command-specific prep needed */
5177  pass = AT_PASS_MISC;
5178  break;
5179  case AT_MergePartitions:
5181  /* No command-specific prep needed */
5182  pass = AT_PASS_MISC;
5183  break;
5184  default: /* oops */
5185  elog(ERROR, "unrecognized alter table type: %d",
5186  (int) cmd->subtype);
5187  pass = AT_PASS_UNSET; /* keep compiler quiet */
5188  break;
5189  }
5190  Assert(pass > AT_PASS_UNSET);
5191 
5192  /* Add the subcommand to the appropriate list for phase 2 */
5193  tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5194 }
#define AT_REWRITE_ALTER_PERSISTENCE
Definition: event_trigger.h:34
bool PartitionHasPendingDetach(Oid partoid)
Definition: pg_inherits.c:620
#define ATT_SEQUENCE
Definition: tablecmds.c:333
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
Definition: tablecmds.c:15525
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:6732
#define ATT_INDEX
Definition: tablecmds.c:329
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:8651
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9128
static bool ATPrepChangePersistence(Relation rel, bool toLogged)
Definition: tablecmds.c:17757
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:7057
#define ATT_PARTITIONED_INDEX
Definition: tablecmds.c:332
#define ATT_VIEW
Definition: tablecmds.c:327
static void ATPrepAddInherit(Relation child_rel)
Definition: tablecmds.c:16144
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:13381
#define ATT_COMPOSITE_TYPE
Definition: tablecmds.c:330
#define ATT_MATVIEW
Definition: tablecmds.c:328
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
Definition: tablecmds.c:15401

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_REWRITE_ALTER_PERSISTENCE, AT_SetAccessMethod, AT_SetAttNotNull, 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(), ATPrepAlterColumnType(), ATPrepChangePersistence(), ATPrepDropColumn(), ATPrepDropExpression(), ATPrepSetAccessMethod(), ATPrepSetTableSpace(), ATSimplePermissions(), ATSimpleRecursion(), ATT_COMPOSITE_TYPE, ATT_FOREIGN_TABLE, ATT_INDEX, ATT_MATVIEW, ATT_PARTITIONED_INDEX, ATT_SEQUENCE, ATT_TABLE, ATT_VIEW, AlteredTableInfo::chgAccessMethod, AlteredTableInfo::chgPersistence, context, copyObject, AlterTableCmd::def, elog, ereport, errcode(), errhint(), errmsg(), ERROR, lappend(), AlterTableCmd::name, AlteredTableInfo::newrelpersistence, PartitionHasPendingDetach(), RelationData::rd_rel, AlterTableCmd::recurse, RelationGetRelationName, RelationGetRelid, AlteredTableInfo::rewrite, 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 9128 of file tablecmds.c.

9131 {
9132  if (rel->rd_rel->reloftype && !recursing)
9133  ereport(ERROR,
9134  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9135  errmsg("cannot drop column from typed table")));
9136 
9137  if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9138  ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9139 
9140  if (recurse)
9141  cmd->recurse = true;
9142 }

References ATTypedTableRecursion(), context, 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 8651 of file tablecmds.c.

8652 {
8653  /*
8654  * Reject ONLY if there are child tables. We could implement this, but it
8655  * is a bit complicated. GENERATED clauses must be attached to the column
8656  * definition and cannot be added later like DEFAULT, so if a child table
8657  * has a generation expression that the parent does not have, the child
8658  * column will necessarily be an attislocal column. So to implement ONLY
8659  * here, we'd need extra code to update attislocal of the direct child
8660  * tables, somewhat similar to how DROP COLUMN does it, so that the
8661  * resulting state can be properly dumped and restored.
8662  */
8663  if (!recurse &&
8665  ereport(ERROR,
8666  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8667  errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8668 
8669  /*
8670  * Cannot drop generation expression from inherited columns.
8671  */
8672  if (!recursing)
8673  {
8674  HeapTuple tuple;
8675  Form_pg_attribute attTup;
8676 
8677  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8678  if (!HeapTupleIsValid(tuple))
8679  ereport(ERROR,
8680  (errcode(ERRCODE_UNDEFINED_COLUMN),
8681  errmsg("column \"%s\" of relation \"%s\" does not exist",
8682  cmd->name, RelationGetRelationName(rel))));
8683 
8684  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8685 
8686  if (attTup->attinhcount > 0)
8687  ereport(ERROR,
8688  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8689  errmsg("cannot drop generation expression from inherited column")));
8690  }
8691 }

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

15402 {
15403  Oid amoid;
15404 
15405  /*
15406  * Look up the access method name and check that it differs from the
15407  * table's current AM. If DEFAULT was specified for a partitioned table
15408  * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15409  */
15410  if (amname != NULL)
15411  amoid = get_table_am_oid(amname, false);
15412  else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15413  amoid = InvalidOid;
15414  else
15416 
15417  /* if it's a match, phase 3 doesn't need to do anything */
15418  if (rel->rd_rel->relam == amoid)
15419  return;
15420 
15421  /* Save info for Phase 3 to do the real work */
15423  tab->newAccessMethod = amoid;
15424  tab->chgAccessMethod = true;
15425 }
Oid get_table_am_oid(const char *amname, bool missing_ok)
Definition: amcmds.c:173
#define AT_REWRITE_ACCESS_METHOD
Definition: event_trigger.h:37
char * default_table_access_method
Definition: tableam.c:48

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

15526 {
15527  Oid tablespaceId;
15528 
15529  /* Check that the tablespace exists */
15530  tablespaceId = get_tablespace_oid(tablespacename, false);
15531 
15532  /* Check permissions except when moving to database's default */
15533  if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
15534  {
15535  AclResult aclresult;
15536 
15537  aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
15538  if (aclresult != ACLCHECK_OK)
15539  aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
15540  }
15541 
15542  /* Save info for Phase 3 to do the real work */
15543  if (OidIsValid(tab->newTableSpace))
15544  ereport(ERROR,
15545  (errcode(ERRCODE_SYNTAX_ERROR),
15546  errmsg("cannot have multiple SET TABLESPACE subcommands")));
15547 
15548  tab->newTableSpace = tablespaceId;
15549 }

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

5206 {
5207  ListCell *ltab;
5208 
5209  /*
5210  * We process all the tables "in parallel", one pass at a time. This is
5211  * needed because we may have to propagate work from one table to another
5212  * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5213  * re-adding of the foreign key constraint to the other table). Work can
5214  * only be propagated into later passes, however.
5215  */
5216  for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5217  {
5218  /* Go through each table that needs to be processed */
5219  foreach(ltab, *wqueue)
5220  {
5221  AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5222  List *subcmds = tab->subcmds[pass];
5223  ListCell *lcmd;
5224 
5225  if (subcmds == NIL)
5226  continue;
5227 
5228  /*
5229  * Open the relation and store it in tab. This allows subroutines
5230  * close and reopen, if necessary. Appropriate lock was obtained
5231  * by phase 1, needn't get it again.
5232  */
5233  tab->rel = relation_open(tab->relid, NoLock);
5234 
5235  foreach(lcmd, subcmds)
5236  ATExecCmd(wqueue, tab,
5237  lfirst_node(AlterTableCmd, lcmd),
5238  lockmode, pass, context);
5239 
5240  /*
5241  * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5242  * (this is not done in ATExecAlterColumnType since it should be
5243  * done only once if multiple columns of a table are altered).
5244  */
5245  if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5246  ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5247 
5248  if (tab->rel)
5249  {
5250  relation_close(tab->rel, NoLock);
5251  tab->rel = NULL;
5252  }
5253  }
5254  }
5255 
5256  /* Check to see if a toast table must be added. */
5257  foreach(ltab, *wqueue)
5258  {
5259  AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5260 
5261  /*
5262  * If the table is source table of ATTACH PARTITION command, we did
5263  * not modify anything about it that will change its toasting
5264  * requirement, so no need to check.
5265  */
5266  if (((tab->relkind == RELKIND_RELATION ||
5267  tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5268  tab->partition_constraint == NULL) ||
5269  tab->relkind == RELKIND_MATVIEW)
5270  AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5271  }
5272 }
Expr * partition_constraint
Definition: tablecmds.c:196
#define AT_NUM_PASSES
Definition: tablecmds.c:164
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
Definition: tablecmds.c:14363
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5278
void AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
Definition: toasting.c:57

References AlterTableCreateToastTable(), AT_NUM_PASSES, AT_PASS_ALTER_TYPE, AT_PASS_SET_EXPRESSION, ATExecCmd(), ATPostAlterTypeCleanup(), context, 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,
LOCKMODE  lockmode 
)
static

Definition at line 6052 of file tablecmds.c.

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

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

Referenced by ATRewriteTables().

◆ ATRewriteTables()

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

Definition at line 5767 of file tablecmds.c.

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

References AlteredTableInfo::afterStmts, ATExecSetTableSpace(), ATRewriteTable(), AlteredTableInfo::chgAccessMethod, AlteredTableInfo::chgPersistence, CommandCounterIncrement(), NewConstraint::conid, Constraint::conname, CONSTR_FOREIGN, AlteredTableInfo::constraints, context, 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 6612 of file tablecmds.c.

6613 {
6614  int actual_target;
6615 
6616  switch (rel->rd_rel->relkind)
6617  {
6618  case RELKIND_RELATION:
6619  case RELKIND_PARTITIONED_TABLE:
6620  actual_target = ATT_TABLE;
6621  break;
6622  case RELKIND_VIEW:
6623  actual_target = ATT_VIEW;
6624  break;
6625  case RELKIND_MATVIEW:
6626  actual_target = ATT_MATVIEW;
6627  break;
6628  case RELKIND_INDEX:
6629  actual_target = ATT_INDEX;
6630  break;
6631  case RELKIND_PARTITIONED_INDEX:
6632  actual_target = ATT_PARTITIONED_INDEX;
6633  break;
6634  case RELKIND_COMPOSITE_TYPE:
6635  actual_target = ATT_COMPOSITE_TYPE;
6636  break;
6637  case RELKIND_FOREIGN_TABLE:
6638  actual_target = ATT_FOREIGN_TABLE;
6639  break;
6640  case RELKIND_SEQUENCE:
6641  actual_target = ATT_SEQUENCE;
6642  break;
6643  default:
6644  actual_target = 0;
6645  break;
6646  }
6647 
6648  /* Wrong target type? */
6649  if ((actual_target & allowed_targets) == 0)
6650  {
6651  const char *action_str = alter_table_type_to_string(cmdtype);
6652 
6653  if (action_str)
6654  ereport(ERROR,
6655  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6656  /* translator: %s is a group of some SQL keywords */
6657  errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6658  action_str, RelationGetRelationName(rel)),
6659  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6660  else
6661  /* internal error? */
6662  elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6664  }
6665 
6666  /* Permissions checks */
6667  if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6670 
6672  ereport(ERROR,
6673  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6674  errmsg("permission denied: \"%s\" is a system catalog",
6675  RelationGetRelationName(rel))));
6676 }
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition: tablecmds.c:6463

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

6690 {
6691  /*
6692  * Propagate to children, if desired and if there are (or might be) any
6693  * children.
6694  */
6695  if (recurse && rel->rd_rel->relhassubclass)
6696  {
6697  Oid relid = RelationGetRelid(rel);
6698  ListCell *child;
6699  List *children;
6700 
6701  children = find_all_inheritors(relid, lockmode, NULL);
6702 
6703  /*
6704  * find_all_inheritors does the recursive search of the inheritance
6705  * hierarchy, so all we have to do is process all of the relids in the
6706  * list that it returns.
6707  */
6708  foreach(child, children)
6709  {
6710  Oid childrelid = lfirst_oid(child);
6711  Relation childrel;
6712 
6713  if (childrelid == relid)
6714  continue;
6715  /* find_all_inheritors already got lock */
6716  childrel = relation_open(childrelid, NoLock);
6717  CheckTableNotInUse(childrel, "ALTER TABLE");
6718  ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6719  relation_close(childrel, NoLock);
6720  }
6721  }
6722 }

References ATPrepCmd(), CheckTableNotInUse(), context, find_all_inheritors(), lfirst_oid, NoLock, RelationData::rd_rel, relation_close(), relation_open(), and RelationGetRelid.

Referenced by ATParseTransformCmd(), and ATPrepCmd().

◆ AttachPartitionEnsureIndexes()

static void AttachPartitionEnsureIndexes ( List **  wqueue,
Relation  rel,
Relation  attachrel 
)
static

Definition at line 19478 of file tablecmds.c.

19479 {
19480  List *idxes;
19481  List *attachRelIdxs;
19482  Relation *attachrelIdxRels;
19483  IndexInfo **attachInfos;
19484  ListCell *cell;
19485  MemoryContext cxt;
19486  MemoryContext oldcxt;
19487 
19489  "AttachPartitionEnsureIndexes",
19491  oldcxt = MemoryContextSwitchTo(cxt);
19492 
19493  idxes = RelationGetIndexList(rel);
19494  attachRelIdxs = RelationGetIndexList(attachrel);
19495  attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19496  attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19497 
19498  /* Build arrays of all existing indexes and their IndexInfos */
19499  foreach(cell, attachRelIdxs)
19500  {
19501  Oid cldIdxId = lfirst_oid(cell);
19502  int i = foreach_current_index(cell);
19503 
19504  attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19505  attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19506  }
19507 
19508  /*
19509  * If we're attaching a foreign table, we must fail if any of the indexes
19510  * is a constraint index; otherwise, there's nothing to do here. Do this
19511  * before starting work, to avoid wasting the effort of building a few
19512  * non-unique indexes before coming across a unique one.
19513  */
19514  if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19515  {
19516  foreach(cell, idxes)
19517  {
19518  Oid idx = lfirst_oid(cell);
19520 
19521  if (idxRel->rd_index->indisunique ||
19522  idxRel->rd_index->indisprimary)
19523  ereport(ERROR,
19524  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19525  errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19526  RelationGetRelationName(attachrel),
19528  errdetail("Partitioned table \"%s\" contains unique indexes.",
19529  RelationGetRelationName(rel))));
19530  index_close(idxRel, AccessShareLock);
19531  }
19532 
19533  goto out;
19534  }
19535 
19536  /*
19537  * For each index on the partitioned table, find a matching one in the
19538  * partition-to-be; if one is not found, create one.
19539  */
19540  foreach(cell, idxes)
19541  {
19542  Oid idx = lfirst_oid(cell);
19544  IndexInfo *info;
19545  AttrMap *attmap;
19546  bool found = false;
19547  Oid constraintOid;
19548 
19549  /*
19550  * Ignore indexes in the partitioned table other than partitioned
19551  * indexes.
19552  */
19553  if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
19554  {
19555  index_close(idxRel, AccessShareLock);
19556  continue;
19557  }
19558 
19559  /* construct an indexinfo to compare existing indexes against */
19560  info = BuildIndexInfo(idxRel);
19561  attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
19562  RelationGetDescr(rel),
19563  false);
19564  constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
19565 
19566  /*
19567  * Scan the list of existing indexes in the partition-to-be, and mark
19568  * the first matching, valid, unattached one we find, if any, as
19569  * partition of the parent index. If we find one, we're done.
19570  */
19571  for (int i = 0; i < list_length(attachRelIdxs); i++)
19572  {
19573  Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
19574  Oid cldConstrOid = InvalidOid;
19575 
19576  /* does this index have a parent? if so, can't use it */
19577  if (attachrelIdxRels[i]->rd_rel->relispartition)
19578  continue;
19579 
19580  /* If this index is invalid, can't use it */
19581  if (!attachrelIdxRels[i]->rd_index->indisvalid)
19582  continue;
19583 
19584  if (CompareIndexInfo(attachInfos[i], info,
19585  attachrelIdxRels[i]->rd_indcollation,
19586  idxRel->rd_indcollation,
19587  attachrelIdxRels[i]->rd_opfamily,
19588  idxRel->rd_opfamily,
19589  attmap))
19590  {
19591  /*
19592  * If this index is being created in the parent because of a
19593  * constraint, then the child needs to have a constraint also,
19594  * so look for one. If there is no such constraint, this
19595  * index is no good, so keep looking.
19596  */
19597  if (OidIsValid(constraintOid))
19598  {
19599  cldConstrOid =
19601  cldIdxId);
19602  /* no dice */
19603  if (!OidIsValid(cldConstrOid))
19604  continue;
19605 
19606  /* Ensure they're both the same type of constraint */
19607  if (get_constraint_type(constraintOid) !=
19608  get_constraint_type(cldConstrOid))
19609  continue;
19610  }
19611 
19612  /* bingo. */
19613  IndexSetParentIndex(attachrelIdxRels[i], idx);
19614  if (OidIsValid(constraintOid))
19615  ConstraintSetParentConstraint(cldConstrOid, constraintOid,
19616  RelationGetRelid(attachrel));
19617  found = true;
19618 
19620  break;
19621  }
19622  }
19623 
19624  /*
19625  * If no suitable index was found in the partition-to-be, create one
19626  * now.
19627  */
19628  if (!found)
19629  {
19630  IndexStmt *stmt;
19631  Oid conOid;
19632 
19634  idxRel, attmap,
19635  &conOid);
19636 
19637  /*
19638  * If the index is a primary key, mark all columns as NOT NULL if
19639  * they aren't already.
19640  */
19641  if (stmt->primary)
19642  {
19643  MemoryContextSwitchTo(oldcxt);
19644  for (int j = 0; j < info->ii_NumIndexKeyAttrs; j++)
19645  {
19646  AttrNumber childattno;
19647 
19648  childattno = get_attnum(RelationGetRelid(attachrel),
19650  info->ii_IndexAttrNumbers[j],
19651  false));
19652  set_attnotnull(wqueue, attachrel, childattno,
19653  true, AccessExclusiveLock);
19654  }
19655  MemoryContextSwitchTo(cxt);
19656  }
19657 
19659  RelationGetRelid(idxRel),
19660  conOid,
19661  -1,
19662  true, false, false, false, false);
19663  }
19664 
19665  index_close(idxRel, AccessShareLock);
19666  }
19667 
19668 out:
19669  /* Clean up. */
19670  for (int i = 0; i < list_length(attachRelIdxs); i++)
19671  index_close(attachrelIdxRels[i], AccessShareLock);
19672  MemoryContextSwitchTo(oldcxt);
19673  MemoryContextDelete(cxt);
19674 }
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
char get_constraint_type(Oid conoid)
Definition: lsyscache.c:1143
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:454
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
int ii_NumIndexKeyAttrs
Definition: execnodes.h:185
AttrNumber ii_IndexAttrNumbers[INDEX_MAX_KEYS]
Definition: execnodes.h:186

References AccessExclusiveLock, AccessShareLock, ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, build_attrmap_by_name(), BuildIndexInfo(), CommandCounterIncrement(), CompareIndexInfo(), ConstraintSetParentConstraint(), CurrentMemoryContext, DefineIndex(), ereport, errcode(), errdetail(), errmsg(), ERROR, foreach_current_index, generateClonedIndexStmt(), get_attname(), get_attnum(), get_constraint_type(), get_relation_idx_constraint_oid(), i, idx(), IndexInfo::ii_IndexAttrNumbers, IndexInfo::ii_NumIndexKeyAttrs, index_close(), index_open(), IndexSetParentIndex(), InvalidOid, j, lfirst_oid, list_length(), MemoryContextDelete(), MemoryContextSwitchTo(), OidIsValid, palloc(), RelationData::rd_indcollation, RelationData::rd_index, RelationData::rd_opfamily, RelationData::rd_rel, RelationGetDescr, RelationGetIndexList(), RelationGetRelationName, RelationGetRelid, set_attnotnull(), and stmt.

Referenced by attachPartitionTable().

◆ attachPartitionTable()

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

Definition at line 19150 of file tablecmds.c.

19151 {
19152  /* OK to create inheritance. Rest of the checks performed there */
19153  CreateInheritance(attachrel, rel, true);
19154 
19155  /* Update the pg_class entry. */
19156  StorePartitionBound(attachrel, rel, bound);
19157 
19158  /* Ensure there exists a correct set of indexes in the partition. */
19159  AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19160 
19161  /* and triggers */
19162  CloneRowTriggersToPartition(rel, attachrel);
19163 
19164  /*
19165  * Clone foreign key constraints. Callee is responsible for setting up
19166  * for phase 3 constraint verification.
19167  */
19168  CloneForeignKeyConstraints(wqueue, rel, attachrel);
19169 }
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition: heap.c:3840
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
Definition: tablecmds.c:19682
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:19478
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:10933

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

6764 {
6765  ListCell *child;
6766  List *children;
6767 
6768  Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6769 
6770  children = find_typed_table_dependencies(rel->rd_rel->reltype,
6772  cmd->behavior);
6773 
6774  foreach(child, children)
6775  {
6776  Oid childrelid = lfirst_oid(child);
6777  Relation childrel;
6778 
6779  childrel = relation_open(childrelid, lockmode);
6780  CheckTableNotInUse(childrel, "ALTER TABLE");
6781  ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6782  relation_close(childrel, NoLock);
6783  }
6784 }
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition: tablecmds.c:6965

References Assert, ATPrepCmd(), AlterTableCmd::behavior, CheckTableNotInUse(), context, 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 1307 of file tablecmds.c.

1308 {
1309  int natts;
1311  ListCell *l;
1312  TupleDesc desc;
1313  bool has_not_null;
1314  char *attname;
1315  Oid atttypid;
1316  int32 atttypmod;
1317  Oid attcollation;
1318  int attdim;
1319 
1320  /*
1321  * allocate a new tuple descriptor
1322  */
1323  natts = list_length(columns);
1324  desc = CreateTemplateTupleDesc(natts);
1325  has_not_null = false;
1326 
1327  attnum = 0;
1328 
1329  foreach(l, columns)
1330  {
1331  ColumnDef *entry = lfirst(l);
1332  AclResult aclresult;
1333  Form_pg_attribute att;
1334 
1335  /*
1336  * for each entry in the list, get the name and type information from
1337  * the list and have TupleDescInitEntry fill in the attribute
1338  * information we need.
1339  */
1340  attnum++;
1341 
1342  attname = entry->colname;
1343  typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1344 
1345  aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1346  if (aclresult != ACLCHECK_OK)
1347  aclcheck_error_type(aclresult, atttypid);
1348 
1349  attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1350  attdim = list_length(entry->typeName->arrayBounds);
1351  if (attdim > PG_INT16_MAX)
1352  ereport(ERROR,
1353  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1354  errmsg("too many array dimensions"));
1355 
1356  if (entry->typeName->setof)
1357  ereport(ERROR,
1358  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1359  errmsg("column \"%s\" cannot be declared SETOF",
1360  attname)));
1361 
1363  atttypid, atttypmod, attdim);
1364  att = TupleDescAttr(desc, attnum - 1);
1365 
1366  /* Override TupleDescInitEntry's settings as requested */
1367  TupleDescInitEntryCollation(desc, attnum, attcollation);
1368 
1369  /* Fill in additional stuff not handled by TupleDescInitEntry */
1370  att->attnotnull = entry->is_not_null;
1371  has_not_null |= entry->is_not_null;
1372  att->attislocal = entry->is_local;
1373  att->attinhcount = entry->inhcount;
1374  att->attidentity = entry->identity;
1375  att->attgenerated = entry->generated;
1376  att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1377  if (entry->storage)
1378  att->attstorage = entry->storage;
1379  else if (entry->storage_name)
1380  att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1381  }
1382 
1383  if (has_not_null)
1384  {
1385  TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
1386 
1387  constr->has_not_null = true;
1388  constr->has_generated_stored = false;
1389  constr->defval = NULL;
1390  constr->missing = NULL;
1391  constr->num_defval = 0;
1392  constr->check = NULL;
1393  constr->num_check = 0;
1394  desc->constr = constr;
1395  }
1396  else
1397  {
1398  desc->constr = NULL;
1399  }
1400 
1401  return desc;
1402 }
NameData attname
Definition: pg_attribute.h:41
char * storage_name
Definition: parsenodes.h:734
char storage
Definition: parsenodes.h:733
char * compression
Definition: parsenodes.h:728
bool has_not_null
Definition: tupdesc.h:44
AttrDefault * defval
Definition: tupdesc.h:39
bool has_generated_stored
Definition: tupdesc.h:45
struct AttrMissing * missing
Definition: tupdesc.h:41
ConstrCheck * check
Definition: tupdesc.h:40
uint16 num_defval
Definition: tupdesc.h:42
uint16 num_check
Definition: tupdesc.h:43
TupleConstr * constr
Definition: tupdesc.h:85
bool setof
Definition: parsenodes.h:270
List * arrayBounds
Definition: parsenodes.h:274
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:67
void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid)
Definition: tupdesc.c:833
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:651

References ACL_USAGE, aclcheck_error_type(), ACLCHECK_OK, TypeName::arrayBounds, attname, attnum, TupleConstr::check, ColumnDef::colname, ColumnDef::compression, TupleDescData::constr, CreateTemplateTupleDesc(), TupleConstr::defval, ereport, errcode(), errmsg(), ERROR, ColumnDef::generated, GetAttributeCompression(), GetAttributeStorage(), GetColumnDefCollation(), GetUserId(), TupleConstr::has_generated_stored, TupleConstr::has_not_null, ColumnDef::identity, ColumnDef::inhcount, ColumnDef::is_local, ColumnDef::is_not_null, lfirst, list_length(), TupleConstr::missing, TupleConstr::num_check, TupleConstr::num_defval, object_aclcheck(), palloc0(), PG_INT16_MAX, TypeName::setof, ColumnDef::storage, ColumnDef::storage_name, TupleDescAttr, TupleDescInitEntry(), TupleDescInitEntryCollation(), ColumnDef::typeName, and typenameTypeIdAndMod().

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

◆ change_owner_fix_column_acls()

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

Definition at line 15223 of file tablecmds.c.

15224 {
15225  Relation attRelation;
15226  SysScanDesc scan;
15227  ScanKeyData key[1];
15228  HeapTuple attributeTuple;
15229 
15230  attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15231  ScanKeyInit(&key[0],
15232  Anum_pg_attribute_attrelid,
15233  BTEqualStrategyNumber, F_OIDEQ,
15234  ObjectIdGetDatum(relationOid));
15235  scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15236  true, NULL, 1, key);
15237  while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15238  {
15239  Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15240  Datum repl_val[Natts_pg_attribute];
15241  bool repl_null[Natts_pg_attribute];
15242  bool repl_repl[Natts_pg_attribute];
15243  Acl *newAcl;
15244  Datum aclDatum;
15245  bool isNull;
15246  HeapTuple newtuple;
15247 
15248  /* Ignore dropped columns */
15249  if (att->attisdropped)
15250  continue;
15251 
15252  aclDatum = heap_getattr(attributeTuple,
15253  Anum_pg_attribute_attacl,
15254  RelationGetDescr(attRelation),
15255  &isNull);
15256  /* Null ACLs do not require changes */
15257  if (isNull)
15258  continue;
15259 
15260  memset(repl_null, false, sizeof(repl_null));
15261  memset(repl_repl, false, sizeof(repl_repl));
15262 
15263  newAcl = aclnewowner(DatumGetAclP(aclDatum),
15264  oldOwnerId, newOwnerId);
15265  repl_repl[Anum_pg_attribute_attacl - 1] = true;
15266  repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15267 
15268  newtuple = heap_modify_tuple(attributeTuple,
15269  RelationGetDescr(attRelation),
15270  repl_val, repl_null, repl_repl);
15271 
15272  CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15273 
15274  heap_freetuple(newtuple);
15275  }
15276  systable_endscan(scan);
15277  table_close(attRelation, RowExclusiveLock);
15278 }

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

15289 {
15290  Relation depRel;
15291  SysScanDesc scan;
15292  ScanKeyData key[2];
15293  HeapTuple tup;
15294 
15295  /*
15296  * SERIAL sequences are those having an auto dependency on one of the
15297  * table's columns (we don't care *which* column, exactly).
15298  */
15299  depRel = table_open(DependRelationId, AccessShareLock);
15300 
15301  ScanKeyInit(&key[0],
15302  Anum_pg_depend_refclassid,
15303  BTEqualStrategyNumber, F_OIDEQ,
15304  ObjectIdGetDatum(RelationRelationId));
15305  ScanKeyInit(&key[1],
15306  Anum_pg_depend_refobjid,
15307  BTEqualStrategyNumber, F_OIDEQ,
15308  ObjectIdGetDatum(relationOid));
15309  /* we leave refobjsubid unspecified */
15310 
15311  scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15312  NULL, 2, key);
15313 
15314  while (HeapTupleIsValid(tup = systable_getnext(scan)))
15315  {
15316  Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15317  Relation seqRel;
15318 
15319  /* skip dependencies other than auto dependencies on columns */
15320  if (depForm->refobjsubid == 0 ||
15321  depForm->classid != RelationRelationId ||
15322  depForm->objsubid != 0 ||
15323  !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15324  continue;
15325 
15326  /* Use relation_open just in case it's an index */
15327  seqRel = relation_open(depForm->objid, lockmode);
15328 
15329  /* skip non-sequence relations */
15330  if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15331  {
15332  /* No need to keep the lock */
15333  relation_close(seqRel, lockmode);
15334  continue;
15335  }
15336 
15337  /* We don't need to close the sequence while we alter it. */
15338  ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15339 
15340  /* Now we can close it. Keep the lock till end of transaction. */
15341  relation_close(seqRel, NoLock);
15342  }
15343 
15344  systable_endscan(scan);
15345 
15347 }

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

7476 {
7477  HeapTuple attTuple;
7478  int attnum;
7479 
7480  /*
7481  * this test is deliberately not attisdropped-aware, since if one tries to
7482  * add a column matching a dropped column name, it's gonna fail anyway.
7483  */
7484  attTuple = SearchSysCache2(ATTNAME,
7486  PointerGetDatum(colname));
7487  if (!HeapTupleIsValid(attTuple))
7488  return true;
7489 
7490  attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7491  ReleaseSysCache(attTuple);
7492 
7493  /*
7494  * We throw a different error message for conflicts with system column
7495  * names, since they are normally not shown and the user might otherwise
7496  * be confused about the reason for the conflict.
7497  */
7498  if (attnum <= 0)
7499  ereport(ERROR,
7500  (errcode(ERRCODE_DUPLICATE_COLUMN),
7501  errmsg("column name \"%s\" conflicts with a system column name",
7502  colname)));
7503  else
7504  {
7505  if (if_not_exists)
7506  {
7507  ereport(NOTICE,
7508  (errcode(ERRCODE_DUPLICATE_COLUMN),
7509  errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7510  colname, RelationGetRelationName(rel))));
7511  return false;
7512  }
7513 
7514  ereport(ERROR,
7515  (errcode(ERRCODE_DUPLICATE_COLUMN),
7516  errmsg("column \"%s\" of relation \"%s\" already exists",
7517  colname, RelationGetRelationName(rel))));
7518  }
7519 
7520  return true;
7521 }
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:229

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

7015 {
7016  Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7017  bool typeOk = false;
7018 
7019  if (typ->typtype == TYPTYPE_COMPOSITE)
7020  {
7021  Relation typeRelation;
7022 
7023  Assert(OidIsValid(typ->typrelid));
7024  typeRelation = relation_open(typ->typrelid, AccessShareLock);
7025  typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7026 
7027  /*
7028  * Close the parent rel, but keep our AccessShareLock on it until xact
7029  * commit. That will prevent someone else from deleting or ALTERing
7030  * the type before the typed table creation/conversion commits.
7031  */
7032  relation_close(typeRelation, NoLock);
7033  }
7034  if (!typeOk)
7035  ereport(ERROR,
7036  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7037  errmsg("type %s is not a composite type",
7038  format_type_be(typ->oid))));
7039 }

References AccessShareLock, Assert, ereport, errcode(), errmsg(), ERROR, format_type_be(), GETSTRUCT, NoLock, OidIsValid, RelationData::rd_rel, relation_close(), and relation_open().

Referenced by ATExecAddOf(), and transformOfType().

◆ checkFkeyPermissions()

static void checkFkeyPermissions ( Relation  rel,
int16 attnums,
int  natts 
)
static

Definition at line 12524 of file tablecmds.c.

12525 {
12526  Oid roleid = GetUserId();
12527  AclResult aclresult;
12528  int i;
12529 
12530  /* Okay if we have relation-level REFERENCES permission */
12531  aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12532  ACL_REFERENCES);
12533  if (aclresult == ACLCHECK_OK)
12534  return;
12535  /* Else we must have REFERENCES on each column */
12536  for (i = 0; i < natts; i++)
12537  {
12538  aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12539  roleid, ACL_REFERENCES);
12540  if (aclresult != ACLCHECK_OK)
12541  aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12543  }
12544 }
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3908
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4079
#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 3629 of file tablecmds.c.

3630 {
3631  Oid oldTableSpaceId;
3632 
3633  /*
3634  * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3635  * stored as 0.
3636  */
3637  oldTableSpaceId = rel->rd_rel->reltablespace;
3638  if (newTableSpaceId == oldTableSpaceId ||
3639  (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3640  return false;
3641 
3642  /*
3643  * We cannot support moving mapped relations into different tablespaces.
3644  * (In particular this eliminates all shared catalogs.)
3645  */
3646  if (RelationIsMapped(rel))
3647  ereport(ERROR,
3648  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3649  errmsg("cannot move system relation \"%s\"",
3650  RelationGetRelationName(rel))));
3651 
3652  /* Cannot move a non-shared relation into pg_global */
3653  if (newTableSpaceId == GLOBALTABLESPACE_OID)
3654  ereport(ERROR,
3655  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3656  errmsg("only shared relations can be placed in pg_global tablespace")));
3657 
3658  /*
3659  * Do not allow moving temp tables of other backends ... their local
3660  * buffer manager is not going to cope.
3661  */
3662  if (RELATION_IS_OTHER_TEMP(rel))
3663  ereport(ERROR,
3664  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3665  errmsg("cannot move temporary tables of other sessions")));
3666 
3667  return true;
3668 }
#define RelationIsMapped(relation)
Definition: rel.h:554

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

4347 {
4348  int expected_refcnt;
4349 
4350  expected_refcnt = rel->rd_isnailed ? 2 : 1;
4351  if (rel->rd_refcnt != expected_refcnt)
4352  ereport(ERROR,
4353  (errcode(ERRCODE_OBJECT_IN_USE),
4354  /* translator: first %s is a SQL command, eg ALTER TABLE */
4355  errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4356  stmt, RelationGetRelationName(rel))));
4357 
4358  if (rel->rd_rel->relkind != RELKIND_INDEX &&
4359  rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4361  ereport(ERROR,
4362  (errcode(ERRCODE_OBJECT_IN_USE),
4363  /* translator: first %s is a SQL command, eg ALTER TABLE */
4364  errmsg("cannot %s \"%s\" because it has pending trigger events",
4365  stmt, RelationGetRelationName(rel))));
4366 }
int rd_refcnt
Definition: rel.h:59
bool rd_isnailed
Definition: rel.h:62
bool AfterTriggerPendingOnRel(Oid relid)
Definition: trigger.c:5974

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

Referenced by addFkRecurseReferencing(), AlterTable(), ATAddCheckNNConstraint(), ATCheckPartitionsNotInUse(), ATExecAddColumn(), ATExecDropColumn(), ATPrepAlterColumnType(), ATSimpleRecursion(), ATTypedTableRecursion(), cluster_rel(), DefineIndex(), DefineVirtualRelation(), dropconstraint_internal(), ExecRefreshMatView(), heap_drop_with_catalog(), index_drop(), MergeAttributes(), reindex_index(), set_attnotnull(), and truncate_check_activity().

◆ ChooseForeignKeyConstraintNameAddition()

static char * ChooseForeignKeyConstraintNameAddition ( List colnames)
static

Definition at line 9686 of file tablecmds.c.

9687 {
9688  char buf[NAMEDATALEN * 2];
9689  int buflen = 0;
9690  ListCell *lc;
9691 
9692  buf[0] = '\0';
9693  foreach(lc, colnames)
9694  {
9695  const char *name = strVal(lfirst(lc));
9696 
9697  if (buflen > 0)
9698  buf[buflen++] = '_'; /* insert _ between names */
9699 
9700  /*
9701  * At this point we have buflen <= NAMEDATALEN. name should be less
9702  * than NAMEDATALEN already, but use strlcpy for paranoia.
9703  */
9704  strlcpy(buf + buflen, name, NAMEDATALEN);
9705  buflen += strlen(buf + buflen);
9706  if (buflen >= NAMEDATALEN)
9707  break;
9708  }
9709  return pstrdup(buf);
9710 }
static char * buf
Definition: pg_test_fsync.c:73

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

Referenced by addFkRecurseReferenced(), addFkRecurseReferencing(), ATExecAddConstraint(), and CloneFkReferencing().

◆ CloneFkReferenced()

static void CloneFkReferenced ( Relation  parentRel,
Relation  partitionRel 
)
static

Definition at line 10964 of file tablecmds.c.

10965 {
10966  Relation pg_constraint;
10967  AttrMap *attmap;
10968  ListCell *cell;
10969  SysScanDesc scan;
10970  ScanKeyData key[2];
10971  HeapTuple tuple;
10972  List *clone = NIL;
10973  Relation trigrel;
10974 
10975  /*
10976  * Search for any constraints where this partition's parent is in the
10977  * referenced side. However, we must not clone any constraint whose
10978  * parent constraint is also going to be cloned, to avoid duplicates. So
10979  * do it in two steps: first construct the list of constraints to clone,
10980  * then go over that list cloning those whose parents are not in the list.
10981  * (We must not rely on the parent being seen first, since the catalog
10982  * scan could return children first.)
10983  */
10984  pg_constraint = table_open(ConstraintRelationId, RowShareLock);
10985  ScanKeyInit(&key[0],
10986  Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10987  F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10988  ScanKeyInit(&key[1],
10989  Anum_pg_constraint_contype, BTEqualStrategyNumber,
10990  F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
10991  /* This is a seqscan, as we don't have a usable index ... */
10992  scan = systable_beginscan(pg_constraint, InvalidOid, true,
10993  NULL, 2, key);
10994  while ((tuple = systable_getnext(scan)) != NULL)
10995  {
10996  Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10997 
10998  clone = lappend_oid(clone, constrForm->oid);
10999  }
11000  systable_endscan(scan);
11001  table_close(pg_constraint, RowShareLock);
11002 
11003  /*
11004  * Triggers of the foreign keys will be manipulated a bunch of times in
11005  * the loop below. To avoid repeatedly opening/closing the trigger
11006  * catalog relation, we open it here and pass it to the subroutines called
11007  * below.
11008  */
11009  trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11010 
11011  attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11012  RelationGetDescr(parentRel),
11013  false);
11014  foreach(cell, clone)
11015  {
11016  Oid constrOid = lfirst_oid(cell);
11017  Form_pg_constraint constrForm;
11018  Relation fkRel;
11019  Oid indexOid;
11020  Oid partIndexId;
11021  int numfks;
11022  AttrNumber conkey[INDEX_MAX_KEYS];
11023  AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11024  AttrNumber confkey[INDEX_MAX_KEYS];
11025  Oid conpfeqop[INDEX_MAX_KEYS];
11026  Oid conppeqop[INDEX_MAX_KEYS];
11027  Oid conffeqop[INDEX_MAX_KEYS];
11028  int numfkdelsetcols;
11029  AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11030  Constraint *fkconstraint;
11031  Oid deleteTriggerOid,
11032  updateTriggerOid;
11033 
11034  tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11035  if (!HeapTupleIsValid(tuple))
11036  elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11037  constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11038 
11039  /*
11040  * As explained above: don't try to clone a constraint for which we're
11041  * going to clone the parent.
11042  */
11043  if (list_member_oid(clone, constrForm->conparentid))
11044  {
11045  ReleaseSysCache(tuple);
11046  continue;
11047  }
11048 
11049  /*
11050  * Don't clone self-referencing foreign keys, which can be in the
11051  * partitioned table or in the partition-to-be.
11052  */
11053  if (constrForm->conrelid == RelationGetRelid(parentRel) ||
11054  constrForm->conrelid == RelationGetRelid(partitionRel))
11055  {
11056  ReleaseSysCache(tuple);
11057  continue;
11058  }
11059 
11060  /*
11061  * Because we're only expanding the key space at the referenced side,
11062  * we don't need to prevent any operation in the referencing table, so
11063  * AccessShareLock suffices (assumes that dropping the constraint
11064  * acquires AEL).
11065  */
11066  fkRel = table_open(constrForm->conrelid, AccessShareLock);
11067 
11068  indexOid = constrForm->conindid;
11070  &numfks,
11071  conkey,
11072  confkey,
11073  conpfeqop,
11074  conppeqop,
11075  conffeqop,
11076  &numfkdelsetcols,
11077  confdelsetcols);
11078 
11079  for (int i = 0; i < numfks; i++)
11080  mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11081 
11082  fkconstraint = makeNode(Constraint);
11083  fkconstraint->contype = CONSTRAINT_FOREIGN;
11084  fkconstraint->conname = NameStr(constrForm->conname);
11085  fkconstraint->deferrable = constrForm->condeferrable;
11086  fkconstraint->initdeferred = constrForm->condeferred;
11087  fkconstraint->location = -1;
11088  fkconstraint->pktable = NULL;
11089  /* ->fk_attrs determined below */
11090  fkconstraint->pk_attrs = NIL;
11091  fkconstraint->fk_matchtype = constrForm->confmatchtype;
11092  fkconstraint->fk_upd_action = constrForm->confupdtype;
11093  fkconstraint->fk_del_action = constrForm->confdeltype;
11094  fkconstraint->fk_del_set_cols = NIL;
11095  fkconstraint->old_conpfeqop = NIL;
11096  fkconstraint->old_pktable_oid = InvalidOid;
11097  fkconstraint->skip_validation = false;
11098  fkconstraint->initially_valid = true;
11099 
11100  /* set up colnames that are used to generate the constraint name */
11101  for (int i = 0; i < numfks; i++)
11102  {
11103  Form_pg_attribute att;
11104 
11105  att = TupleDescAttr(RelationGetDescr(fkRel),
11106  conkey[i] - 1);
11107  fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11108  makeString(NameStr(att->attname)));
11109  }
11110 
11111  /*
11112  * Add the new foreign key constraint pointing to the new partition.
11113  * Because this new partition appears in the referenced side of the
11114  * constraint, we don't need to set up for Phase 3 check.
11115  */
11116  partIndexId = index_get_partition(partitionRel, indexOid);
11117  if (!OidIsValid(partIndexId))
11118  elog(ERROR, "index for %u not found in partition %s",
11119  indexOid, RelationGetRelationName(partitionRel));
11120 
11121  /*
11122  * Get the "action" triggers belonging to the constraint to pass as
11123  * parent OIDs for similar triggers that will be created on the
11124  * partition in addFkRecurseReferenced().
11125  */
11126  GetForeignKeyActionTriggers(trigrel, constrOid,
11127  constrForm->confrelid, constrForm->conrelid,
11128  &deleteTriggerOid, &updateTriggerOid);
11129 
11131  fkconstraint,
11132  fkRel,
11133  partitionRel,
11134  partIndexId,
11135  constrOid,
11136  numfks,
11137  mapped_confkey,
11138  conkey,
11139  conpfeqop,
11140  conppeqop,
11141  conffeqop,
11142  numfkdelsetcols,
11143  confdelsetcols,
11144  true,
11145  deleteTriggerOid,
11146  updateTriggerOid,
11147  constrForm->conperiod);
11148 
11149  table_close(fkRel, NoLock);
11150  ReleaseSysCache(tuple);
11151  }
11152 
11153  table_close(trigrel, RowExclusiveLock);
11154 }
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:122
static void GetForeignKeyActionTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:11585

References AccessShareLock, addFkRecurseReferenced(), AttrMap::attnums, BTEqualStrategyNumber, build_attrmap_by_name(), CharGetDatum(), Constraint::conname, Constraint::contype, DeconstructFkConstraintRow(), Constraint::deferrable, elog, ERROR, Constraint::fk_attrs, Constraint::fk_del_action, Constraint::fk_del_set_cols, Constraint::fk_matchtype, Constraint::fk_upd_action, GetForeignKeyActionTriggers(), GETSTRUCT, HeapTupleIsValid, i, index_get_partition(), INDEX_MAX_KEYS, Constraint::initdeferred, Constraint::initially_valid, InvalidOid, sort-test::key, lappend(), lappend_oid(), lfirst_oid, list_member_oid(), Constraint::location, makeNode, makeString(), NameStr, NIL, NoLock, ObjectIdGetDatum(), OidIsValid, Constraint::old_conpfeqop, Constraint::old_pktable_oid, Constraint::pk_attrs, Constraint::pktable, RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, RowShareLock, ScanKeyInit(), SearchSysCache1(), 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 11170 of file tablecmds.c.

11171 {
11172  AttrMap *attmap;
11173  List *partFKs;
11174  List *clone = NIL;
11175  ListCell *cell;
11176  Relation trigrel;
11177 
11178  /* obtain a list of constraints that we need to clone */
11179  foreach(cell, RelationGetFKeyList(parentRel))
11180  {
11181  ForeignKeyCacheInfo *fk = lfirst(cell);
11182 
11183  clone = lappend_oid(clone, fk->conoid);
11184  }
11185 
11186  /*
11187  * Silently do nothing if there's nothing to do. In particular, this
11188  * avoids throwing a spurious error for foreign tables.
11189  */
11190  if (clone == NIL)
11191  return;
11192 
11193  if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11194  ereport(ERROR,
11195  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11196  errmsg("foreign key constraints are not supported on foreign tables")));
11197 
11198  /*
11199  * Triggers of the foreign keys will be manipulated a bunch of times in
11200  * the loop below. To avoid repeatedly opening/closing the trigger
11201  * catalog relation, we open it here and pass it to the subroutines called
11202  * below.
11203  */
11204  trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11205 
11206  /*
11207  * The constraint key may differ, if the columns in the partition are
11208  * different. This map is used to convert them.
11209  */
11210  attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11211  RelationGetDescr(parentRel),
11212  false);
11213 
11214  partFKs = copyObject(RelationGetFKeyList(partRel));
11215 
11216  foreach(cell, clone)
11217  {
11218  Oid parentConstrOid = lfirst_oid(cell);
11219  Form_pg_constraint constrForm;
11220  Relation pkrel;
11221  HeapTuple tuple;
11222  int numfks;
11223  AttrNumber conkey[INDEX_MAX_KEYS];
11224  AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11225  AttrNumber confkey[INDEX_MAX_KEYS];
11226  Oid conpfeqop[INDEX_MAX_KEYS];
11227  Oid conppeqop[INDEX_MAX_KEYS];
11228  Oid conffeqop[INDEX_MAX_KEYS];
11229  int numfkdelsetcols;
11230  AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11231  Constraint *fkconstraint;
11232  bool attached;
11233  Oid indexOid;
11234  Oid constrOid;
11235  ObjectAddress address,
11236  referenced;
11237  ListCell *lc;
11238  Oid insertTriggerOid,
11239  updateTriggerOid;
11240  bool with_period;
11241 
11242  tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11243  if (!HeapTupleIsValid(tuple))
11244  elog(ERROR, "cache lookup failed for constraint %u",
11245  parentConstrOid);
11246  constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11247 
11248  /* Don't clone constraints whose parents are being cloned */
11249  if (list_member_oid(clone, constrForm->conparentid))
11250  {
11251  ReleaseSysCache(tuple);
11252  continue;
11253  }
11254 
11255  /*
11256  * Need to prevent concurrent deletions. If pkrel is a partitioned
11257  * relation, that means to lock all partitions.
11258  */
11259  pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11260  if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11261  (void) find_all_inheritors(RelationGetRelid(pkrel),
11262  ShareRowExclusiveLock, NULL);
11263 
11264  DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11265  conpfeqop, conppeqop, conffeqop,
11266  &numfkdelsetcols, confdelsetcols);
11267  for (int i = 0; i < numfks; i++)
11268  mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11269 
11270  /*
11271  * Get the "check" triggers belonging to the constraint to pass as
11272  * parent OIDs for similar triggers that will be created on the
11273  * partition in addFkRecurseReferencing(). They are also passed to
11274  * tryAttachPartitionForeignKey() below to simply assign as parents to
11275  * the partition's existing "check" triggers, that is, if the
11276  * corresponding constraints is deemed attachable to the parent
11277  * constraint.
11278  */
11279  GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11280  constrForm->confrelid, constrForm->conrelid,
11281  &insertTriggerOid, &updateTriggerOid);
11282 
11283  /*
11284  * Before creating a new constraint, see whether any existing FKs are
11285  * fit for the purpose. If one is, attach the parent constraint to
11286  * it, and don't clone anything. This way we avoid the expensive
11287  * verification step and don't end up with a duplicate FK, and we
11288  * don't need to recurse to partitions for this constraint.
11289  */
11290  attached = false;
11291  foreach(lc, partFKs)
11292  {
11294 
11296  RelationGetRelid(partRel),
11297  parentConstrOid,
11298  numfks,
11299  mapped_conkey,
11300  confkey,
11301  conpfeqop,
11302  insertTriggerOid,
11303  updateTriggerOid,
11304  trigrel))
11305  {
11306  attached = true;
11307  table_close(pkrel, NoLock);
11308  break;
11309  }
11310  }
11311  if (attached)
11312  {
11313  ReleaseSysCache(tuple);
11314  continue;
11315  }
11316 
11317  /* No dice. Set up to create our own constraint */
11318  fkconstraint = makeNode(Constraint);
11319  fkconstraint->contype = CONSTRAINT_FOREIGN;
11320  /* ->conname determined below */
11321  fkconstraint->deferrable = constrForm->condeferrable;
11322  fkconstraint->initdeferred = constrForm->condeferred;
11323  fkconstraint->location = -1;
11324  fkconstraint->pktable = NULL;
11325  /* ->fk_attrs determined below */
11326  fkconstraint->pk_attrs = NIL;
11327  fkconstraint->fk_matchtype = constrForm->confmatchtype;
11328  fkconstraint->fk_upd_action = constrForm->confupdtype;
11329  fkconstraint->fk_del_action = constrForm->confdeltype;
11330  fkconstraint->fk_del_set_cols = NIL;
11331  fkconstraint->old_conpfeqop = NIL;
11332  fkconstraint->old_pktable_oid = InvalidOid;
11333  fkconstraint->skip_validation = false;
11334  fkconstraint->initially_valid = true;
11335  for (int i = 0; i < numfks; i++)
11336  {
11337  Form_pg_attribute att;
11338 
11339  att = TupleDescAttr(RelationGetDescr(partRel),
11340  mapped_conkey[i] - 1);
11341  fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11342  makeString(NameStr(att->attname)));
11343  }
11345  RelationGetRelid(partRel),
11346  NameStr(constrForm->conname)))
11347  fkconstraint->conname =
11350  "fkey",
11351  RelationGetNamespace(partRel), NIL);
11352  else
11353  fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
11354 
11355  indexOid = constrForm->conindid;
11356  with_period = constrForm->conperiod;
11357  constrOid =
11358  CreateConstraintEntry(fkconstraint->conname,
11359  constrForm->connamespace,
11360  CONSTRAINT_FOREIGN,
11361  fkconstraint->deferrable,
11362  fkconstraint->initdeferred,
11363  constrForm->convalidated,
11364  parentConstrOid,
11365  RelationGetRelid(partRel),
11366  mapped_conkey,
11367  numfks,
11368  numfks,
11369  InvalidOid, /* not a domain constraint */
11370  indexOid,
11371  constrForm->confrelid, /* same foreign rel */
11372  confkey,
11373  conpfeqop,
11374  conppeqop,
11375  conffeqop,
11376  numfks,
11377  fkconstraint->fk_upd_action,
11378  fkconstraint->fk_del_action,
11379  confdelsetcols,
11380  numfkdelsetcols,
11381  fkconstraint->fk_matchtype,
11382  NULL,
11383  NULL,
11384  NULL,
11385  false, /* islocal */
11386  1, /* inhcount */
11387  false, /* conNoInherit */
11388  with_period, /* conPeriod */
11389  true);
11390 
11391  /* Set up partition dependencies for the new constraint */
11392  ObjectAddressSet(address, ConstraintRelationId, constrOid);
11393  ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
11394  recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
11395  ObjectAddressSet(referenced, RelationRelationId,
11396  RelationGetRelid(partRel));
11397  recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
11398 
11399  /* Done with the cloned constraint's tuple */
11400  ReleaseSysCache(tuple);
11401 
11402  /* Make all this visible before recursing */
11404 
11405  addFkRecurseReferencing(wqueue,
11406  fkconstraint,
11407  partRel,
11408  pkrel,
11409  indexOid,
11410  constrOid,
11411  numfks,
11412  confkey,
11413  mapped_conkey,
11414  conpfeqop,
11415  conppeqop,
11416  conffeqop,
11417  numfkdelsetcols,
11418  confdelsetcols,
11419  false, /* no old check exists */
11421  insertTriggerOid,
11422  updateTriggerOid,
11423  with_period);
11424  table_close(pkrel, NoLock);
11425  }
11426 
11427  table_close(trigrel, RowExclusiveLock);
11428 }
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:11646

References AccessExclusiveLock, addFkRecurseReferencing(), AttrMap::attnums, build_attrmap_by_name(), ChooseConstraintName(), ChooseForeignKeyConstraintNameAddition(), CommandCounterIncrement(), Constraint::conname, ForeignKeyCacheInfo::conoid, CONSTRAINT_RELATION, ConstraintNameIsUsed(), Constraint::contype, copyObject, CreateConstraintEntry(), DeconstructFkConstraintRow(), Constraint::deferrable, DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, 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, GetForeignKeyCheckTriggers(), GETSTRUCT, HeapTupleIsValid, i, INDEX_MAX_KEYS, Constraint::initdeferred, Constraint::initially_valid, InvalidOid, lappend(), lappend_oid(), lfirst, lfirst_node, lfirst_oid, list_member_oid(), Constraint::location, makeNode, makeString(), NameStr, NIL, NoLock, ObjectAddressSet, ObjectIdGetDatum(), Constraint::old_conpfeqop, Constraint::old_pktable_oid, Constraint::pk_attrs, Constraint::pktable, pstrdup(), RelationData::rd_rel, recordDependencyOn(), RelationGetDescr, RelationGetFKeyList(), RelationGetNamespace, 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 10933 of file tablecmds.c.

10935 {
10936  /* This only works for declarative partitioning */
10937  Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10938 
10939  /*
10940  * Clone constraints for which the parent is on the referenced side.
10941  */
10942  CloneFkReferenced(parentRel, partitionRel);
10943 
10944  /*
10945  * Now clone constraints where the parent is on the referencing side.
10946  */
10947  CloneFkReferencing(wqueue, parentRel, partitionRel);
10948 }
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11170
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:10964

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

19683 {
19684  Relation pg_trigger;
19685  ScanKeyData key;
19686  SysScanDesc scan;
19687  HeapTuple tuple;
19688  MemoryContext perTupCxt;
19689 
19690  ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19691  F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
19692  pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
19693  scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
19694  true, NULL, 1, &key);
19695 
19697  "clone trig", ALLOCSET_SMALL_SIZES);
19698 
19699  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19700  {
19701  Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
19702  CreateTrigStmt *trigStmt;
19703  Node *qual = NULL;
19704  Datum value;
19705  bool isnull;
19706  List *cols = NIL;
19707  List *trigargs = NIL;
19708  MemoryContext oldcxt;
19709 
19710  /*
19711  * Ignore statement-level triggers; those are not cloned.
19712  */
19713  if (!TRIGGER_FOR_ROW(trigForm->tgtype))
19714  continue;
19715 
19716  /*
19717  * Don't clone internal triggers, because the constraint cloning code
19718  * will.
19719  */
19720  if (trigForm->tgisinternal)
19721  continue;
19722 
19723  /*
19724  * Complain if we find an unexpected trigger type.
19725  */
19726  if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
19727  !TRIGGER_FOR_AFTER(trigForm->tgtype))
19728  elog(ERROR, "unexpected trigger \"%s\" found",
19729  NameStr(trigForm->tgname));
19730 
19731  /* Use short-lived context for CREATE TRIGGER */
19732  oldcxt = MemoryContextSwitchTo(perTupCxt);
19733 
19734  /*
19735  * If there is a WHEN clause, generate a 'cooked' version of it that's
19736  * appropriate for the partition.
19737  */
19738  value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
19739  RelationGetDescr(pg_trigger), &isnull);
19740  if (!isnull)
19741  {
19743  qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
19744  partition, parent);
19745  qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
19746  partition, parent);
19747  }
19748 
19749  /*
19750  * If there is a column list, transform it to a list of column names.
19751  * Note we don't need to map this list in any way ...
19752  */
19753  if (trigForm->tgattr.dim1 > 0)
19754  {
19755  int i;
19756 
19757  for (i = 0; i < trigForm->tgattr.dim1; i++)
19758  {
19759  Form_pg_attribute col;
19760 
19761  col = TupleDescAttr(parent->rd_att,
19762  trigForm->tgattr.values[i] - 1);
19763  cols = lappend(cols,
19764  makeString(pstrdup(NameStr(col->attname))));
19765  }
19766  }
19767 
19768  /* Reconstruct trigger arguments list. */
19769  if (trigForm->tgnargs > 0)
19770  {
19771  char *p;
19772 
19773  value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
19774  RelationGetDescr(pg_trigger), &isnull);
19775  if (isnull)
19776  elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19777  NameStr(trigForm->tgname), RelationGetRelationName(partition));
19778 
19779  p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
19780 
19781  for (int i = 0; i < trigForm->tgnargs; i++)
19782  {
19783  trigargs = lappend(trigargs, makeString(pstrdup(p)));
19784  p += strlen(p) + 1;
19785  }
19786  }
19787 
19788  trigStmt = makeNode(CreateTrigStmt);
19789  trigStmt->replace = false;
19790  trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
19791  trigStmt->trigname = NameStr(trigForm->tgname);
19792  trigStmt->relation = NULL;
19793  trigStmt->funcname = NULL; /* passed separately */
19794  trigStmt->args = trigargs;
19795  trigStmt->row = true;
19796  trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
19797  trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
19798  trigStmt->columns = cols;
19799  trigStmt->whenClause = NULL; /* passed separately */
19800  trigStmt->transitionRels = NIL; /* not supported at present */
19801  trigStmt->deferrable = trigForm->tgdeferrable;
19802  trigStmt->initdeferred = trigForm->tginitdeferred;
19803  trigStmt->constrrel = NULL; /* passed separately */
19804 
19805  CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
19806  trigForm->tgconstrrelid, InvalidOid, InvalidOid,
19807  trigForm->tgfoid, trigForm->oid, qual,
19808  false, true, trigForm->tgenabled);
19809 
19810  MemoryContextSwitchTo(oldcxt);
19811  MemoryContextReset(perTupCxt);
19812  }
19813 
19814  MemoryContextDelete(perTupCxt);
19815 
19816  systable_endscan(scan);
19817  table_close(pg_trigger, RowExclusiveLock);
19818 }
#define DatumGetByteaPP(X)
Definition: fmgr.h:291
static struct @155 value
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:383
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:170
#define PRS2_OLD_VARNO
Definition: primnodes.h:244
#define PRS2_NEW_VARNO
Definition: primnodes.h:245
Node * whenClause
Definition: parsenodes.h:3027
List * transitionRels
Definition: parsenodes.h:3029
RangeVar * constrrel
Definition: parsenodes.h:3033
RangeVar * relation
Definition: parsenodes.h:3018
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:175
#define VARDATA_ANY(PTR)
Definition: varatt.h:324

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, CreateTrigStmt::args, BTEqualStrategyNumber, CreateTrigStmt::columns, CreateTrigStmt::constrrel, CreateTriggerFiringOn(), CurrentMemoryContext, DatumGetByteaPP, CreateTrigStmt::deferrable, elog, ERROR, CreateTrigStmt::events, CreateTrigStmt::funcname, GETSTRUCT, heap_getattr(), HeapTupleIsValid, i, CreateTrigStmt::initdeferred, InvalidOid, CreateTrigStmt::isconstraint, sort-test::key, lappend(), makeNode, makeString(), map_partition_varattnos(), MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), NameStr, NIL, ObjectIdGetDatum(), OidIsValid, PRS2_NEW_VARNO, PRS2_OLD_VARNO, pstrdup(), RelationData::rd_att, CreateTrigStmt::relation, RelationGetDescr, RelationGetRelationName, RelationGetRelid, CreateTrigStmt::replace, CreateTrigStmt::row, RowExclusiveLock, ScanKeyInit(), stringToNode(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TextDatumGetCString, CreateTrigStmt::timing, CreateTrigStmt::transitionRels, CreateTrigStmt::trigname, TupleDescAttr, value, VARDATA_ANY, and CreateTrigStmt::whenClause.

Referenced by 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 18705 of file tablecmds.c.

18708 {
18709  int attn;
18710  ListCell *lc;
18711  Oid am_oid;
18712 
18713  attn = 0;
18714  foreach(lc, partParams)
18715  {
18716  PartitionElem *pelem = lfirst_node(PartitionElem, lc);
18717  Oid atttype;
18718  Oid attcollation;
18719 
18720  if (pelem->name != NULL)
18721  {
18722  /* Simple attribute reference */
18723  HeapTuple atttuple;
18724  Form_pg_attribute attform;
18725 
18726  atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
18727  pelem->name);
18728  if (!HeapTupleIsValid(atttuple))
18729  ereport(ERROR,
18730  (errcode(ERRCODE_UNDEFINED_COLUMN),
18731  errmsg("column \"%s\" named in partition key does not exist",
18732  pelem->name),
18733  parser_errposition(pstate, pelem->location)));
18734  attform = (Form_pg_attribute) GETSTRUCT(atttuple);
18735 
18736  if (attform->attnum <= 0)
18737  ereport(ERROR,
18738  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18739  errmsg("cannot use system column \"%s\" in partition key",
18740  pelem->name),
18741  parser_errposition(pstate, pelem->location)));
18742 
18743  /*
18744  * Generated columns cannot work: They are computed after BEFORE
18745  * triggers, but partition routing is done before all triggers.
18746  */
18747  if (attform->attgenerated)
18748  ereport(ERROR,
18749  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18750  errmsg("cannot use generated column in partition key"),
18751  errdetail("Column \"%s\" is a generated column.",
18752  pelem->name),
18753  parser_errposition(pstate, pelem->location)));
18754 
18755  partattrs[attn] = attform->attnum;
18756  atttype = attform->atttypid;
18757  attcollation = attform->attcollation;
18758  ReleaseSysCache(atttuple);
18759  }
18760  else
18761  {
18762  /* Expression */
18763  Node *expr = pelem->expr;
18764  char partattname[16];
18765 
18766  Assert(expr != NULL);
18767  atttype = exprType(expr);
18768  attcollation = exprCollation(expr);
18769 
18770  /*
18771  * The expression must be of a storable type (e.g., not RECORD).
18772  * The test is the same as for whether a table column is of a safe
18773  * type (which is why we needn't check for the non-expression
18774  * case).
18775  */
18776  snprintf(partattname, sizeof(partattname), "%d", attn + 1);
18777  CheckAttributeType(partattname,
18778  atttype, attcollation,
18780 
18781  /*
18782  * Strip any top-level COLLATE clause. This ensures that we treat
18783  * "x COLLATE y" and "(x COLLATE y)" alike.
18784  */
18785  while (IsA(expr, CollateExpr))
18786  expr = (Node *) ((CollateExpr *) expr)->arg;
18787 
18788  if (IsA(expr, Var) &&
18789  ((Var *) expr)->varattno > 0)
18790  {
18791  /*
18792  * User wrote "(column)" or "(column COLLATE something)".
18793  * Treat it like simple attribute anyway.
18794  */
18795  partattrs[attn] = ((Var *) expr)->varattno;
18796  }
18797  else
18798  {
18799  Bitmapset *expr_attrs = NULL;
18800  int i;
18801 
18802  partattrs[attn] = 0; /* marks the column as expression */
18803  *partexprs = lappend(*partexprs, expr);
18804 
18805  /*
18806  * transformPartitionSpec() should have already rejected
18807  * subqueries, aggregates, window functions, and SRFs, based
18808  * on the EXPR_KIND_ for partition expressions.
18809  */
18810 
18811  /*
18812  * Cannot allow system column references, since that would
18813  * make partition routing impossible: their values won't be
18814  * known yet when we need to do that.
18815  */
18816  pull_varattnos(expr, 1, &expr_attrs);
18817  for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
18818  {
18820  expr_attrs))
18821  ereport(ERROR,
18822  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18823  errmsg("partition key expressions cannot contain system column references")));
18824  }
18825 
18826  /*
18827  * Generated columns cannot work: They are computed after
18828  * BEFORE triggers, but partition routing is done before all
18829  * triggers.
18830  */
18831  i = -1;
18832  while ((i = bms_next_member(expr_attrs, i)) >= 0)
18833  {
18835 
18836  if (attno > 0 &&
18837  TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18838  ereport(ERROR,
18839  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18840  errmsg("cannot use generated column in partition key"),
18841  errdetail("Column \"%s\" is a generated column.",
18842  get_attname(RelationGetRelid(rel), attno, false)),
18843  parser_errposition(pstate, pelem->location)));
18844  }
18845 
18846  /*
18847  * Preprocess the expression before checking for mutability.
18848  * This is essential for the reasons described in
18849  * contain_mutable_functions_after_planning. However, we call
18850  * expression_planner for ourselves rather than using that
18851  * function, because if constant-folding reduces the
18852  * expression to a constant, we'd like to know that so we can
18853  * complain below.
18854  *
18855  * Like contain_mutable_functions_after_planning, assume that
18856  * expression_planner won't scribble on its input, so this
18857  * won't affect the partexprs entry we saved above.
18858  */
18859  expr = (Node *) expression_planner((Expr *) expr);
18860 
18861  /*
18862  * Partition expressions cannot contain mutable functions,
18863  * because a given row must always map to the same partition
18864  * as long as there is no change in the partition boundary
18865  * structure.
18866  */
18867  if (contain_mutable_functions(expr))
18868  ereport(ERROR,
18869  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18870  errmsg("functions in partition key expression must be marked IMMUTABLE")));
18871 
18872  /*
18873  * While it is not exactly *wrong* for a partition expression
18874  * to be a constant, it seems better to reject such keys.
18875  */
18876  if (IsA(expr, Const))
18877  ereport(ERROR,
18878  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18879  errmsg("cannot use constant expression as partition key")));
18880  }
18881  }
18882 
18883  /*
18884  * Apply collation override if any
18885  */
18886  if (pelem->collation)
18887  attcollation = get_collation_oid(pelem->collation, false);
18888 
18889  /*
18890  * Check we have a collation iff it's a collatable type. The only
18891  * expected failures here are (1) COLLATE applied to a noncollatable
18892  * type, or (2) partition expression had an unresolved collation. But
18893  * we might as well code this to be a complete consistency check.
18894  */
18895  if (type_is_collatable(atttype))
18896  {
18897  if (!OidIsValid(attcollation))
18898  ereport(ERROR,
18899  (errcode(ERRCODE_INDETERMINATE_COLLATION),
18900  errmsg("could not determine which collation to use for partition expression"),
18901  errhint("Use the COLLATE clause to set the collation explicitly.")));
18902  }
18903  else
18904  {
18905  if (OidIsValid(attcollation))
18906  ereport(ERROR,
18907  (errcode(ERRCODE_DATATYPE_MISMATCH),
18908  errmsg("collations are not supported by type %s",
18909  format_type_be(atttype))));
18910  }
18911 
18912  partcollation[attn] = attcollation;
18913 
18914  /*
18915  * Identify the appropriate operator class. For list and range
18916  * partitioning, we use a btree operator class; hash partitioning uses
18917  * a hash operator class.
18918  */
18919  if (strategy == PARTITION_STRATEGY_HASH)
18920  am_oid = HASH_AM_OID;
18921  else
18922  am_oid = BTREE_AM_OID;
18923 
18924  if (!pelem->opclass)
18925  {
18926  partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
18927 
18928  if (!OidIsValid(partopclass[attn]))
18929  {
18930  if (strategy == PARTITION_STRATEGY_HASH)
18931  ereport(ERROR,
18932  (errcode(ERRCODE_UNDEFINED_OBJECT),
18933  errmsg("data type %s has no default operator class for access method \"%s\"",
18934  format_type_be(atttype), "hash"),
18935  errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18936  else
18937  ereport(ERROR,
18938  (errcode(ERRCODE_UNDEFINED_OBJECT),
18939  errmsg("data type %s has no default operator class for access method \"%s\"",
18940  format_type_be(atttype), "btree"),
18941  errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18942  }
18943  }
18944  else
18945  partopclass[attn] = ResolveOpClass(pelem->opclass,
18946  atttype,
18947  am_oid == HASH_AM_OID ? "hash" : "btree",
18948  am_oid);
18949 
18950  attn++;
18951  }
18952 }
bool contain_mutable_functions(Node *clause)
Definition: clauses.c:370
#define CHKATYPE_IS_PARTKEY
Definition: heap.h:25
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2338
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition: indexcmds.c:2253
bool type_is_collatable(Oid typid)
Definition: lsyscache.c:3081
Oid get_collation_oid(List *collname, bool missing_ok)
Definition: namespace.c:3956
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:816
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:874
#define snprintf
Definition: port.h:238
List * collation
Definition: parsenodes.h:865
ParseLoc location
Definition: parsenodes.h:867
List * opclass
Definition: parsenodes.h:866
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:291

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

Referenced by DefineRelation().

◆ ConstraintImpliedByRelConstraint()

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

Definition at line 19016 of file tablecmds.c.

19017 {
19018  List *existConstraint = list_copy(provenConstraint);
19019  TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19020  int num_check,
19021  i;
19022 
19023  num_check = (constr != NULL) ? constr->num_check : 0;
19024  for (i = 0; i < num_check; i++)
19025  {
19026  Node *cexpr;
19027 
19028  /*
19029  * If this constraint hasn't been fully validated yet, we must ignore
19030  * it here.
19031  */
19032  if (!constr->check[i].ccvalid)
19033  continue;
19034 
19035  cexpr = stringToNode(constr->check[i].ccbin);
19036 
19037  /*
19038  * Run each expression through const-simplification and
19039  * canonicalization. It is necessary, because we will be comparing it
19040  * to similarly-processed partition constraint expressions, and may
19041  * fail to detect valid matches without this.
19042  */
19043  cexpr = eval_const_expressions(NULL, cexpr);
19044  cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
19045 
19046  existConstraint = list_concat(existConstraint,
19047  make_ands_implicit((Expr *) cexpr));
19048  }
19049 
19050  /*
19051  * Try to make the proof. Since we are comparing CHECK constraints, we
19052  * need to use weak implication, i.e., we assume existConstraint is
19053  * not-false and try to prove the same for testConstraint.
19054  *
19055  * Note that predicate_implied_by assumes its first argument is known
19056  * immutable. That should always be true for both NOT NULL and partition
19057  * constraints, so we don't test it here.
19058  */
19059  return predicate_implied_by(testConstraint, existConstraint, true);
19060 }
for(;;)
List * list_copy(const List *oldlist)
Definition: list.c:1573
List * make_ands_implicit(Expr *clause)
Definition: makefuncs.c:737
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 ccvalid
Definition: tupdesc.h:32
char * ccbin
Definition: tupdesc.h:31

References canonicalize_qual(), ConstrCheck::ccbin, 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 16379 of file tablecmds.c.

16380 {
16383 
16384  if (acon->condeferrable != bcon->condeferrable ||
16385  acon->condeferred != bcon->condeferred ||
16386  strcmp(decompile_conbin(a, tupleDesc),
16387  decompile_conbin(b, tupleDesc)) != 0)
16388  return false;
16389  else
16390  return true;
16391 }
int b
Definition: isn.c:70
int a
Definition: isn.c:69
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:16354

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

12656 {
12657  ObjectAddress trigAddress;
12658  CreateTrigStmt *fk_trigger;
12659 
12660  /*
12661  * Note: for a self-referential FK (referencing and referenced tables are
12662  * the same), it is important that the ON UPDATE action fires before the
12663  * CHECK action, since both triggers will fire on the same row during an
12664  * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12665  * state of the row. Triggers fire in name order, so we ensure this by
12666  * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12667  * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12668  */
12669  fk_trigger = makeNode(CreateTrigStmt);
12670  fk_trigger->replace = false;
12671  fk_trigger->isconstraint = true;
12672  fk_trigger->trigname = "RI_ConstraintTrigger_c";
12673  fk_trigger->relation = NULL;
12674 
12675  /* Either ON INSERT or ON UPDATE */
12676  if (on_insert)
12677  {
12678  fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12679  fk_trigger->events = TRIGGER_TYPE_INSERT;
12680  }
12681  else
12682  {
12683  fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12684  fk_trigger->events = TRIGGER_TYPE_UPDATE;
12685  }
12686 
12687  fk_trigger->args = NIL;
12688  fk_trigger->row = true;
12689  fk_trigger->timing = TRIGGER_TYPE_AFTER;
12690  fk_trigger->columns = NIL;
12691  fk_trigger->whenClause = NULL;
12692  fk_trigger->transitionRels = NIL;
12693  fk_trigger->deferrable = fkconstraint->deferrable;
12694  fk_trigger->initdeferred = fkconstraint->initdeferred;
12695  fk_trigger->constrrel = NULL;
12696 
12697  trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12698  constraintOid, indexOid, InvalidOid,
12699  parentTrigOid, NULL, true, false);
12700 
12701  /* Make changes-so-far visible */
12703 
12704  return trigAddress.objectId;
12705 }
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:158

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

Referenced by createForeignKeyCheckTriggers().

◆ createForeignKeyActionTriggers()

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

Definition at line 12716 of file tablecmds.c.

12720 {
12721  CreateTrigStmt *fk_trigger;
12722  ObjectAddress trigAddress;
12723 
12724  /*
12725  * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12726  * DELETE action on the referenced table.
12727  */
12728  fk_trigger = makeNode(CreateTrigStmt);
12729  fk_trigger->replace = false;
12730  fk_trigger->isconstraint = true;
12731  fk_trigger->trigname = "RI_ConstraintTrigger_a";
12732  fk_trigger->relation = NULL;
12733  fk_trigger->args = NIL;
12734  fk_trigger->row = true;
12735  fk_trigger->timing = TRIGGER_TYPE_AFTER;
12736  fk_trigger->events = TRIGGER_TYPE_DELETE;
12737  fk_trigger->columns = NIL;
12738  fk_trigger->whenClause = NULL;
12739  fk_trigger->transitionRels = NIL;
12740  fk_trigger->constrrel = NULL;
12741 
12742  switch (fkconstraint->fk_del_action)
12743  {
12745  fk_trigger->deferrable = fkconstraint->deferrable;
12746  fk_trigger->initdeferred = fkconstraint->initdeferred;
12747  fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12748  break;
12750  fk_trigger->deferrable = false;
12751  fk_trigger->initdeferred = false;
12752  fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12753  break;
12755  fk_trigger->deferrable = false;
12756  fk_trigger->initdeferred = false;
12757  fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12758  break;
12760  fk_trigger->deferrable = false;
12761  fk_trigger->initdeferred = false;
12762  fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12763  break;
12765  fk_trigger->deferrable = false;
12766  fk_trigger->initdeferred = false;
12767  fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12768  break;
12769  default:
12770  elog(ERROR, "unrecognized FK action type: %d",
12771  (int) fkconstraint->fk_del_action);
12772  break;
12773  }
12774 
12775  trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12776  RelationGetRelid(rel),
12777  constraintOid, indexOid, InvalidOid,
12778  parentDelTrigger, NULL, true, false);
12779  if (deleteTrigOid)
12780  *deleteTrigOid = trigAddress.objectId;
12781 
12782  /* Make changes-so-far visible */
12784 
12785  /*
12786  * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12787  * UPDATE action on the referenced table.
12788  */
12789  fk_trigger = makeNode(CreateTrigStmt);
12790  fk_trigger->replace = false;
12791  fk_trigger->isconstraint = true;
12792  fk_trigger->trigname = "RI_ConstraintTrigger_a";
12793  fk_trigger->relation = NULL;
12794  fk_trigger->args = NIL;
12795  fk_trigger->row = true;
12796  fk_trigger->timing = TRIGGER_TYPE_AFTER;
12797  fk_trigger->events = TRIGGER_TYPE_UPDATE;
12798  fk_trigger->columns = NIL;
12799  fk_trigger->whenClause = NULL;
12800  fk_trigger->transitionRels = NIL;
12801  fk_trigger->constrrel = NULL;
12802 
12803  switch (fkconstraint->fk_upd_action)
12804  {
12806  fk_trigger->deferrable = fkconstraint->deferrable;
12807  fk_trigger->initdeferred = fkconstraint->initdeferred;
12808  fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12809  break;
12811  fk_trigger->deferrable = false;
12812  fk_trigger->initdeferred = false;
12813  fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12814  break;
12816  fk_trigger->deferrable = false;
12817  fk_trigger->initdeferred = false;
12818  fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12819  break;
12821  fk_trigger->deferrable = false;
12822  fk_trigger->initdeferred = false;
12823  fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12824  break;
12826  fk_trigger->deferrable = false;
12827  fk_trigger->initdeferred = false;
12828  fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12829  break;
12830  default:
12831  elog(ERROR, "unrecognized FK action type: %d",
12832  (int) fkconstraint->fk_upd_action);
12833  break;
12834  }
12835 
12836  trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12837  RelationGetRelid(rel),
12838  constraintOid, indexOid, InvalidOid,
12839  parentUpdTrigger, NULL, true, false);
12840  if (updateTrigOid)
12841  *updateTrigOid = trigAddress.objectId;
12842 }
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2726
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2725

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

Referenced by addFkRecurseReferenced(), and DetachPartitionFinalize().

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

12858 {
12859  *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12860  constraintOid, indexOid,
12861  parentInsTrigger, true);
12862  *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12863  constraintOid, indexOid,
12864  parentUpdTrigger, false);
12865 }
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:12653

References CreateFKCheckTrigger().

Referenced by addFkRecurseReferencing().

◆ CreateInheritance()

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

Definition at line 16285 of file tablecmds.c.

16286 {
16287  Relation catalogRelation;
16288  SysScanDesc scan;
16289  ScanKeyData key;
16290  HeapTuple inheritsTuple;
16291  int32 inhseqno;
16292 
16293  /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16294  catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16295 
16296  /*
16297  * Check for duplicates in the list of parents, and determine the highest
16298  * inhseqno already present; we'll use the next one for the new parent.
16299  * Also, if proposed child is a partition, it cannot already be
16300  * inheriting.
16301  *
16302  * Note: we do not reject the case where the child already inherits from
16303  * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16304  */
16305  ScanKeyInit(&key,
16306  Anum_pg_inherits_inhrelid,
16307  BTEqualStrategyNumber, F_OIDEQ,
16308  ObjectIdGetDatum(RelationGetRelid(child_rel)));
16309  scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16310  true, NULL, 1, &key);
16311 
16312  /* inhseqno sequences start at 1 */
16313  inhseqno = 0;
16314  while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16315  {
16316  Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16317 
16318  if (inh->inhparent == RelationGetRelid(parent_rel))
16319  ereport(ERROR,
16320  (errcode(ERRCODE_DUPLICATE_TABLE),
16321  errmsg("relation \"%s\" would be inherited from more than once",
16322  RelationGetRelationName(parent_rel))));
16323 
16324  if (inh->inhseqno > inhseqno)
16325  inhseqno = inh->inhseqno;
16326  }
16327  systable_endscan(scan);
16328 
16329  /* Match up the columns and bump attinhcount as needed */
16330  MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16331 
16332  /* Match up the constraints and bump coninhcount as needed */
16333  MergeConstraintsIntoExisting(child_rel, parent_rel);
16334 
16335  /*
16336  * OK, it looks valid. Make the catalog entries that show inheritance.
16337  */
16339  RelationGetRelid(parent_rel),
16340  inhseqno + 1,
16341  catalogRelation,
16342  parent_rel->rd_rel->relkind ==
16343  RELKIND_PARTITIONED_TABLE);
16344 
16345  /* Now we're done with pg_inherits */
16346  table_close(catalogRelation, RowExclusiveLock);
16347 }
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:16408
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3513
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:16538

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 void createPartitionTable ( RangeVar newPartName,
RangeVar modelRelName,
AlterTableUtilityContext context 
)
static

Definition at line 21192 of file tablecmds.c.

21194 {
21195  CreateStmt *createStmt;
21196  TableLikeClause *tlc;
21197  PlannedStmt *wrapper;
21198 
21199  createStmt = makeNode(CreateStmt);
21200  createStmt->relation = newPartName;
21201  createStmt->tableElts = NIL;
21202  createStmt->inhRelations = NIL;
21203  createStmt->constraints = NIL;
21204  createStmt->options = NIL;
21205  createStmt->oncommit = ONCOMMIT_NOOP;
21206  createStmt->tablespacename = NULL;
21207  createStmt->if_not_exists = false;
21208 
21209  tlc = makeNode(TableLikeClause);
21210  tlc->relation = modelRelName;
21211 
21212  /*
21213  * Indexes will be inherited on "attach new partitions" stage, after data
21214  * moving.
21215  */
21217  tlc->relationOid = InvalidOid;
21218  createStmt->tableElts = lappend(createStmt->tableElts, tlc);
21219 
21220  /* Need to make a wrapper PlannedStmt. */
21221  wrapper = makeNode(PlannedStmt);
21222  wrapper->commandType = CMD_UTILITY;
21223  wrapper->canSetTag = false;
21224  wrapper->utilityStmt = (Node *) createStmt;
21225  wrapper->stmt_location = context->pstmt->stmt_location;
21226  wrapper->stmt_len = context->pstmt->stmt_len;
21227 
21228  ProcessUtility(wrapper,
21229  context->queryString,
21230  false,
21232  NULL,
21233  NULL,
21234  None_Receiver,
21235  NULL);
21236 }
DestReceiver * None_Receiver
Definition: dest.c:96
@ CMD_UTILITY
Definition: nodes.h:270
@ CREATE_TABLE_LIKE_IDENTITY
Definition: parsenodes.h:766
@ CREATE_TABLE_LIKE_ALL
Definition: parsenodes.h:770
@ CREATE_TABLE_LIKE_INDEXES
Definition: parsenodes.h:767
@ ONCOMMIT_NOOP
Definition: primnodes.h:57
List * tableElts
Definition: parsenodes.h:2659
OnCommitAction oncommit
Definition: parsenodes.h:2668
List * options
Definition: parsenodes.h:2667
bool if_not_exists
Definition: parsenodes.h:2671
List * inhRelations
Definition: parsenodes.h:2660
RangeVar * relation
Definition: parsenodes.h:2658
char * tablespacename
Definition: parsenodes.h:2669
List * constraints
Definition: parsenodes.h:2665
bool canSetTag
Definition: plannodes.h:60
ParseLoc stmt_len
Definition: plannodes.h:99
ParseLoc stmt_location
Definition: plannodes.h:98
CmdType commandType
Definition: plannodes.h:52
Node * utilityStmt
Definition: plannodes.h:95
RangeVar * relation
Definition: parsenodes.h:754
void ProcessUtility(PlannedStmt *pstmt, const char *queryString, bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment *queryEnv, DestReceiver *dest, QueryCompletion *qc)
Definition: utility.c:499
@ PROCESS_UTILITY_SUBCOMMAND
Definition: utility.h:26

References PlannedStmt::canSetTag, CMD_UTILITY, PlannedStmt::commandType, CreateStmt::constraints, context, CREATE_TABLE_LIKE_ALL, CREATE_TABLE_LIKE_IDENTITY, CREATE_TABLE_LIKE_INDEXES, CreateStmt::if_not_exists, CreateStmt::inhRelations, InvalidOid, lappend(), makeNode, NIL, None_Receiver, CreateStmt::oncommit, ONCOMMIT_NOOP, TableLikeClause::options, CreateStmt::options, PROCESS_UTILITY_SUBCOMMAND, ProcessUtility(), TableLikeClause::relation, CreateStmt::relation, TableLikeClause::relationOid, PlannedStmt::stmt_len, PlannedStmt::stmt_location, CreateStmt::tableElts, CreateStmt::tablespacename, and PlannedStmt::utilityStmt.

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ createSplitPartitionContext()

static SplitPartitionContext* createSplitPartitionContext ( Relation  partRel)
static

Definition at line 20951 of file tablecmds.c.

20952 {
20954 
20956  pc->partRel = partRel;
20957 
20958  /*
20959  * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
20960  * don't bother using it.
20961  */
20962  pc->bistate = GetBulkInsertState();
20963 
20964  /* Create tuple slot for new partition. */
20968 
20969  return pc;
20970 }
TupleTableSlot * dstslot
Definition: tablecmds.c:20942
BulkInsertState bistate
Definition: tablecmds.c:20941

References SplitPartitionContext::bistate, SplitPartitionContext::dstslot, ExecStoreAllNullTuple(), GetBulkInsertState(), MakeSingleTupleTableSlot(), palloc0(), SplitPartitionContext::partRel, RelationGetDescr, and table_slot_callbacks().

Referenced by moveSplitTableRows().

◆ decompile_conbin()

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

Definition at line 16354 of file tablecmds.c.

16355 {
16356  Form_pg_constraint con;
16357  bool isnull;
16358  Datum attr;
16359  Datum expr;
16360 
16361  con = (Form_pg_constraint) GETSTRUCT(contup);
16362  attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16363  if (isnull)
16364  elog(ERROR, "null conbin for constraint %u", con->oid);
16365 
16366  expr = DirectFunctionCall2(pg_get_expr, attr,
16367  ObjectIdGetDatum(con->conrelid));
16368  return TextDatumGetCString(expr);
16369 }
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:644
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition: ruleutils.c:2655

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

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

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, 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_local, CookedConstraint::is_no_inherit, lappend(), lappend_oid(), lfirst, lfirst_int, lfirst_oid, linitial_oid, list_concat(), list_free(), list_length(), list_member_oid(), make_parsestate(), MergeAttributes(), RawColumnDefault::missingMode, MyDatabaseTableSpace, CookedConstraint::name, NAMEDATALEN, NIL, NoLock, object_aclcheck(), OBJECT_TABLESPACE, ObjectAddressSet, OidIsValid, ONCOMMIT_NOOP, ParseState::p_sourcetext, palloc(), PARTITION_MAX_KEYS, partitioned_table_reloptions(), RangeVarGetAndCheckCreationNamespace(), RangeVarGetRelid, RawColumnDefault::raw_default, ColumnDef::raw_default, RelationData::rd_index, RelationData::rd_rel, relation_close(), relation_open(), RelationGetDescr, RelationGetIndexList(), RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, relname, set_attnotnull(), ShareUpdateExclusiveLock, CookedConstraint::skip_validation, stmt, StoreCatalogInheritance(), StorePartitionBound(), StorePartitionKey(), strlcpy(), table_close(), table_open(), transformPartitionBound(), transformPartitionSpec(), transformRelOptions(), RelationData::trigdesc, TupleDescAttr, typenameTypeId(), and view_reloptions().

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

◆ deleteSplitPartitionContext()

static void deleteSplitPartitionContext ( SplitPartitionContext pc,
int  ti_options 
)
static

◆ DetachAddConstraintIfNeeded()

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

Definition at line 20284 of file tablecmds.c.

20285 {
20286  List *constraintExpr;
20287 
20288  constraintExpr = RelationGetPartitionQual(partRel);
20289  constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20290 
20291  /*
20292  * Avoid adding a new constraint if the needed constraint is implied by an
20293  * existing constraint
20294  */
20295  if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20296  {
20297  AlteredTableInfo *tab;
20298  Constraint *n;
20299 
20300  tab = ATGetQueueEntry(wqueue, partRel);
20301 
20302  /* Add constraint on partition, equivalent to the partition constraint */
20303  n = makeNode(Constraint);
20304  n->contype = CONSTR_CHECK;
20305  n->conname = NULL;
20306  n->location = -1;
20307  n->is_no_inherit = false;
20308  n->raw_expr = NULL;
20309  n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20310  n->initially_valid = true;
20311  n->skip_validation = true;
20312  /* It's a re-add, since it nominally already exists */
20313  ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20314  true, false, true, ShareUpdateExclusiveLock);
20315  }
20316 }
char * nodeToString(const void *obj)
Definition: outfuncs.c:791
char * cooked_expr
Definition: parsenodes.h:2748
Node * raw_expr
Definition: parsenodes.h:2746
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:18963

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

Referenced by ATExecDetachPartition().

◆ DetachPartitionFinalize()

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

Definition at line 20010 of file tablecmds.c.

20012 {
20013  Relation classRel;
20014  List *fks;
20015  ListCell *cell;
20016  List *indexes;
20017  Datum new_val[Natts_pg_class];
20018  bool new_null[Natts_pg_class],
20019  new_repl[Natts_pg_class];
20020  HeapTuple tuple,
20021  newtuple;
20022  Relation trigrel = NULL;
20023 
20024  if (concurrent)
20025  {
20026  /*
20027  * We can remove the pg_inherits row now. (In the non-concurrent case,
20028  * this was already done).
20029  */
20030  RemoveInheritance(partRel, rel, true);
20031  }
20032 
20033  /* Drop any triggers that were cloned on creation/attach. */
20035 
20036  /*
20037  * Detach any foreign keys that are inherited. This includes creating
20038  * additional action triggers.
20039  */
20040  fks = copyObject(RelationGetFKeyList(partRel));
20041  if (fks != NIL)
20042  trigrel = table_open(TriggerRelationId, RowExclusiveLock);
20043  foreach(cell, fks)
20044  {
20045  ForeignKeyCacheInfo *fk = lfirst(cell);
20046  HeapTuple contup;
20047  Form_pg_constraint conform;
20048  Constraint *fkconstraint;
20049  Oid insertTriggerOid,
20050  updateTriggerOid;
20051 
20052  contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
20053  if (!HeapTupleIsValid(contup))
20054  elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
20055  conform = (Form_pg_constraint) GETSTRUCT(contup);
20056 
20057  /* consider only the inherited foreign keys */
20058  if (conform->contype != CONSTRAINT_FOREIGN ||
20059  !OidIsValid(conform->conparentid))
20060  {
20061  ReleaseSysCache(contup);
20062  continue;
20063  }
20064 
20065  /* unset conparentid and adjust conislocal, coninhcount, etc. */
20067 
20068  /*
20069  * Also, look up the partition's "check" triggers corresponding to the
20070  * constraint being detached and detach them from the parent triggers.
20071  */
20073  fk->conoid, fk->confrelid, fk->conrelid,
20074  &insertTriggerOid, &updateTriggerOid);
20075  Assert(OidIsValid(insertTriggerOid));
20076  TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
20077  RelationGetRelid(partRel));
20078  Assert(OidIsValid(updateTriggerOid));
20079  TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
20080  RelationGetRelid(partRel));
20081 
20082  /*
20083  * Make the action triggers on the referenced relation. When this was
20084  * a partition the action triggers pointed to the parent rel (they
20085  * still do), but now we need separate ones of our own.
20086  */
20087  fkconstraint = makeNode(Constraint);
20088  fkconstraint->contype = CONSTRAINT_FOREIGN;
20089  fkconstraint->conname = pstrdup(NameStr(conform->conname));
20090  fkconstraint->deferrable = conform->condeferrable;
20091  fkconstraint->initdeferred = conform->condeferred;
20092  fkconstraint->location = -1;
20093  fkconstraint->pktable = NULL;
20094  fkconstraint->fk_attrs = NIL;
20095  fkconstraint->pk_attrs = NIL;
20096  fkconstraint->fk_matchtype = conform->confmatchtype;
20097  fkconstraint->fk_upd_action = conform->confupdtype;
20098  fkconstraint->fk_del_action = conform->confdeltype;
20099  fkconstraint->fk_del_set_cols = NIL;
20100  fkconstraint->old_conpfeqop = NIL;
20101  fkconstraint->old_pktable_oid = InvalidOid;
20102  fkconstraint->skip_validation = false;
20103  fkconstraint->initially_valid = true;
20104 
20105  createForeignKeyActionTriggers(partRel, conform->confrelid,
20106  fkconstraint, fk->conoid,
20107  conform->conindid,
20109  NULL, NULL);
20110 
20111  ReleaseSysCache(contup);
20112  }
20113  list_free_deep(fks);
20114  if (trigrel)
20115  table_close(trigrel, RowExclusiveLock);
20116 
20117  /*
20118  * Any sub-constraints that are in the referenced-side of a larger
20119  * constraint have to be removed. This partition is no longer part of the
20120  * key space of the constraint.
20121  */
20122  foreach(cell, GetParentedForeignKeyRefs(partRel))
20123  {
20124  Oid constrOid = lfirst_oid(cell);
20125  ObjectAddress constraint;
20126 
20128  deleteDependencyRecordsForClass(ConstraintRelationId,
20129  constrOid,
20130  ConstraintRelationId,
20133 
20134  ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
20135  performDeletion(&constraint, DROP_RESTRICT, 0);
20136  }
20137 
20138  /* Now we can detach indexes */
20139  indexes = RelationGetIndexList(partRel);
20140  foreach(cell, indexes)
20141  {
20142  Oid idxid = lfirst_oid(cell);
20143  Relation idx;
20144  Oid constrOid;
20145 
20146  if (!has_superclass(idxid))
20147  continue;
20148 
20149  Assert((IndexGetRelation(get_partition_parent(idxid, false), false) ==
20150  RelationGetRelid(rel)));
20151 
20154 
20155  /* If there's a constraint associated with the index, detach it too */
20157  idxid);
20158  if (OidIsValid(constrOid))
20160 
20162  }
20163 
20164  /* Update pg_class tuple */
20165  classRel = table_open(RelationRelationId, RowExclusiveLock);
20166  tuple = SearchSysCacheCopy1(RELOID,
20168  if (!HeapTupleIsValid(tuple))
20169  elog(ERROR, "cache lookup failed for relation %u",
20170  RelationGetRelid(partRel));
20171  Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
20172 
20173  /* Clear relpartbound and reset relispartition */
20174  memset(new_val, 0, sizeof(new_val));
20175  memset(new_null, false, sizeof(new_null));
20176  memset(new_repl, false, sizeof(new_repl));
20177  new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
20178  new_null[Anum_pg_class_relpartbound - 1] = true;
20179  new_repl[Anum_pg_class_relpartbound - 1] = true;
20180  newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
20181  new_val, new_null, new_repl);
20182 
20183  ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
20184  CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
20185  heap_freetuple(newtuple);
20186  table_close(classRel, RowExclusiveLock);
20187 
20188  /*
20189  * Drop identity property from all identity columns of partition.
20190  */
20191  for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
20192  {
20193  Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
20194 
20195  if (!attr->attisdropped && attr->attidentity)
20196  ATExecDropIdentity(partRel, NameStr(attr->attname), false,
20197  AccessExclusiveLock, true, true);
20198  }
20199 
20200  if (OidIsValid(defaultPartOid))
20201  {
20202  /*
20203  * If the relation being detached is the default partition itself,
20204  * remove it from the parent's pg_partitioned_table entry.
20205  *
20206  * If not, we must invalidate default partition's relcache entry, as
20207  * in StorePartitionBound: its partition constraint depends on every
20208  * other partition's partition constraint.
20209  */
20210  if (RelationGetRelid(partRel) == defaultPartOid)
20212  else
20213  CacheInvalidateRelcacheByRelid(defaultPartOid);
20214  }
20215 
20216  /*
20217  * Invalidate the parent's relcache so that the partition is no longer
20218  * included in its partition descriptor.
20219  */
20221 
20222  /*
20223  * If the partition we just detached is partitioned itself, invalidate
20224  * relcache for all descendent partitions too to ensure that their
20225  * rd_partcheck expression trees are rebuilt; must lock partitions before
20226  * doing so, using the same lockmode as what partRel has been locked with
20227  * by the caller.
20228  */
20229  if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20230  {
20231  List *children;
20232 
20233  children = find_all_inheritors(RelationGetRelid(partRel),
20234  AccessExclusiveLock, NULL);
20235  foreach(cell, children)
20236  {
20238  }
20239  }
20240 }
void list_free_deep(List *list)
Definition: list.c:1560
void update_default_partition_oid(Oid parentId, Oid defaultPartId)
Definition: partition.c:340
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:377
#define RelationGetNumberOfAttributes(relation)
Definition: rel.h:511
static void DropClonedTriggersFromPartition(Oid partitionId)
Definition: tablecmds.c:20325
void TriggerSetParentTrigger(Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
Definition: trigger.c:1216

References AccessExclusiveLock, Assert, ATExecDropIdentity(), CacheInvalidateRelcache(), CacheInvalidateRelcacheByRelid(), CatalogTupleUpdate(), CommandCounterIncrement(), ForeignKeyCacheInfo::confrelid, Constraint::conname, ForeignKeyCacheInfo::conoid, ForeignKeyCacheInfo::conrelid, ConstraintSetParentConstraint(), Constraint::contype, copyObject, createForeignKeyActionTriggers(), 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, get_partition_parent(), get_relation_idx_constraint_oid(), GetForeignKeyCheckTriggers(), GetParentedForeignKeyRefs(), GETSTRUCT, has_superclass(), heap_freetuple(), heap_modify_tuple(), HeapTupleIsValid, idx(), index_close(), index_open(), IndexGetRelation(), IndexSetParentIndex(), Constraint::initdeferred, Constraint::initially_valid, InvalidOid, lfirst, lfirst_oid, list_free_deep(), Constraint::location, makeNode, 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, Constraint::skip_validation, HeapTupleData::t_self, table_close(), table_open(), TriggerSetParentTrigger(), TupleDescAttr, and update_default_partition_oid().

Referenced by ATExecDetachPartition(), ATExecDetachPartitionFinalize(), ATExecMergePartitions(), and ATExecSplitPartition().

◆ drop_parent_dependency()

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

Definition at line 17103 of file tablecmds.c.

17105 {
17106  Relation catalogRelation;
17107  SysScanDesc scan;
17108  ScanKeyData key[3];
17109  HeapTuple depTuple;
17110 
17111  catalogRelation = table_open(DependRelationId, RowExclusiveLock);
17112 
17113  ScanKeyInit(&key[0],
17114  Anum_pg_depend_classid,
17115  BTEqualStrategyNumber, F_OIDEQ,
17116  ObjectIdGetDatum(RelationRelationId));
17117  ScanKeyInit(&key[1],
17118  Anum_pg_depend_objid,
17119  BTEqualStrategyNumber, F_OIDEQ,
17120  ObjectIdGetDatum(relid));
17121  ScanKeyInit(&key[2],
17122  Anum_pg_depend_objsubid,
17123  BTEqualStrategyNumber, F_INT4EQ,
17124  Int32GetDatum(0));
17125 
17126  scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
17127  NULL, 3, key);
17128 
17129  while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
17130  {
17131  Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
17132 
17133  if (dep->refclassid == refclassid &&
17134  dep->refobjid == refobjid &&
17135  dep->refobjsubid == 0 &&
17136  dep->deptype == deptype)
17137  CatalogTupleDelete(catalogRelation, &depTuple->t_self);
17138  }
17139 
17140  systable_endscan(scan);
17141  table_close(catalogRelation, RowExclusiveLock);
17142 }

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

20326 {
20327  ScanKeyData skey;
20328  SysScanDesc scan;
20329  HeapTuple trigtup;
20330  Relation tgrel;
20331  ObjectAddresses *objects;
20332 
20333  objects = new_object_addresses();
20334 
20335  /*
20336  * Scan pg_trigger to search for all triggers on this rel.
20337  */
20338  ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20339  F_OIDEQ, ObjectIdGetDatum(partitionId));
20340  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20341  scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20342  true, NULL, 1, &skey);
20343  while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20344  {
20345  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20346  ObjectAddress trig;
20347 
20348  /* Ignore triggers that weren't cloned */
20349  if (!OidIsValid(pg_trigger->tgparentid))
20350  continue;
20351 
20352  /*
20353  * Ignore internal triggers that are implementation objects of foreign
20354  * keys, because these will be detached when the foreign keys
20355  * themselves are.
20356  */
20357  if (OidIsValid(pg_trigger->tgconstrrelid))
20358  continue;
20359 
20360  /*
20361  * This is ugly, but necessary: remove the dependency markings on the
20362  * trigger so that it can be removed.
20363  */
20364  deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20365  TriggerRelationId,
20367  deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20368  RelationRelationId,
20370 
20371  /* remember this trigger to remove it below */
20372  ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20373  add_exact_object_address(&trig, objects);
20374  }
20375 
20376  /* make the dependency removal visible to the deletion below */
20379 
20380  /* done */
20381  free_object_addresses(objects);
20382  systable_endscan(scan);
20383  table_close(tgrel, RowExclusiveLock);
20384 }

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

Definition at line 12940 of file tablecmds.c.

12943 {
12944  Relation conrel;
12945  Form_pg_constraint con;
12946  ObjectAddress conobj;
12947  List *children;
12948  bool is_no_inherit_constraint = false;
12949  char *constrName;
12950  List *unconstrained_cols = NIL;
12951  char *colname = NULL;
12952  bool dropping_pk = false;
12953 
12954  if (list_member_oid(*readyRels, RelationGetRelid(rel)))
12955  return InvalidObjectAddress;
12956  *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
12957 
12958  /* Guard against stack overflow due to overly deep inheritance tree. */
12960 
12961  /* At top level, permission check was done in ATPrepCmd, else do it */
12962  if (recursing)
12964 
12965  conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12966 
12967  con = (Form_pg_constraint) GETSTRUCT(constraintTup);
12968  constrName = NameStr(con->conname);
12969 
12970  /*
12971  * If we're asked to drop a constraint which is both defined locally and
12972  * inherited, we can simply mark it as no longer having a local
12973  * definition, and no further changes are required.
12974  *
12975  * XXX We do this for not-null constraints only, not CHECK, because the
12976  * latter have historically not behaved this way and it might be confusing
12977  * to change the behavior now.
12978  */
12979  if (con->contype == CONSTRAINT_NOTNULL &&
12980  con->conislocal && con->coninhcount > 0)
12981  {
12982  HeapTuple copytup;
12983 
12984  copytup = heap_copytuple(constraintTup);
12985  con = (Form_pg_constraint) GETSTRUCT(copytup);
12986  con->conislocal = false;
12987  CatalogTupleUpdate(conrel, &copytup->t_self, copytup);
12988  ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
12989 
12991  table_close(conrel, RowExclusiveLock);
12992  return conobj;
12993  }
12994 
12995  /* Don't allow drop of inherited constraints */
12996  if (con->coninhcount > 0 && !recursing)
12997  ereport(ERROR,
12998  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12999  errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
13000  constrName, RelationGetRelationName(rel))));
13001 
13002  /*
13003  * See if we have a not-null constraint or a PRIMARY KEY. If so, we have
13004  * more checks and actions below, so obtain the list of columns that are
13005  * constrained by the constraint being dropped.
13006  */
13007  if (con->contype == CONSTRAINT_NOTNULL)
13008  {
13009  AttrNumber colnum;
13010 
13011  colnum = extractNotNullColumn(constraintTup);
13012  unconstrained_cols = list_make1_int(colnum);
13013  colname = NameStr(TupleDescAttr(RelationGetDescr(rel),
13014  colnum - 1)->attname);
13015  }
13016  else if (con->contype == CONSTRAINT_PRIMARY)
13017  {
13018  Datum adatum;
13019  ArrayType *arr;
13020  int numkeys;
13021  bool isNull;
13022  int16 *attnums;
13023 
13024  dropping_pk = true;
13025 
13026  adatum = heap_getattr(constraintTup, Anum_pg_constraint_conkey,
13027  RelationGetDescr(conrel), &isNull);
13028  if (isNull)
13029  elog(ERROR, "null conkey for constraint %u", con->oid);
13030  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
13031  numkeys = ARR_DIMS(arr)[0];
13032  if (ARR_NDIM(arr) != 1 ||
13033  numkeys < 0 ||
13034  ARR_HASNULL(arr) ||
13035  ARR_ELEMTYPE(arr) != INT2OID)
13036  elog(ERROR, "conkey is not a 1-D smallint array");
13037  attnums = (int16 *) ARR_DATA_PTR(arr);
13038 
13039  for (int i = 0; i < numkeys; i++)
13040  unconstrained_cols = lappend_int(unconstrained_cols, attnums[i]);
13041  }
13042 
13043  is_no_inherit_constraint = con->connoinherit;
13044 
13045  /*
13046  * If it's a foreign-key constraint, we'd better lock the referenced table
13047  * and check that that's not in use, just as we've already done for the
13048  * constrained table (else we might, eg, be dropping a trigger that has
13049  * unfired events). But we can/must skip that in the self-referential
13050  * case.
13051  */
13052  if (con->contype == CONSTRAINT_FOREIGN &&
13053  con->confrelid != RelationGetRelid(rel))
13054  {
13055  Relation frel;
13056 
13057  /* Must match lock taken by RemoveTriggerById: */
13058  frel = table_open(con->confrelid, AccessExclusiveLock);
13059  CheckTableNotInUse(frel, "ALTER TABLE");
13060  table_close(frel, NoLock);
13061  }
13062 
13063  /*
13064  * Perform the actual constraint deletion
13065  */
13066  ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13067  performDeletion(&conobj, behavior, 0);
13068 
13069  /*
13070  * If this was a NOT NULL or the primary key, verify that we still have
13071  * constraints to support GENERATED AS IDENTITY or the replica identity.
13072  */
13073  if (unconstrained_cols != NIL)
13074  {
13075  Relation attrel;
13076  Bitmapset *pkcols;
13077  Bitmapset *ircols;
13078 
13079  /* Make implicit attnotnull changes visible */
13081 
13082  attrel = table_open(AttributeRelationId, RowExclusiveLock);
13083 
13084  /*
13085  * We want to test columns for their presence in the primary key, but
13086  * only if we're not dropping it.
13087  */
13088  pkcols = dropping_pk ? NULL :
13092 
13093  foreach_int(attnum, unconstrained_cols)
13094  {
13095  HeapTuple atttup;
13096  HeapTuple contup;
13097  Form_pg_attribute attForm;
13098  char attidentity;
13099 
13100  /*
13101  * Obtain pg_attribute tuple and verify conditions on it.
13102  */
13104  if (!HeapTupleIsValid(atttup))
13105  elog(ERROR, "cache lookup failed for attribute %d of relation %u",
13106  attnum, RelationGetRelid(rel));
13107  attForm = (Form_pg_attribute) GETSTRUCT(atttup);
13108  attidentity = attForm->attidentity;
13109  ReleaseSysCache(atttup);
13110 
13111  /*
13112  * Since the above deletion has been made visible, we can now
13113  * search for any remaining constraints on this column (or these
13114  * columns, in the case we're dropping a multicol primary key.)
13115  * Then, verify whether any further NOT NULL or primary key
13116  * exists: if none and this is a generated identity column or the
13117  * replica identity, abort the whole thing.
13118  */
13120  if (contup ||
13122  pkcols))
13123  continue;
13124 
13125  /*
13126  * It's not valid to drop the not-null constraint for a GENERATED
13127  * AS IDENTITY column.
13128  */
13129  if (attidentity != '\0')
13130  ereport(ERROR,
13131  errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13132  errmsg("column \"%s\" of relation \"%s\" is an identity column",
13134  false),
13135  RelationGetRelationName(rel)));
13136 
13137  /*
13138  * It's not valid to drop the not-null constraint for a column in
13139  * the replica identity index, either. (FULL is not affected.)
13140  */
13142  ereport(ERROR,
13143  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13144  errmsg("column \"%s\" is in index used as replica identity",
13145  get_attname(RelationGetRelid(rel), attnum, false)));
13146  }
13147  table_close(attrel, RowExclusiveLock);
13148  }
13149 
13150  /*
13151  * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13152  * are dropped via the dependency mechanism, so we're done here.
13153  */
13154  if (con->contype != CONSTRAINT_CHECK &&
13155  con->contype != CONSTRAINT_NOTNULL &&
13156  rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13157  {
13158  table_close(conrel, RowExclusiveLock);
13159  return conobj;
13160  }
13161 
13162  /*
13163  * Propagate to children as appropriate. Unlike most other ALTER
13164  * routines, we have to do this one level of recursion at a time; we can't
13165  * use find_all_inheritors to do it in one pass.
13166  */
13167  if (!is_no_inherit_constraint)
13168  children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13169  else
13170  children = NIL;
13171 
13172  /*
13173  * For a partitioned table, if partitions exist and we are told not to
13174  * recurse, it's a user error. It doesn't make sense to have a constraint
13175  * be defined only on the parent, especially if it's a partitioned table.
13176  */
13177  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
13178  children != NIL && !recurse)
13179  ereport(ERROR,
13180  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13181  errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
13182  errhint("Do not specify the ONLY keyword.")));
13183 
13184  foreach_oid(childrelid, children)
13185  {
13186  Relation childrel;
13187  HeapTuple tuple;
13188  Form_pg_constraint childcon;
13189 
13190  if (list_member_oid(*readyRels, childrelid))
13191  continue; /* child already processed */
13192 
13193  /* find_inheritance_children already got lock */
13194  childrel = table_open(childrelid, NoLock);
13195  CheckTableNotInUse(childrel, "ALTER TABLE");
13196 
13197  /*
13198  * We search for not-null constraints by column name, and others by
13199  * constraint name.
13200  */
13201  if (con->contype == CONSTRAINT_NOTNULL)
13202  {
13203  tuple = findNotNullConstraint(childrelid, colname);
13204  if (!HeapTupleIsValid(tuple))
13205  elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13206  colname, RelationGetRelid(childrel));
13207  }
13208  else
13209  {
13210  SysScanDesc scan;
13211  ScanKeyData skey[3];
13212 
13213  ScanKeyInit(&skey[0],
13214  Anum_pg_constraint_conrelid,
13215  BTEqualStrategyNumber, F_OIDEQ,
13216  ObjectIdGetDatum(childrelid));
13217  ScanKeyInit(&skey[1],
13218  Anum_pg_constraint_contypid,
13219  BTEqualStrategyNumber, F_OIDEQ,
13221  ScanKeyInit(&skey[2],
13222  Anum_pg_constraint_conname,
13223  BTEqualStrategyNumber, F_NAMEEQ,
13224  CStringGetDatum(constrName));
13225  scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13226  true, NULL, 3, skey);
13227  /* There can only be one, so no need to loop */
13228  tuple = systable_getnext(scan);
13229  if (!HeapTupleIsValid(tuple))
13230  ereport(ERROR,
13231  (errcode(ERRCODE_UNDEFINED_OBJECT),
13232  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13233  constrName,
13234  RelationGetRelationName(childrel))));
13235  tuple = heap_copytuple(tuple);
13236  systable_endscan(scan);
13237  }
13238 
13239  childcon = (Form_pg_constraint) GETSTRUCT(tuple);
13240 
13241  /* Right now only CHECK and not-null constraints can be inherited */
13242  if (childcon->contype != CONSTRAINT_CHECK &&
13243  childcon->contype != CONSTRAINT_NOTNULL)
13244  elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
13245 
13246  if (childcon->coninhcount <= 0) /* shouldn't happen */
13247  elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13248  childrelid, NameStr(childcon->conname));
13249 
13250  if (recurse)
13251  {
13252  /*
13253  * If the child constraint has other definition sources, just
13254  * decrement its inheritance count; if not, recurse to delete it.
13255  */
13256  if (childcon->coninhcount == 1 && !childcon->conislocal)
13257  {
13258  /* Time to delete this child constraint, too */
13259  dropconstraint_internal(childrel, tuple, behavior,
13260  recurse, true, missing_ok, readyRels,
13261  lockmode);
13262  }
13263  else
13264  {
13265  /* Child constraint must survive my deletion */
13266  childcon->coninhcount--;
13267  CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13268 
13269  /* Make update visible */
13271  }
13272  }
13273  else
13274  {
13275  /*
13276  * If we were told to drop ONLY in this table (no recursion) and
13277  * there are no further parents for this constraint, we need to
13278  * mark the inheritors' constraints as locally defined rather than
13279  * inherited.
13280  */
13281  childcon->coninhcount--;
13282  if (childcon->coninhcount == 0)
13283  childcon->conislocal = true;
13284 
13285  CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13286 
13287  /* Make update visible */
13289  }
13290 
13291  heap_freetuple(tuple);
13292 
13293  table_close(childrel, NoLock);
13294  }
13295 
13296  /*
13297  * In addition, when dropping a primary key from a legacy-inheritance
13298  * parent table, we must recurse to children to mark the corresponding NOT
13299  * NULL constraint as no longer inherited, or drop it if this its last
13300  * reference.
13301  */
13302  if (con->contype == CONSTRAINT_PRIMARY &&
13303  rel->rd_rel->relkind == RELKIND_RELATION &&
13304  rel->rd_rel->relhassubclass)
13305  {
13306  List *colnames = NIL;
13307  List *pkready = NIL;
13308 
13309  /*
13310  * Because primary keys are always marked as NO INHERIT, we don't have
13311  * a list of children yet, so obtain one now.
13312  */
13313  children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13314 
13315  /*
13316  * Find out the list of column names to process. Fortunately, we
13317  * already have the list of column numbers.
13318  */
13319  foreach_int(attnum, unconstrained_cols)
13320  {
13321  colnames = lappend(colnames, get_attname(RelationGetRelid(rel),
13322  attnum, false));
13323  }
13324 
13325  foreach_oid(childrelid, children)
13326  {
13327  Relation childrel;
13328 
13329  if (list_member_oid(pkready, childrelid))
13330  continue; /* child already processed */
13331 
13332  /* find_inheritance_children already got lock */
13333  childrel = table_open(childrelid, NoLock);
13334  CheckTableNotInUse(childrel, "ALTER TABLE");
13335 
13336  foreach_ptr(char, colName, colnames)
13337  {
13338  HeapTuple contup;
13339 
13340  contup = findNotNullConstraint(childrelid, colName);
13341  if (contup == NULL)
13342  elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\", relation \"%s\"",
13343  colName, RelationGetRelationName(childrel));
13344 
13345  dropconstraint_internal(childrel, contup,
13346  DROP_RESTRICT, true, true,
13347  false, &pkready,
13348  lockmode);
13349  pkready = NIL;
13350  }
13351 
13352  table_close(childrel, NoLock);
13353 
13354  pkready = lappend_oid(pkready, childrelid);
13355  }
13356  }
13357 
13358  table_close(conrel, RowExclusiveLock);
13359 
13360  return conobj;
13361 }
#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
HeapTuple findNotNullConstraintAttnum(Oid relid, AttrNumber attnum)
#define foreach_ptr(type, var, lst)
Definition: pg_list.h:469
#define list_make1_int(x1)
Definition: pg_list.h:227
#define foreach_int(var, lst)
Definition: pg_list.h:470

References AccessExclusiveLock, ARR_DATA_PTR, ARR_DIMS, ARR_ELEMTYPE, ARR_HASNULL, ARR_NDIM, AT_DropConstraint, ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_TABLE, attname, attnum, bms_is_member(), BTEqualStrategyNumber, CatalogTupleUpdate(), check_stack_depth(), CheckTableNotInUse(), CommandCounterIncrement(), CStringGetDatum(), DatumGetArrayTypeP, DROP_RESTRICT, elog, ereport, errcode(), errhint(), errmsg(), ERROR, extractNotNullColumn(), find_inheritance_children(), findNotNullConstraint(), findNotNullConstraintAttnum(), FirstLowInvalidHeapAttributeNumber, foreach_int, foreach_oid, foreach_ptr, get_attname(), GETSTRUCT, heap_copytuple(), heap_freetuple(), heap_getattr(), HeapTupleIsValid, i, INDEX_ATTR_BITMAP_IDENTITY_KEY, INDEX_ATTR_BITMAP_PRIMARY_KEY, InvalidObjectAddress, InvalidOid, lappend(), lappend_int(), lappend_oid(), list_make1_int, list_member_oid(), NameStr, NIL, NoLock, ObjectAddressSet, ObjectIdGetDatum(), performDeletion(), RelationData::rd_rel, RelationGetDescr, RelationGetIndexAttrBitmap(), RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, ScanKeyInit(), SearchSysCacheAttNum(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), table_open(), and TupleDescAttr.

Referenced by ATExecDropConstraint(), and ATExecDropNotNull().

◆ DropErrorMsgNonExistent()

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

Definition at line 1409 of file tablecmds.c.

1410 {
1411  const struct dropmsgstrings *rentry;
1412 
1413  if (rel->schemaname != NULL &&
1415  {
1416  if (!missing_ok)
1417  {
1418  ereport(ERROR,
1419  (errcode(ERRCODE_UNDEFINED_SCHEMA),
1420  errmsg("schema \"%s\" does not exist", rel->schemaname)));
1421  }
1422  else
1423  {
1424  ereport(NOTICE,
1425  (errmsg("schema \"%s\" does not exist, skipping",
1426  rel->schemaname)));
1427  }
1428  return;
1429  }
1430 
1431  for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1432  {
1433  if (rentry->kind == rightkind)
1434  {
1435  if (!missing_ok)
1436  {
1437  ereport(ERROR,
1438  (errcode(rentry->nonexistent_code),
1439  errmsg(rentry->nonexistent_msg, rel->relname)));
1440  }
1441  else
1442  {
1443  ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1444  break;
1445  }
1446  }
1447  }
1448 
1449  Assert(rentry->kind != '\0'); /* Should be impossible */
1450 }
Oid LookupNamespaceNoError(const char *nspname)
Definition: namespace.c:3340
char * schemaname
Definition: primnodes.h:79
const char * skipping_msg
Definition: tablecmds.c:248
int nonexistent_code
Definition: tablecmds.c:246
const char * nonexistent_msg
Definition: tablecmds.c:247
static const struct dropmsgstrings dropmsgstringarray[]
Definition: tablecmds.c:253

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

1458 {
1459  const struct dropmsgstrings *rentry;
1460  const struct dropmsgstrings *wentry;
1461 
1462  for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1463  if (rentry->kind == rightkind)
1464  break;
1465  Assert(rentry->kind != '\0');
1466 
1467  for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1468  if (wentry->kind == wrongkind)
1469  break;
1470  /* wrongkind could be something we don't have in our table... */
1471 
1472  ereport(ERROR,
1473  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1474  errmsg(rentry->nota_msg, relname),
1475  (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1476 }
const char * drophint_msg
Definition: tablecmds.c:250
const char * nota_msg
Definition: tablecmds.c:249

References _, Assert, dropmsgstrings::drophint_msg, dropmsgstringarray, ereport, errcode(), errhint(), errmsg(), ERROR, dropmsgstrings::kind, dropmsgstrings::nota_msg, and relname.

Referenced by RangeVarCallbackForDropRelation().

◆ ExecuteTruncate()

void ExecuteTruncate ( TruncateStmt stmt)

Definition at line 1807 of file tablecmds.c.

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

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

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

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

Referenced by apply_handle_truncate(), and ExecuteTruncate().

◆ find_composite_type_dependencies()

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

Definition at line 6807 of file tablecmds.c.

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

References AccessShareLock, BTEqualStrategyNumber, check_stack_depth(), ereport, errcode(), errmsg(), ERROR, 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(), 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 6965 of file tablecmds.c.

6966 {
6967  Relation classRel;
6968  ScanKeyData key[1];
6969  TableScanDesc scan;
6970  HeapTuple tuple;
6971  List *result = NIL;
6972 
6973  classRel = table_open(RelationRelationId, AccessShareLock);
6974 
6975  ScanKeyInit(&key[0],
6976  Anum_pg_class_reloftype,
6977  BTEqualStrategyNumber, F_OIDEQ,
6978  ObjectIdGetDatum(typeOid));
6979 
6980  scan = table_beginscan_catalog(classRel, 1, key);
6981 
6982  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6983  {
6984  Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6985 
6986  if (behavior == DROP_RESTRICT)
6987  ereport(ERROR,
6988  (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6989  errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6990  typeName),
6991  errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6992  else
6993  result = lappend_oid(result, classform->oid);
6994  }
6995 
6996  table_endscan(scan);
6997  table_close(classRel, AccessShareLock);
6998 
6999  return result;
7000 }

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

3559 {
3560  ListCell *lc;
3561  int i = 1;
3562 
3563  foreach(lc, columns)
3564  {
3565  if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3566  return i;
3567 
3568  i++;
3569  }
3570  return 0;
3571 }

References i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 12495 of file tablecmds.c.

12496 {
12497  CoercionPathType ret;
12498 
12499  if (targetTypeId == sourceTypeId)
12500  {
12502  *funcid = InvalidOid;
12503  }
12504  else
12505  {
12506  ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12507  COERCION_IMPLICIT, funcid);
12508  if (ret == COERCION_PATH_NONE)
12509  /* A previously-relied-upon cast is now gone. */
12510  elog(ERROR, "could not find cast from %u to %u",
12511  sourceTypeId, targetTypeId);
12512  }
12513 
12514  return ret;
12515 }
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 20863 of file tablecmds.c.

20864 {
20865  char cmethod;
20866 
20867  if (compression == NULL || strcmp(compression, "default") == 0)
20868  return InvalidCompressionMethod;
20869 
20870  /*
20871  * To specify a nondefault method, the column data type must be toastable.
20872  * Note this says nothing about whether the column's attstorage setting
20873  * permits compression; we intentionally allow attstorage and
20874  * attcompression to be independent. But with a non-toastable type,
20875  * attstorage could not be set to a value that would permit compression.
20876  *
20877  * We don't actually need to enforce this, since nothing bad would happen
20878  * if attcompression were non-default; it would never be consulted. But
20879  * it seems more user-friendly to complain about a certainly-useless
20880  * attempt to set the property.
20881  */
20882  if (!TypeIsToastable(atttypid))
20883  ereport(ERROR,
20884  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20885  errmsg("column data type %s does not support compression",
20886  format_type_be(atttypid))));
20887 
20888  cmethod = CompressionNameToMethod(compression);
20889  if (!CompressionMethodIsValid(cmethod))
20890  ereport(ERROR,
20891  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20892  errmsg("invalid compression method \"%s\"", compression)));
20893 
20894  return cmethod;
20895 }
#define TypeIsToastable(typid)
Definition: lsyscache.h:213
char CompressionNameToMethod(const char *compression)
#define CompressionMethodIsValid(cm)

References CompressionMethodIsValid, CompressionNameToMethod(), ereport, errcode(), errmsg(), ERROR, format_type_be(), InvalidCompressionMethod, and TypeIsToastable.

Referenced by ATExecSetCompression(), and BuildDescForRelation().

◆ GetAttributeStorage()

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

Definition at line 20901 of file tablecmds.c.

20902 {
20903  char cstorage = 0;
20904 
20905  if (pg_strcasecmp(storagemode, "plain") == 0)
20906  cstorage = TYPSTORAGE_PLAIN;
20907  else if (pg_strcasecmp(storagemode, "external") == 0)
20908  cstorage = TYPSTORAGE_EXTERNAL;
20909  else if (pg_strcasecmp(storagemode, "extended") == 0)
20910  cstorage = TYPSTORAGE_EXTENDED;
20911  else if (pg_strcasecmp(storagemode, "main") == 0)
20912  cstorage = TYPSTORAGE_MAIN;
20913  else if (pg_strcasecmp(storagemode, "default") == 0)
20914  cstorage = get_typstorage(atttypid);
20915  else
20916  ereport(ERROR,
20917  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20918  errmsg("invalid storage type \"%s\"",
20919  storagemode)));
20920 
20921  /*
20922  * safety check: do not allow toasted storage modes unless column datatype
20923  * is TOAST-aware.
20924  */
20925  if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20926  ereport(ERROR,
20927  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20928  errmsg("column data type %s can only have storage PLAIN",
20929  format_type_be(atttypid))));
20930 
20931  return cstorage;
20932 }
char get_typstorage(Oid typid)
Definition: lsyscache.c:2419
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36

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

Referenced by ATExecSetStorage(), and BuildDescForRelation().

◆ GetForeignKeyActionTriggers()

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

Definition at line 11585 of file tablecmds.c.

11589 {
11590  ScanKeyData key;
11591  SysScanDesc scan;
11592  HeapTuple trigtup;
11593 
11594  *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11595  ScanKeyInit(&key,
11596  Anum_pg_trigger_tgconstraint,
11597  BTEqualStrategyNumber, F_OIDEQ,
11598  ObjectIdGetDatum(conoid));
11599 
11600  scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11601  NULL, 1, &key);
11602  while ((trigtup = systable_getnext(scan)) != NULL)
11603  {
11604  Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11605 
11606  if (trgform->tgconstrrelid != conrelid)
11607  continue;
11608  if (trgform->tgrelid != confrelid)
11609  continue;
11610  /* Only ever look at "action" triggers on the PK side. */
11611  if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11612  continue;
11613  if (TRIGGER_FOR_DELETE(trgform->tgtype))
11614  {
11615  Assert(*deleteTriggerOid == InvalidOid);
11616  *deleteTriggerOid = trgform->oid;
11617  }
11618  else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11619  {
11620  Assert(*updateTriggerOid == InvalidOid);
11621  *updateTriggerOid = trgform->oid;
11622  }
11623 #ifndef USE_ASSERT_CHECKING
11624  /* In an assert-enabled build, continue looking to find duplicates */
11625  if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11626  break;
11627 #endif
11628  }
11629 
11630  if (!OidIsValid(*deleteTriggerOid))
11631  elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11632  conoid);
11633  if (!OidIsValid(*updateTriggerOid))
11634  elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11635  conoid);
11636 
11637  systable_endscan(scan);
11638 }
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3123
#define RI_TRIGGER_PK
Definition: trigger.h:282

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

11650 {
11651  ScanKeyData key;
11652  SysScanDesc scan;
11653  HeapTuple trigtup;
11654 
11655  *insertTriggerOid = *updateTriggerOid = InvalidOid;
11656  ScanKeyInit(&key,
11657  Anum_pg_trigger_tgconstraint,
11658  BTEqualStrategyNumber, F_OIDEQ,
11659  ObjectIdGetDatum(conoid));
11660 
11661  scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11662  NULL, 1, &key);
11663  while ((trigtup = systable_getnext(scan)) != NULL)
11664  {
11665  Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11666 
11667  if (trgform->tgconstrrelid != confrelid)
11668  continue;
11669  if (trgform->tgrelid != conrelid)
11670  continue;
11671  /* Only ever look at "check" triggers on the FK side. */
11672  if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11673  continue;
11674  if (TRIGGER_FOR_INSERT(trgform->tgtype))
11675  {
11676  Assert(*insertTriggerOid == InvalidOid);
11677  *insertTriggerOid = trgform->oid;
11678  }
11679  else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11680  {
11681  Assert(*updateTriggerOid == InvalidOid);
11682  *updateTriggerOid = trgform->oid;
11683  }
11684 #ifndef USE_ASSERT_CHECKING
11685  /* In an assert-enabled build, continue looking to find duplicates. */
11686  if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11687  break;
11688 #endif
11689  }
11690 
11691  if (!OidIsValid(*insertTriggerOid))
11692  elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11693  conoid);
11694  if (!OidIsValid(*updateTriggerOid))
11695  elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11696  conoid);
11697 
11698  systable_endscan(scan);
11699 }
#define RI_TRIGGER_FK
Definition: trigger.h:283

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 CloneFkReferencing(), DetachPartitionFinalize(), and tryAttachPartitionForeignKey().

◆ GetParentedForeignKeyRefs()

static List * GetParentedForeignKeyRefs ( Relation  partition)
static

Definition at line 20761 of file tablecmds.c.

20762 {
20763  Relation pg_constraint;
20764  HeapTuple tuple;
20765  SysScanDesc scan;
20766  ScanKeyData key[2];
20767  List *constraints = NIL;
20768 
20769  /*
20770  * If no indexes, or no columns are referenceable by FKs, we can avoid the
20771  * scan.
20772  */
20773  if (RelationGetIndexList(partition) == NIL ||
20776  return NIL;
20777 
20778  /* Search for constraints referencing this table */
20779  pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
20780  ScanKeyInit(&key[0],
20781  Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
20782  F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
20783  ScanKeyInit(&key[1],
20784  Anum_pg_constraint_contype, BTEqualStrategyNumber,
20785  F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
20786 
20787  /* XXX This is a seqscan, as we don't have a usable index */
20788  scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
20789  while ((tuple = systable_getnext(scan)) != NULL)
20790  {
20791  Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20792 
20793  /*
20794  * We only need to process constraints that are part of larger ones.
20795  */
20796  if (!OidIsValid(constrForm->conparentid))
20797  continue;
20798 
20799  constraints = lappend_oid(constraints, constrForm->oid);
20800  }
20801 
20802  systable_endscan(scan);
20803  table_close(pg_constraint, AccessShareLock);
20804 
20805  return constraints;
20806 }
#define bms_is_empty(a)
Definition: bitmapset.h:118
@ INDEX_ATTR_BITMAP_KEY
Definition: relcache.h:61

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

Referenced by ATDetachCheckNoForeignKeyRefs(), and DetachPartitionFinalize().

◆ index_copy_data()

static void index_copy_data ( Relation  rel,
RelFileLocator  newrlocator 
)
static

Definition at line 16052 of file tablecmds.c.

16053 {
16054  SMgrRelation dstrel;
16055 
16056  /*
16057  * Since we copy the file directly without looking at the shared buffers,
16058  * we'd better first flush out any pages of the source relation that are
16059  * in shared buffers. We assume no new changes will be made while we are
16060  * holding exclusive lock on the rel.
16061  */
16062  FlushRelationBuffers(rel);
16063 
16064  /*
16065  * Create and copy all forks of the relation, and schedule unlinking of
16066  * old physical files.
16067  *
16068  * NOTE: any conflict in relfilenumber value will be caught in
16069  * RelationCreateStorage().
16070  */
16071  dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
16072 
16073  /* copy main fork */
16075  rel->rd_rel->relpersistence);
16076 
16077  /* copy those extra forks that exist */
16078  for (ForkNumber forkNum = MAIN_FORKNUM + 1;
16079  forkNum <= MAX_FORKNUM; forkNum++)
16080  {
16081  if (smgrexists(RelationGetSmgr(rel), forkNum))
16082  {
16083  smgrcreate(dstrel, forkNum, false);
16084 
16085  /*
16086  * WAL log creation if the relation is persistent, or this is the
16087  * init fork of an unlogged relation.
16088  */
16089  if (RelationIsPermanent(rel) ||
16090  (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
16091  forkNum == INIT_FORKNUM))
16092  log_smgrcreate(&newrlocator, forkNum);
16093  RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
16094  rel->rd_rel->relpersistence);
16095  }
16096  }
16097 
16098  /* drop old relation, and close new one */
16099  RelationDropStorage(rel);
16100  smgrclose(dstrel);
16101 }
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:4435
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:567
ForkNumber
Definition: relpath.h:48
@ MAIN_FORKNUM
Definition: relpath.h:50
@ INIT_FORKNUM
Definition: relpath.h:53
#define MAX_FORKNUM
Definition: relpath.h:62
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition: smgr.c:411
void smgrclose(SMgrRelation reln)
Definition: smgr.c:320
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:398
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition: storage.c:452
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition: storage.c:121
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition: storage.c:186
void RelationDropStorage(Relation rel)
Definition: storage.c:206

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

Referenced by ATExecSetTableSpace().

◆ MarkInheritDetached()

static void MarkInheritDetached ( Relation  child_rel,
Relation  parent_rel 
)
static

Definition at line 16778 of file tablecmds.c.

16779 {
16780  Relation catalogRelation;
16781  SysScanDesc scan;
16782  ScanKeyData key;
16783  HeapTuple inheritsTuple;
16784  bool found = false;
16785 
16786  Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16787 
16788  /*
16789  * Find pg_inherits entries by inhparent. (We need to scan them all in
16790  * order to verify that no other partition is pending detach.)
16791  */
16792  catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16793  ScanKeyInit(&key,
16794  Anum_pg_inherits_inhparent,
16795  BTEqualStrategyNumber, F_OIDEQ,
16796  ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16797  scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16798  true, NULL, 1, &key);
16799 
16800  while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16801  {
16802  Form_pg_inherits inhForm;
16803 
16804  inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16805  if (inhForm->inhdetachpending)
16806  ereport(ERROR,
16807  errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16808  errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16809  get_rel_name(inhForm->inhrelid),
16810  get_namespace_name(parent_rel->rd_rel->relnamespace),
16811  RelationGetRelationName(parent_rel)),
16812  errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16813 
16814  if (inhForm->inhrelid == RelationGetRelid(child_rel))
16815  {
16816  HeapTuple newtup;
16817 
16818  newtup = heap_copytuple(inheritsTuple);
16819  ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16820 
16821  CatalogTupleUpdate(catalogRelation,
16822  &inheritsTuple->t_self,
16823  newtup);
16824  found = true;
16825  heap_freetuple(newtup);
16826  /* keep looking, to ensure we catch others pending detach */
16827  }
16828  }
16829 
16830  /* Done */
16831  systable_endscan(scan);
16832  table_close(catalogRelation, RowExclusiveLock);
16833 
16834  if (!found)
16835  ereport(ERROR,
16837  errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16838  RelationGetRelationName(child_rel),
16839  RelationGetRelationName(parent_rel))));
16840 }
#define ERRCODE_UNDEFINED_TABLE
Definition: pgbench.c:78

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

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

References aclcheck_error(), ACLCHECK_NOT_OWNER, Assert, CookedConstraint::attnum, attnum, AttrMap::attnums, bms_add_member(), bms_is_member(), ConstrCheck::ccname, TupleConstr::check, CheckTableNotInUse(), ColumnDef::colname, ColumnDef::compression, CompressionMethodIsValid, CookedConstraint::conoid, TupleDescData::constr, CONSTR_NOTNULL, ColumnDef::constraints, CookedConstraint::contype, ColumnDef::cooked_default, elog, equal(), ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, CookedConstraint::expr, findAttrByName(), FirstLowInvalidHeapAttributeNumber, forboth, free_attrmap(), ColumnDef::generated, get_relkind_objtype(), GetCompressionMethodName(), GetUserId(), i, ColumnDef::identity, INDEX_ATTR_BITMAP_PRIMARY_KEY, CookedConstraint::inhcount, ColumnDef::inhcount, InvalidOid, ColumnDef::is_from_type, CookedConstraint::is_local, ColumnDef::is_local, CookedConstraint::is_no_inherit, 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, CookedConstraint::name, NameStr, TupleDescData::natts, NIL, NoLock, TupleConstr::num_check, object_ownercheck(), palloc(), pstrdup(), ColumnDef::raw_default, RelationData::rd_islocaltemp, RelationData::rd_rel, RelationGetDescr, RelationGetIndexAttrBitmap(), RelationGetNotNullConstraints(), RelationGetRelationName, RelationGetRelid, CookedConstraint::skip_validation, 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 16408 of file tablecmds.c.

16409 {
16410  Relation attrrel;
16411  TupleDesc parent_desc;
16412 
16413  attrrel = table_open(AttributeRelationId, RowExclusiveLock);
16414  parent_desc = RelationGetDescr(parent_rel);
16415 
16416  for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
16417  {
16418  Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
16419  char *parent_attname = NameStr(parent_att->attname);
16420  HeapTuple tuple;
16421 
16422  /* Ignore dropped columns in the parent. */
16423  if (parent_att->attisdropped)
16424  continue;
16425 
16426  /* Find same column in child (matching on column name). */
16427  tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
16428  if (HeapTupleIsValid(tuple))
16429  {
16430  Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
16431 
16432  if (parent_att->atttypid != child_att->atttypid ||
16433  parent_att->atttypmod != child_att->atttypmod)
16434  ereport(ERROR,
16435  (errcode(ERRCODE_DATATYPE_MISMATCH),
16436  errmsg("child table \"%s\" has different type for column \"%s\"",
16437  RelationGetRelationName(child_rel), parent_attname)));
16438 
16439  if (parent_att->attcollation != child_att->attcollation)
16440  ereport(ERROR,
16441  (errcode(ERRCODE_COLLATION_MISMATCH),
16442  errmsg("child table \"%s\" has different collation for column \"%s\"",
16443  RelationGetRelationName(child_rel), parent_attname)));
16444 
16445  /*
16446  * If the parent has a not-null constraint that's not NO INHERIT,
16447  * make sure the child has one too.
16448  *
16449  * Other constraints are checked elsewhere.
16450  */
16451  if (parent_att->attnotnull && !child_att->attnotnull)
16452  {
16453  HeapTuple contup;
16454 
16455  contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
16456  parent_att->attnum);
16457  if (HeapTupleIsValid(contup) &&
16458  !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
16459  ereport(ERROR,
16460  errcode(ERRCODE_DATATYPE_MISMATCH),
16461  errmsg("column \"%s\" in child table must be marked NOT NULL",
16462  parent_attname));
16463  }
16464 
16465  /*
16466  * Child column must be generated if and only if parent column is.
16467  */
16468  if (parent_att->attgenerated && !child_att->attgenerated)
16469  ereport(ERROR,
16470  (errcode(ERRCODE_DATATYPE_MISMATCH),
16471  errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
16472  if (child_att->attgenerated && !parent_att->attgenerated)
16473  ereport(ERROR,
16474  (errcode(ERRCODE_DATATYPE_MISMATCH),
16475  errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
16476 
16477  /*
16478  * Regular inheritance children are independent enough not to
16479  * inherit identity columns. But partitions are integral part of
16480  * a partitioned table and inherit identity column.
16481  */
16482  if (ispartition)
16483  child_att->attidentity = parent_att->attidentity;
16484 
16485  /*
16486  * OK, bump the child column's inheritance count. (If we fail
16487  * later on, this change will just roll back.)
16488  */
16489  child_att->attinhcount++;
16490  if (child_att->attinhcount < 0)
16491  ereport(ERROR,
16492  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16493  errmsg("too many inheritance parents"));
16494 
16495  /*
16496  * In case of partitions, we must enforce that value of attislocal
16497  * is same in all partitions. (Note: there are only inherited
16498  * attributes in partitions)
16499  */
16500  if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16501  {
16502  Assert(child_att->attinhcount == 1);
16503  child_att->attislocal = false;
16504  }
16505 
16506  CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
16507  heap_freetuple(tuple);
16508  }
16509  else
16510  {
16511  ereport(ERROR,
16512  (errcode(ERRCODE_DATATYPE_MISMATCH),
16513  errmsg("child table is missing column \"%s\"", parent_attname)));
16514  }
16515  }
16516 
16517  table_close(attrrel, RowExclusiveLock);
16518 }

References Assert, CatalogTupleUpdate(), ereport, errcode(), errmsg(), ERROR, findNotNullConstraintAttnum(), GETSTRUCT, heap_freetuple(), HeapTupleIsValid, NameStr, TupleDescData::natts, 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 
)
static

Definition at line 3141 of file tablecmds.c.

3142 {
3143  ListCell *lc;
3144  CookedConstraint *newcon;
3145 
3146  foreach(lc, constraints)
3147  {
3148  CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3149 
3150  Assert(ccon->contype == CONSTR_CHECK);
3151 
3152  /* Non-matching names never conflict */
3153  if (strcmp(ccon->name, name) != 0)
3154  continue;
3155 
3156  if (equal(expr, ccon->expr))
3157  {
3158  /* OK to merge constraint with existing */
3159  ccon->inhcount++;
3160  if (ccon->inhcount < 0)
3161  ereport(ERROR,
3162  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3163  errmsg("too many inheritance parents"));
3164  return constraints;
3165  }
3166 
3167  ereport(ERROR,
3169  errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3170  name)));
3171  }
3172 
3173  /*
3174  * Constraint couldn't be merged with an existing one and also didn't
3175  * conflict with an existing one, so add it as a new one to the list.
3176  */
3177  newcon = palloc0_object(CookedConstraint);
3178  newcon->contype = CONSTR_CHECK;
3179  newcon->name = pstrdup(name);
3180  newcon->expr = expr;
3181  newcon->inhcount = 1;
3182  return lappend(constraints, newcon);
3183 }
#define palloc0_object(type)
Definition: fe_memutils.h:63

References Assert, CONSTR_CHECK, CookedConstraint::contype, equal(), ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, CookedConstraint::expr, CookedConstraint::inhcount, lappend(), lfirst, name, CookedConstraint::name, palloc0_object, and pstrdup().

Referenced by MergeAttributes().

◆ MergeChildAttribute()

static void MergeChildAttribute ( List inh_columns,
int  exist_attno,
int  newcol_attno,
const ColumnDef newdef 
)
static

Definition at line 3207 of file tablecmds.c.

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

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

16539 {
16540  Relation constraintrel;
16541  SysScanDesc parent_scan;
16542  ScanKeyData parent_key;
16543  HeapTuple parent_tuple;
16544  Oid parent_relid = RelationGetRelid(parent_rel);
16545 
16546  constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
16547 
16548  /* Outer loop scans through the parent's constraint definitions */
16549  ScanKeyInit(&parent_key,
16550  Anum_pg_constraint_conrelid,
16551  BTEqualStrategyNumber, F_OIDEQ,
16552  ObjectIdGetDatum(parent_relid));
16553  parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16554  true, NULL, 1, &parent_key);
16555 
16556  while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
16557  {
16558  Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
16559  SysScanDesc child_scan;
16560  ScanKeyData child_key;
16561  HeapTuple child_tuple;
16562  bool found = false;
16563 
16564  if (parent_con->contype != CONSTRAINT_CHECK &&
16565  parent_con->contype != CONSTRAINT_NOTNULL)
16566  continue;
16567 
16568  /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16569  if (parent_con->connoinherit)
16570  continue;
16571 
16572  /* Search for a child constraint matching this one */
16573  ScanKeyInit(&child_key,
16574  Anum_pg_constraint_conrelid,
16575  BTEqualStrategyNumber, F_OIDEQ,
16576  ObjectIdGetDatum(RelationGetRelid(child_rel)));
16577  child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16578  true, NULL, 1, &child_key);
16579 
16580  while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
16581  {
16582  Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
16583  HeapTuple child_copy;
16584 
16585  if (child_con->contype != parent_con->contype)
16586  continue;
16587 
16588  /*
16589  * CHECK constraint are matched by name, NOT NULL ones by
16590  * attribute number
16591  */
16592  if (child_con->contype == CONSTRAINT_CHECK)
16593  {
16594  if (strcmp(NameStr(parent_con->conname),
16595  NameStr(child_con->conname)) != 0)
16596  continue;
16597  }
16598  else if (child_con->contype == CONSTRAINT_NOTNULL)
16599  {
16600  AttrNumber parent_attno = extractNotNullColumn(parent_tuple);
16601  AttrNumber child_attno = extractNotNullColumn(child_tuple);
16602 
16603  if (strcmp(get_attname(parent_relid, parent_attno, false),
16604  get_attname(RelationGetRelid(child_rel), child_attno,
16605  false)) != 0)
16606  continue;
16607  }
16608 
16609  if (child_con->contype == CONSTRAINT_CHECK &&
16610  !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
16611  ereport(ERROR,
16612  (errcode(ERRCODE_DATATYPE_MISMATCH),
16613  errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16614  RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
16615 
16616  /*
16617  * If the CHECK child constraint is "no inherit" then cannot
16618  * merge.
16619  *
16620  * This is not desirable for not-null constraints, mostly because
16621  * it breaks our pg_upgrade strategy, but it also makes sense on
16622  * its own: if a child has its own not-null constraint and then
16623  * acquires a parent with the same constraint, then we start to
16624  * enforce that constraint for all the descendants of that child
16625  * too, if any.
16626  */
16627  if (child_con->contype == CONSTRAINT_CHECK &&
16628  child_con->connoinherit)
16629  ereport(ERROR,
16630  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16631  errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
16632  NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16633 
16634  /*
16635  * If the child constraint is "not valid" then cannot merge with a
16636  * valid parent constraint
16637  */
16638  if (parent_con->convalidated && !child_con->convalidated)
16639  ereport(ERROR,
16640  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16641  errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16642  NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16643 
16644  /*
16645  * OK, bump the child constraint's inheritance count. (If we fail
16646  * later on, this change will just roll back.)
16647  */
16648  child_copy = heap_copytuple(child_tuple);
16649  child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
16650  child_con->coninhcount++;
16651  if (child_con->coninhcount < 0)
16652  ereport(ERROR,
16653  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16654  errmsg("too many inheritance parents"));
16655  if (child_con->contype == CONSTRAINT_NOTNULL &&
16656  child_con->connoinherit)
16657  {
16658  /*
16659  * If the child has children, it's not possible to turn a NO
16660  * INHERIT constraint into an inheritable one: we would need
16661  * to recurse to create constraints in those children, but
16662  * this is not a good place to do that.
16663  */
16664  if (child_rel->rd_rel->relhassubclass)
16665  ereport(ERROR,
16666  errmsg("cannot add NOT NULL constraint to column \"%s\" of relation \"%s\" with inheritance children",
16667  get_attname(RelationGetRelid(child_rel),
16668  extractNotNullColumn(child_tuple),
16669  false),
16670  RelationGetRelationName(child_rel)),
16671  errdetail("Existing constraint \"%s\" is marked NO INHERIT.",
16672  NameStr(child_con->conname)));
16673 
16674  child_con->connoinherit = false;
16675  }
16676 
16677  /*
16678  * In case of partitions, an inherited constraint must be
16679  * inherited only once since it cannot have multiple parents and
16680  * it is never considered local.
16681  */
16682  if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16683  {
16684  Assert(child_con->coninhcount == 1);
16685  child_con->conislocal = false;
16686  }
16687 
16688  CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
16689  heap_freetuple(child_copy);
16690 
16691  found = true;
16692  break;
16693  }
16694 
16695  systable_endscan(child_scan);
16696 
16697  if (!found)
16698  {
16699  if (parent_con->contype == CONSTRAINT_NOTNULL)
16700  ereport(ERROR,
16701  errcode(ERRCODE_DATATYPE_MISMATCH),
16702  errmsg("column \"%s\" in child table must be marked NOT NULL",
16703  get_attname(parent_relid,
16704  extractNotNullColumn(parent_tuple),
16705  false)));
16706 
16707  ereport(ERROR,
16708  (errcode(ERRCODE_DATATYPE_MISMATCH),
16709  errmsg("child table is missing constraint \"%s\"",
16710  NameStr(parent_con->conname))));
16711  }
16712  }
16713 
16714  systable_endscan(parent_scan);
16715  table_close(constraintrel, RowExclusiveLock);
16716 }
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
Definition: tablecmds.c:16379

References Assert, BTEqualStrategyNumber, CatalogTupleUpdate(), constraints_equivalent(), ereport, errcode(), errdetail(), errmsg(), ERROR, extractNotNullColumn(), get_attname(), GETSTRUCT, heap_copytuple(), heap_freetuple(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by CreateInheritance().

◆ MergeInheritedAttribute()

static ColumnDef * MergeInheritedAttribute ( List inh_columns,
int  exist_attno,
const ColumnDef newdef 
)
static

Definition at line 3368 of file tablecmds.c.

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

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, ColumnDef::storage, storage_name(), ColumnDef::typeName, and typenameTypeIdAndMod().

Referenced by MergeAttributes().

◆ moveMergedTablesRows()

static void moveMergedTablesRows ( Relation  rel,
List mergingPartitionsList,
Relation  newPartRel 
)
static

Definition at line 21376 of file tablecmds.c.

21378 {
21379  CommandId mycid;
21380 
21381  /* The FSM is empty, so don't bother using it. */
21382  int ti_options = TABLE_INSERT_SKIP_FSM;
21383  ListCell *listptr;
21384  BulkInsertState bistate; /* state of bulk inserts for partition */
21385  TupleTableSlot *dstslot;
21386 
21387  mycid = GetCurrentCommandId(true);
21388 
21389  /* Prepare a BulkInsertState for table_tuple_insert. */
21390  bistate = GetBulkInsertState();
21391 
21392  /* Create necessary tuple slot. */
21393  dstslot = MakeSingleTupleTableSlot(RelationGetDescr(newPartRel),
21394  table_slot_callbacks(newPartRel));
21395  ExecStoreAllNullTuple(dstslot);
21396 
21397  foreach(listptr, mergingPartitionsList)
21398  {
21399  Relation mergingPartition = (Relation) lfirst(listptr);
21400  TupleTableSlot *srcslot;
21401  TupleConversionMap *tuple_map;
21402  TableScanDesc scan;
21403  Snapshot snapshot;
21404 
21405  /* Create tuple slot for new partition. */
21406  srcslot = MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition),
21407  table_slot_callbacks(mergingPartition));
21408 
21409  /*
21410  * Map computing for moving attributes of merged partition to new
21411  * partition.
21412  */
21413  tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
21414  RelationGetDescr(newPartRel));
21415 
21416  /* Scan through the rows. */
21417  snapshot = RegisterSnapshot(GetLatestSnapshot());
21418  scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
21419 
21420  while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
21421  {
21422  TupleTableSlot *insertslot;
21423 
21424  /* Extract data from old tuple. */
21425  slot_getallattrs(srcslot);
21426 
21427  if (tuple_map)
21428  {
21429  /* Need to use map to copy attributes. */
21430  insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
21431  }
21432  else
21433  {
21434  /* Copy attributes directly. */
21435  insertslot = dstslot;
21436 
21437  ExecClearTuple(insertslot);
21438 
21439  memcpy(insertslot->tts_values, srcslot->tts_values,
21440  sizeof(Datum) * srcslot->tts_nvalid);
21441  memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
21442  sizeof(bool) * srcslot->tts_nvalid);
21443 
21444  ExecStoreVirtualTuple(insertslot);
21445  }
21446 
21447  /* Write the tuple out to the new relation. */
21448  table_tuple_insert(newPartRel, insertslot, mycid,
21449  ti_options, bistate);
21450 
21452  }
21453 
21454  table_endscan(scan);
21455  UnregisterSnapshot(snapshot);
21456 
21457  if (tuple_map)
21458  free_conversion_map(tuple_map);
21459 
21461  }
21462 
21464  FreeBulkInsertState(bistate);
21465 
21466  table_finish_bulk_insert(newPartRel, ti_options);
21467 }
AttrMap * attrMap
Definition: tupconvert.h:28
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition: tupconvert.c:192
void free_conversion_map(TupleConversionMap *map)
Definition: tupconvert.c:299
TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc)
Definition: tupconvert.c:102

References TupleConversionMap::attrMap, CHECK_FOR_INTERRUPTS, convert_tuples_by_name(), ExecClearTuple(), ExecDropSingleTupleTableSlot(), ExecStoreAllNullTuple(), ExecStoreVirtualTuple(), execute_attr_map_slot(), ForwardScanDirection, free_conversion_map(), FreeBulkInsertState(), GetBulkInsertState(), GetCurrentCommandId(), GetLatestSnapshot(), lfirst, MakeSingleTupleTableSlot(), RegisterSnapshot(), RelationGetDescr, slot_getallattrs(), table_beginscan(), table_endscan(), table_finish_bulk_insert(), TABLE_INSERT_SKIP_FSM, table_scan_getnextslot(), table_slot_callbacks(), table_tuple_insert(), TupleTableSlot::tts_isnull, TupleTableSlot::tts_nvalid, TupleTableSlot::tts_values, and UnregisterSnapshot().

Referenced by ATExecMergePartitions().

◆ moveSplitTableRows()

static void moveSplitTableRows ( Relation  rel,
Relation  splitRel,
List partlist,
List newPartRels,
Oid  defaultPartOid 
)
static

Definition at line 20996 of file tablecmds.c.

20997 {
20998  /* The FSM is empty, so don't bother using it. */
20999  int ti_options = TABLE_INSERT_SKIP_FSM;
21000  CommandId mycid;
21001  EState *estate;
21002  ListCell *listptr,
21003  *listptr2;
21004  TupleTableSlot *srcslot;
21005  ExprContext *econtext;
21006  TableScanDesc scan;
21007  Snapshot snapshot;
21008  MemoryContext oldCxt;
21009  List *partContexts = NIL;
21010  TupleConversionMap *tuple_map;
21011  SplitPartitionContext *defaultPartCtx = NULL,
21012  *pc;
21013  bool isOldDefaultPart = false;
21014 
21015  mycid = GetCurrentCommandId(true);
21016 
21017  estate = CreateExecutorState();
21018 
21019  forboth(listptr, partlist, listptr2, newPartRels)
21020  {
21021  SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21022 
21023  pc = createSplitPartitionContext((Relation) lfirst(listptr2));
21024 
21025  if (sps->bound->is_default)
21026  {
21027  /* We should not create constraint for detached DEFAULT partition. */
21028  defaultPartCtx = pc;
21029  }
21030  else
21031  {
21032  List *partConstraint;
21033 
21034  /* Build expression execution states for partition check quals. */
21035  partConstraint = get_qual_from_partbound(rel, sps->bound);
21036  partConstraint =
21037  (List *) eval_const_expressions(NULL,
21038  (Node *) partConstraint);
21039  /* Make boolean expression for ExecCheck(). */
21040  partConstraint = list_make1(make_ands_explicit(partConstraint));
21041 
21042  /*
21043  * Map the vars in the constraint expression from rel's attnos to
21044  * splitRel's.
21045  */
21046  partConstraint = map_partition_varattnos(partConstraint,
21047  1, splitRel, rel);
21048 
21049  pc->partqualstate =
21050  ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
21051  Assert(pc->partqualstate != NULL);
21052  }
21053 
21054  /* Store partition context into list. */
21055  partContexts = lappend(partContexts, pc);
21056  }
21057 
21058  /*
21059  * Create partition context for DEFAULT partition. We can insert values
21060  * into this partition in case spaces with values between new partitions.
21061  */
21062  if (!defaultPartCtx && OidIsValid(defaultPartOid))
21063  {
21064  /* Indicate that we allocate context for old DEFAULT partition */
21065  isOldDefaultPart = true;
21066  defaultPartCtx = createSplitPartitionContext(table_open(defaultPartOid, AccessExclusiveLock));
21067  }
21068 
21069  econtext = GetPerTupleExprContext(estate);
21070 
21071  /* Create necessary tuple slot. */
21072  srcslot = MakeSingleTupleTableSlot(RelationGetDescr(splitRel),
21073  table_slot_callbacks(splitRel));
21074 
21075  /*
21076  * Map computing for moving attributes of split partition to new partition
21077  * (for first new partition, but other new partitions can use the same
21078  * map).
21079  */
21080  pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
21081  tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
21082  RelationGetDescr(pc->partRel));
21083 
21084  /* Scan through the rows. */
21085  snapshot = RegisterSnapshot(GetLatestSnapshot());
21086  scan = table_beginscan(splitRel, snapshot, 0, NULL);
21087 
21088  /*
21089  * Switch to per-tuple memory context and reset it for each tuple
21090  * produced, so we don't leak memory.
21091  */
21093 
21094  while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
21095  {
21096  bool found = false;
21097  TupleTableSlot *insertslot;
21098 
21099  /* Extract data from old tuple. */
21100  slot_getallattrs(srcslot);
21101 
21102  econtext->ecxt_scantuple = srcslot;
21103 
21104  /* Search partition for current slot srcslot. */
21105  foreach(listptr, partContexts)
21106  {
21107  pc = (SplitPartitionContext *) lfirst(listptr);
21108 
21109  if (pc->partqualstate /* skip DEFAULT partition */ &&
21110  ExecCheck(pc->partqualstate, econtext))
21111  {
21112  found = true;
21113  break;
21114  }
21115  ResetExprContext(econtext);
21116  }
21117  if (!found)
21118  {
21119  /* Use DEFAULT partition if it exists. */
21120  if (defaultPartCtx)
21121  pc = defaultPartCtx;
21122  else
21123  ereport(ERROR,
21124  (errcode(ERRCODE_CHECK_VIOLATION),
21125  errmsg("can not find partition for split partition row"),
21126  errtable(splitRel)));
21127  }
21128 
21129  if (tuple_map)
21130  {
21131  /* Need to use map to copy attributes. */
21132  insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
21133  }
21134  else
21135  {
21136  /* Copy attributes directly. */
21137  insertslot = pc->dstslot;
21138 
21139  ExecClearTuple(insertslot);
21140 
21141  memcpy(insertslot->tts_values, srcslot->tts_values,
21142  sizeof(Datum) * srcslot->tts_nvalid);
21143  memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
21144  sizeof(bool) * srcslot->tts_nvalid);
21145 
21146  ExecStoreVirtualTuple(insertslot);
21147  }
21148 
21149  /* Write the tuple out to the new relation. */
21150  table_tuple_insert(pc->partRel, insertslot, mycid,
21151  ti_options, pc->bistate);
21152 
21153  ResetExprContext(econtext);
21154 
21156  }
21157 
21158  MemoryContextSwitchTo(oldCxt);
21159 
21160  table_endscan(scan);
21161  UnregisterSnapshot(snapshot);
21162 
21163  if (tuple_map)
21164  free_conversion_map(tuple_map);
21165 
21167 
21168  FreeExecutorState(estate);
21169 
21170  foreach(listptr, partContexts)
21171  deleteSplitPartitionContext((SplitPartitionContext *) lfirst(listptr), ti_options);
21172 
21173  /* Need to close table and free buffers for DEFAULT partition. */
21174  if (isOldDefaultPart)
21175  {
21176  Relation defaultPartRel = defaultPartCtx->partRel;
21177 
21178  deleteSplitPartitionContext(defaultPartCtx, ti_options);
21179  /* Keep the lock until commit. */
21180  table_close(defaultPartRel, NoLock);
21181  }
21182 }
static SplitPartitionContext * createSplitPartitionContext(Relation partRel)
Definition: tablecmds.c:20951
static void deleteSplitPartitionContext(SplitPartitionContext *pc, int ti_options)
Definition: tablecmds.c:20976

References AccessExclusiveLock, Assert, TupleConversionMap::attrMap, SinglePartitionSpec::bound, CHECK_FOR_INTERRUPTS, convert_tuples_by_name(), CreateExecutorState(), createSplitPartitionContext(), deleteSplitPartitionContext(), ExprContext::ecxt_scantuple, ereport, errcode(), errmsg(), ERROR, errtable(), eval_const_expressions(), ExecCheck(), ExecClearTuple(), ExecDropSingleTupleTableSlot(), ExecPrepareExpr(), ExecStoreVirtualTuple(), execute_attr_map_slot(), forboth, 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(), MakeSingleTupleTableSlot(), map_partition_varattnos(), MemoryContextSwitchTo(), NIL, NoLock, OidIsValid, SplitPartitionContext::partRel, RegisterSnapshot(), RelationGetDescr, ResetExprContext, slot_getallattrs(), table_beginscan(), table_close(), table_endscan(), TABLE_INSERT_SKIP_FSM, table_open(), table_scan_getnextslot(), table_slot_callbacks(), table_tuple_insert(), TupleTableSlot::tts_isnull, TupleTableSlot::tts_nvalid, TupleTableSlot::tts_values, and UnregisterSnapshot().

Referenced by ATExecSplitPartition().

◆ NotNullImpliedByRelConstraints()

static bool NotNullImpliedByRelConstraints ( Relation  rel,
Form_pg_attribute  attr 
)
static

Definition at line 8049 of file tablecmds.c.

8050 {
8051  NullTest *nnulltest = makeNode(NullTest);
8052 
8053  nnulltest->arg = (Expr *) makeVar(1,
8054  attr->attnum,
8055  attr->atttypid,
8056  attr->atttypmod,
8057  attr->attcollation,
8058  0);
8059  nnulltest->nulltesttype = IS_NOT_NULL;
8060 
8061  /*
8062  * argisrow = false is correct even for a composite column, because
8063  * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8064  * case, just IS DISTINCT FROM NULL.
8065  */
8066  nnulltest->argisrow = false;
8067  nnulltest->location = -1;
8068 
8069  if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8070  {
8071  ereport(DEBUG1,
8072  (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8073  RelationGetRelationName(rel), NameStr(attr->attname))));
8074  return true;
8075  }
8076 
8077  return false;
8078 }
@ IS_NOT_NULL
Definition: primnodes.h:1924
NullTestType nulltesttype
Definition: primnodes.h:1931
ParseLoc location
Definition: primnodes.h:1934
Expr * arg
Definition: primnodes.h:1930
static bool ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
Definition: tablecmds.c:19016

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

18965 {
18966  List *existConstraint = NIL;
18967  TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18968  int i;
18969 
18970  if (constr && constr->has_not_null)
18971  {
18972  int natts = scanrel->rd_att->natts;
18973 
18974  for (i = 1; i <= natts; i++)
18975  {
18976  Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
18977 
18978  if (att->attnotnull && !att->attisdropped)
18979  {
18980  NullTest *ntest = makeNode(NullTest);
18981 
18982  ntest->arg = (Expr *) makeVar(1,
18983  i,
18984  att->atttypid,
18985  att->atttypmod,
18986  att->attcollation,
18987  0);
18988  ntest->nulltesttype = IS_NOT_NULL;
18989 
18990  /*
18991  * argisrow=false is correct even for a composite column,
18992  * because attnotnull does not represent a SQL-spec IS NOT
18993  * NULL test in such a case, just IS DISTINCT FROM NULL.
18994  */
18995  ntest->argisrow = false;
18996  ntest->location = -1;
18997  existConstraint = lappend(existConstraint, ntest);
18998  }
18999  }
19000  }
19001 
19002  return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
19003 }

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

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

◆ PreCommit_on_commit_actions()

void PreCommit_on_commit_actions ( void  )

Definition at line 18240 of file tablecmds.c.

18241 {
18242  ListCell *l;
18243  List *oids_to_truncate = NIL;
18244  List *oids_to_drop = NIL;
18245 
18246  foreach(l, on_commits)
18247  {
18248  OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18249 
18250  /* Ignore entry if already dropped in this xact */
18252  continue;
18253 
18254  switch (oc->oncommit)
18255  {
18256  case ONCOMMIT_NOOP:
18258  /* Do nothing (there shouldn't be such entries, actually) */
18259  break;
18260  case ONCOMMIT_DELETE_ROWS:
18261 
18262  /*
18263  * If this transaction hasn't accessed any temporary
18264  * relations, we can skip truncating ON COMMIT DELETE ROWS
18265  * tables, as they must still be empty.
18266  */
18268  oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18269  break;
18270  case ONCOMMIT_DROP:
18271  oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18272  break;
18273  }
18274  }
18275 
18276  /*
18277  * Truncate relations before dropping so that all dependencies between
18278  * relations are removed after they are worked on. Doing it like this
18279  * might be a waste as it is possible that a relation being truncated will
18280  * be dropped anyway due to its parent being dropped, but this makes the
18281  * code more robust because of not having to re-check that the relation
18282  * exists at truncation time.
18283  */
18284  if (oids_to_truncate != NIL)
18285  heap_truncate(oids_to_truncate);
18286 
18287  if (oids_to_drop != NIL)
18288  {
18289  ObjectAddresses *targetObjects = new_object_addresses();
18290 
18291  foreach(l, oids_to_drop)
18292  {
18293  ObjectAddress object;
18294 
18295  object.classId = RelationRelationId;
18296  object.objectId = lfirst_oid(l);
18297  object.objectSubId = 0;
18298 
18299  Assert(!object_address_present(&object, targetObjects));
18300 
18301  add_exact_object_address(&object, targetObjects);
18302  }
18303 
18304  /*
18305  * Object deletion might involve toast table access (to clean up
18306  * toasted catalog entries), so ensure we have a valid snapshot.
18307  */
18309 
18310  /*
18311  * Since this is an automatic drop, rather than one directly initiated
18312  * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18313  */
18314  performMultipleDeletions(targetObjects, DROP_CASCADE,
18316 
18318 
18319 #ifdef USE_ASSERT_CHECKING
18320 
18321  /*
18322  * Note that table deletion will call remove_on_commit_action, so the
18323  * entry should get marked as deleted.
18324  */
18325  foreach(l, on_commits)
18326  {
18327  OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18328 
18329  if (oc->oncommit != ONCOMMIT_DROP)
18330  continue;
18331 
18333  }
18334 #endif
18335  }
18336 }
#define PERFORM_DELETION_QUIETLY
Definition: dependency.h:87
void heap_truncate(List *relids)
Definition: heap.c:3377
@ ONCOMMIT_DELETE_ROWS
Definition: primnodes.h:59
@ ONCOMMIT_PRESERVE_ROWS
Definition: primnodes.h:58
@ ONCOMMIT_DROP
Definition: primnodes.h:60
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:216
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:648
OnCommitAction oncommit
Definition: tablecmds.c:115
int MyXactFlags
Definition: xact.c:134
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:102

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

Referenced by CommitTransaction(), and PrepareTransaction().

◆ QueuePartitionConstraintValidation()

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

Definition at line 19073 of file tablecmds.c.

19076 {
19077  /*
19078  * Based on the table's existing constraints, determine whether or not we
19079  * may skip scanning the table.
19080  */
19081  if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
19082  {
19083  if (!validate_default)
19084  ereport(DEBUG1,
19085  (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19086  RelationGetRelationName(scanrel))));
19087  else
19088  ereport(DEBUG1,
19089  (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19090  RelationGetRelationName(scanrel))));
19091  return;
19092  }
19093 
19094  /*
19095  * Constraints proved insufficient. For plain relations, queue a
19096  * validation item now; for partitioned tables, recurse to process each
19097  * partition.
19098  */
19099  if (scanrel->rd_rel->relkind == RELKIND_RELATION)
19100  {
19101  AlteredTableInfo *tab;
19102 
19103  /* Grab a work queue entry. */
19104  tab = ATGetQueueEntry(wqueue, scanrel);
19105  Assert(tab->partition_constraint == NULL);
19106  tab->partition_constraint = (Expr *) linitial(partConstraint);
19107  tab->validate_default = validate_default;
19108  }
19109  else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19110  {
19111  PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
19112  int i;
19113 
19114  for (i = 0; i < partdesc->nparts; i++)
19115  {
19116  Relation part_rel;
19117  List *thisPartConstraint;
19118 
19119  /*
19120  * This is the minimum lock we need to prevent deadlocks.
19121  */
19122  part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
19123 
19124  /*
19125  * Adjust the constraint for scanrel so that it matches this
19126  * partition's attribute numbers.
19127  */
19128  thisPartConstraint =
19129  map_partition_varattnos(partConstraint, 1,
19130  part_rel, scanrel);
19131 
19132  QueuePartitionConstraintValidation(wqueue, part_rel,
19133  thisPartConstraint,
19134  validate_default);
19135  table_close(part_rel, NoLock); /* keep lock till commit */
19136  }
19137  }
19138 }

References AccessExclusiveLock, Assert, ATGetQueueEntry(), DEBUG1, ereport, errmsg_internal(), i, linitial, map_partition_varattnos(), NoLock, PartitionDescData::nparts, PartitionDescData::oids, PartConstraintImpliedByRelConstraint(), AlteredTableInfo::partition_constraint, RelationData::rd_rel, RelationGetPartitionDesc(), RelationGetRelationName, table_close(), table_open(), and AlteredTableInfo::validate_default.

Referenced by ATExecAttachPartition().

◆ RangeVarCallbackForAlterRelation()

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

Definition at line 18506 of file tablecmds.c.

18508 {
18509  Node *stmt = (Node *) arg;
18510  ObjectType reltype;
18511  HeapTuple tuple;
18512  Form_pg_class classform;
18513  AclResult aclresult;
18514  char relkind;
18515 
18516  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
18517  if (!HeapTupleIsValid(tuple))
18518  return; /* concurrently dropped */
18519  classform = (Form_pg_class) GETSTRUCT(tuple);
18520  relkind = classform->relkind;
18521 
18522  /* Must own relation. */
18523  if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
18525 
18526  /* No system table modifications unless explicitly allowed. */
18527  if (!allowSystemTableMods && IsSystemClass(relid, classform))
18528  ereport(ERROR,
18529  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18530  errmsg("permission denied: \"%s\" is a system catalog",
18531  rv->relname)));
18532 
18533  /*
18534  * Extract the specified relation type from the statement parse tree.
18535  *
18536  * Also, for ALTER .. RENAME, check permissions: the user must (still)
18537  * have CREATE rights on the containing namespace.
18538  */
18539  if (IsA(stmt, RenameStmt))
18540  {
18541  aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
18542  GetUserId(), ACL_CREATE);
18543  if (aclresult != ACLCHECK_OK)
18544  aclcheck_error(aclresult, OBJECT_SCHEMA,
18545  get_namespace_name(classform->relnamespace));
18546  reltype = ((RenameStmt *) stmt)->renameType;
18547  }
18548  else if (IsA(stmt, AlterObjectSchemaStmt))
18549  reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
18550 
18551  else if (IsA(stmt, AlterTableStmt))
18552  reltype = ((AlterTableStmt *) stmt)->objtype;
18553  else
18554  {
18555  elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
18556  reltype = OBJECT_TABLE; /* placate compiler */
18557  }
18558 
18559  /*
18560  * For compatibility with prior releases, we allow ALTER TABLE to be used
18561  * with most other types of relations (but not composite types). We allow
18562  * similar flexibility for ALTER INDEX in the case of RENAME, but not
18563  * otherwise. Otherwise, the user must select the correct form of the
18564  * command for the relation at issue.
18565  */
18566  if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
18567  ereport(ERROR,
18568  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18569  errmsg("\"%s\" is not a sequence", rv->relname)));
18570 
18571  if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
18572  ereport(ERROR,
18573  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18574  errmsg("\"%s\" is not a view", rv->relname)));
18575 
18576  if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
18577  ereport(ERROR,
18578  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18579  errmsg("\"%s\" is not a materialized view", rv->relname)));
18580 
18581  if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
18582  ereport(ERROR,
18583  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18584  errmsg("\"%s\" is not a foreign table", rv->relname)));
18585 
18586  if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
18587  ereport(ERROR,
18588  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18589  errmsg("\"%s\" is not a composite type", rv->relname)));
18590 
18591  if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
18592  relkind != RELKIND_PARTITIONED_INDEX
18593  && !IsA(stmt, RenameStmt))
18594  ereport(ERROR,
18595  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18596  errmsg("\"%s\" is not an index", rv->relname)));
18597 
18598  /*
18599  * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18600  * TYPE for that.
18601  */
18602  if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
18603  ereport(ERROR,
18604  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18605  errmsg("\"%s\" is a composite type", rv->relname),
18606  /* translator: %s is an SQL ALTER command */
18607  errhint("Use %s instead.",
18608  "ALTER TYPE")));
18609 
18610  /*
18611  * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18612  * to a different schema, such as indexes and TOAST tables.
18613  */
18615  {
18616  if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
18617  ereport(ERROR,
18618  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18619  errmsg("cannot change schema of index \"%s\"",
18620  rv->relname),
18621  errhint("Change the schema of the table instead.")));
18622  else if (relkind == RELKIND_COMPOSITE_TYPE)
18623  ereport(ERROR,
18624  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18625  errmsg("cannot change schema of composite type \"%s\"",
18626  rv->relname),
18627  /* translator: %s is an SQL ALTER command */
18628  errhint("Use %s instead.",
18629  "ALTER TYPE")));
18630  else if (relkind == RELKIND_TOASTVALUE)
18631  ereport(ERROR,
18632  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18633  errmsg("cannot change schema of TOAST table \"%s\"",
18634  rv->relname),
18635  errhint("Change the schema of the table instead.")));
18636  }
18637 
18638  ReleaseSysCache(tuple);
18639 }
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:85
ObjectType
Definition: parsenodes.h:2262
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2281
@ OBJECT_VIEW
Definition: parsenodes.h:2314
@ OBJECT_TYPE
Definition: parsenodes.h:2312

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

20400 {
20402  Form_pg_class classform;
20403  HeapTuple tuple;
20404 
20405  state = (struct AttachIndexCallbackState *) arg;
20406 
20407  if (!state->lockedParentTbl)
20408  {
20409  LockRelationOid(state->parentTblOid, AccessShareLock);
20410  state->lockedParentTbl = true;
20411  }
20412 
20413  /*
20414  * If we previously locked some other heap, and the name we're looking up
20415  * no longer refers to an index on that relation, release the now-useless
20416  * lock. XXX maybe we should do *after* we verify whether the index does
20417  * not actually belong to the same relation ...
20418  */
20419  if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20420  {
20421  UnlockRelationOid(state->partitionOid, AccessShareLock);
20422  state->partitionOid = InvalidOid;
20423  }
20424 
20425  /* Didn't find a relation, so no need for locking or permission checks. */
20426  if (!OidIsValid(relOid))
20427  return;
20428 
20429  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20430  if (!HeapTupleIsValid(tuple))
20431  return; /* concurrently dropped, so nothing to do */
20432  classform = (Form_pg_class) GETSTRUCT(tuple);
20433  if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20434  classform->relkind != RELKIND_INDEX)
20435  ereport(ERROR,
20436  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20437  errmsg("\"%s\" is not an index", rv->relname)));
20438  ReleaseSysCache(tuple);
20439 
20440  /*
20441  * Since we need only examine the heap's tupledesc, an access share lock
20442  * on it (preventing any DDL) is sufficient.
20443  */
20444  state->partitionOid = IndexGetRelation(relOid, false);
20445  LockRelationOid(state->partitionOid, AccessShareLock);
20446 }
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:227

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

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

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

3924 {
3925  HeapTuple tuple;
3926  Form_pg_class form;
3927 
3928  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3929  if (!HeapTupleIsValid(tuple))
3930  return; /* concurrently dropped */
3931  form = (Form_pg_class) GETSTRUCT(tuple);
3932  renameatt_check(relid, form, false);
3933  ReleaseSysCache(tuple);
3934 }
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition: tablecmds.c:3728

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

18452 {
18453  HeapTuple tuple;
18454 
18455  /* Nothing to do if the relation was not found. */
18456  if (!OidIsValid(relId))
18457  return;
18458 
18459  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18460  if (!HeapTupleIsValid(tuple)) /* should not happen */
18461  elog(ERROR, "cache lookup failed for relation %u", relId);
18462 
18463  truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
18464  truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
18465 
18466  ReleaseSysCache(tuple);
18467 }

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

18416 {
18417  char relkind;
18418  AclResult aclresult;
18419 
18420  /* Nothing to do if the relation was not found. */
18421  if (!OidIsValid(relId))
18422  return;
18423 
18424  /*
18425  * If the relation does exist, check whether it's an index. But note that
18426  * the relation might have been dropped between the time we did the name
18427  * lookup and now. In that case, there's nothing to do.
18428  */
18429  relkind = get_rel_relkind(relId);
18430  if (!relkind)
18431  return;
18432  if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18433  relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18434  ereport(ERROR,
18435  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18436  errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18437 
18438  /* Check permissions */
18439  aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18440  if (aclresult != ACLCHECK_OK)
18441  aclcheck_error(aclresult,
18443  relation->relname);
18444 }
#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 18474 of file tablecmds.c.

18476 {
18477  HeapTuple tuple;
18478 
18479  /* Nothing to do if the relation was not found. */
18480  if (!OidIsValid(relId))
18481  return;
18482 
18483  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18484  if (!HeapTupleIsValid(tuple)) /* should not happen */
18485  elog(ERROR, "cache lookup failed for relation %u", relId);
18486 
18487  if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18489  relation->relname);
18490 
18491  if (!allowSystemTableMods &&
18492  IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18493  ereport(ERROR,
18494  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18495  errmsg("permission denied: \"%s\" is a system catalog",
18496  relation->relname)));
18497 
18498  ReleaseSysCache(tuple);
18499 }

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, elog, ereport, errcode(), errmsg(), ERROR, get_rel_relkind(), get_relkind_objtype(), GETSTRUCT, GetUserId(), HeapTupleIsValid, IsSystemClass(), object_ownercheck(), ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), RangeVar::relname, and SearchSysCache1().

Referenced by AlterSequence(), and ProcessUtilitySlow().

◆ RebuildConstraintComment()

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

Definition at line 14752 of file tablecmds.c.

14755 {
14756  CommentStmt *cmd;
14757  char *comment_str;
14758  AlterTableCmd *newcmd;
14759 
14760  /* Look for comment for object wanted, and leave if none */
14761  comment_str = GetComment(objid, ConstraintRelationId, 0);
14762  if (comment_str == NULL)
14763  return;
14764 
14765  /* Build CommentStmt node, copying all input data for safety */
14766  cmd = makeNode(CommentStmt);
14767  if (rel)
14768  {
14770  cmd->object = (Node *)
14773  makeString(pstrdup(conname)));
14774  }
14775  else
14776  {
14778  cmd->object = (Node *)
14780  makeString(pstrdup(conname)));
14781  }
14782  cmd->comment = comment_str;
14783 
14784  /* Append it to list of commands */
14785  newcmd = makeNode(AlterTableCmd);
14786  newcmd->subtype = AT_ReAddComment;
14787  newcmd->def = (Node *) cmd;
14788  tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14789 }
TypeName * makeTypeNameFromNameList(List *names)
Definition: makefuncs.c:458
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2303
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2276
#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:3268
ObjectType objtype
Definition: parsenodes.h:3266
Node * object
Definition: parsenodes.h:3267

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

20615 {
20616  Oid existingIdx;
20617 
20618  existingIdx = index_get_partition(partitionTbl,
20619  RelationGetRelid(parentIdx));
20620  if (OidIsValid(existingIdx))
20621  ereport(ERROR,
20622  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20623  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20624  RelationGetRelationName(partIdx),
20625  RelationGetRelationName(parentIdx)),
20626  errdetail("Another index is already attached for partition \"%s\".",
20627  RelationGetRelationName(partitionTbl))));
20628 }

References ereport, errcode(), errdetail(), errmsg(), ERROR, index_get_partition(), OidIsValid, RelationGetRelationName, and RelationGetRelid.

Referenced by ATExecAttachPartitionIdx().

◆ register_on_commit_action()

void register_on_commit_action ( Oid  relid,
OnCommitAction  action 
)

Definition at line 18181 of file tablecmds.c.

18182 {
18183  OnCommitItem *oc;
18184  MemoryContext oldcxt;
18185 
18186  /*
18187  * We needn't bother registering the relation unless there is an ON COMMIT
18188  * action we need to take.
18189  */
18191  return;
18192 
18194 
18195  oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18196  oc->relid = relid;
18197  oc->oncommit = action;
18200 
18201  /*
18202  * We use lcons() here so that ON COMMIT actions are processed in reverse
18203  * order of registration. That might not be essential but it seems
18204  * reasonable.
18205  */
18206  on_commits = lcons(oc, on_commits);
18207 
18208  MemoryContextSwitchTo(oldcxt);
18209 }
List * lcons(void *datum, List *list)
Definition: list.c:495
MemoryContext CacheMemoryContext
Definition: mcxt.c:152

References generate_unaccent_rules::action, CacheMemoryContext, OnCommitItem::creating_subid, OnCommitItem::deleting_subid, GetCurrentSubTransactionId(), InvalidSubTransactionId, lcons(), MemoryContextSwitchTo(), on_commits, OnCommitItem::oncommit, ONCOMMIT_NOOP, ONCOMMIT_PRESERVE_ROWS, palloc(), and OnCommitItem::relid.

Referenced by heap_create_with_catalog().

◆ relation_mark_replica_identity()

static void relation_mark_replica_identity ( Relation  rel,
char  ri_type,
Oid  indexOid,
bool  is_internal 
)
static

Definition at line 17341 of file tablecmds.c.

17343 {
17344  Relation pg_index;
17345  Relation pg_class;
17346  HeapTuple pg_class_tuple;
17347  HeapTuple pg_index_tuple;
17348  Form_pg_class pg_class_form;
17349  Form_pg_index pg_index_form;
17350  ListCell *index;
17351 
17352  /*
17353  * Check whether relreplident has changed, and update it if so.
17354  */
17355  pg_class = table_open(RelationRelationId, RowExclusiveLock);
17356  pg_class_tuple = SearchSysCacheCopy1(RELOID,
17358  if (!HeapTupleIsValid(pg_class_tuple))
17359  elog(ERROR, "cache lookup failed for relation \"%s\"",
17361  pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17362  if (pg_class_form->relreplident != ri_type)
17363  {
17364  pg_class_form->relreplident = ri_type;
17365  CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17366  }
17367  table_close(pg_class, RowExclusiveLock);
17368  heap_freetuple(pg_class_tuple);
17369 
17370  /*
17371  * Update the per-index indisreplident flags correctly.
17372  */
17373  pg_index = table_open(IndexRelationId, RowExclusiveLock);
17374  foreach(index, RelationGetIndexList(rel))
17375  {
17376  Oid thisIndexOid = lfirst_oid(index);
17377  bool dirty = false;
17378 
17379  pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17380  ObjectIdGetDatum(thisIndexOid));
17381  if (!HeapTupleIsValid(pg_index_tuple))
17382  elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17383  pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17384 
17385  if (thisIndexOid == indexOid)
17386  {
17387  /* Set the bit if not already set. */
17388  if (!pg_index_form->indisreplident)
17389  {
17390  dirty = true;
17391  pg_index_form->indisreplident = true;
17392  }
17393  }
17394  else
17395  {
17396  /* Unset the bit if set. */
17397  if (pg_index_form->indisreplident)
17398  {
17399  dirty = true;
17400  pg_index_form->indisreplident = false;
17401  }
17402  }
17403 
17404  if (dirty)
17405  {
17406  CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17407  InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17408  InvalidOid, is_internal);
17409 
17410  /*
17411  * Invalidate the relcache for the table, so that after we commit
17412  * all sessions will refresh the table's replica identity index
17413  * before attempting any UPDATE or DELETE on the table. (If we
17414  * changed the table's pg_class row above, then a relcache inval
17415  * is already queued due to that; but we might not have.)
17416  */
17418  }
17419  heap_freetuple(pg_index_tuple);
17420  }
17421 
17422  table_close(pg_index, RowExclusiveLock);
17423 }
Definition: type.h:95

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

14028 {
14029  Relation depRel;
14030  ScanKeyData key[3];
14031  SysScanDesc scan;
14032  HeapTuple depTup;
14033 
14034  Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
14035 
14036  depRel = table_open(DependRelationId, RowExclusiveLock);
14037 
14038  ScanKeyInit(&key[0],
14039  Anum_pg_depend_refclassid,
14040  BTEqualStrategyNumber, F_OIDEQ,
14041  ObjectIdGetDatum(RelationRelationId));
14042  ScanKeyInit(&key[1],
14043  Anum_pg_depend_refobjid,
14044  BTEqualStrategyNumber, F_OIDEQ,
14046  ScanKeyInit(&key[2],
14047  Anum_pg_depend_refobjsubid,
14048  BTEqualStrategyNumber, F_INT4EQ,
14050 
14051  scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14052  NULL, 3, key);
14053 
14054  while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14055  {
14056  Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14057  ObjectAddress foundObject;
14058 
14059  foundObject.classId = foundDep->classid;
14060  foundObject.objectId = foundDep->objid;
14061  foundObject.objectSubId = foundDep->objsubid;
14062 
14063  switch (foundObject.classId)
14064  {
14065  case RelationRelationId:
14066  {
14067  char relKind = get_rel_relkind(foundObject.objectId);
14068 
14069  if (relKind == RELKIND_INDEX ||
14070  relKind == RELKIND_PARTITIONED_INDEX)
14071  {
14072  Assert(foundObject.objectSubId == 0);
14073  RememberIndexForRebuilding(foundObject.objectId, tab);
14074  }
14075  else if (relKind == RELKIND_SEQUENCE)
14076  {
14077  /*
14078  * This must be a SERIAL column's sequence. We need
14079  * not do anything to it.
14080  */
14081  Assert(foundObject.objectSubId == 0);
14082  }
14083  else
14084  {
14085  /* Not expecting any other direct dependencies... */
14086  elog(ERROR, "unexpected object depending on column: %s",
14087  getObjectDescription(&foundObject, false));
14088  }
14089  break;
14090  }
14091 
14092  case ConstraintRelationId:
14093  Assert(foundObject.objectSubId == 0);
14094  RememberConstraintForRebuilding(foundObject.objectId, tab);
14095  break;
14096 
14097  case RewriteRelationId:
14098  /* XXX someday see if we can cope with revising views */
14099  if (subtype == AT_AlterColumnType)
14100  ereport(ERROR,
14101  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14102  errmsg("cannot alter type of a column used by a view or rule"),
14103  errdetail("%s depends on column \"%s\"",
14104  getObjectDescription(&foundObject, false),
14105  colName)));
14106  break;
14107 
14108  case TriggerRelationId:
14109 
14110  /*
14111  * A trigger can depend on a column because the column is
14112  * specified as an update target, or because the column is
14113  * used in the trigger's WHEN condition. The first case would
14114  * not require any extra work, but the second case would
14115  * require updating the WHEN expression, which will take a
14116  * significant amount of new code. Since we can't easily tell
14117  * which case applies, we punt for both. FIXME someday.
14118  */
14119  if (subtype == AT_AlterColumnType)
14120  ereport(ERROR,
14121  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14122  errmsg("cannot alter type of a column used in a trigger definition"),
14123  errdetail("%s depends on column \"%s\"",
14124  getObjectDescription(&foundObject, false),
14125  colName)));
14126  break;
14127 
14128  case PolicyRelationId:
14129 
14130  /*
14131  * A policy can depend on a column because the column is
14132  * specified in the policy's USING or WITH CHECK qual
14133  * expressions. It might be possible to rewrite and recheck
14134  * the policy expression, but punt for now. It's certainly
14135  * easy enough to remove and recreate the policy; still, FIXME
14136  * someday.
14137  */
14138  if (subtype == AT_AlterColumnType)
14139  ereport(ERROR,
14140  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14141  errmsg("cannot alter type of a column used in a policy definition"),
14142  errdetail("%s depends on column \"%s\"",
14143  getObjectDescription(&foundObject, false),
14144  colName)));
14145  break;
14146 
14147  case AttrDefaultRelationId:
14148  {
14150 
14151  if (col.objectId == RelationGetRelid(rel) &&
14152  col.objectSubId == attnum)
14153  {
14154  /*
14155  * Ignore the column's own default expression. The
14156  * caller deals with it.
14157  */
14158  }
14159  else
14160  {
14161  /*
14162  * This must be a reference from the expression of a
14163  * generated column elsewhere in the same table.
14164  * Changing the type/generated expression of a column
14165  * that is used by a generated column is not allowed
14166  * by SQL standard, so just punt for now. It might be
14167  * doable with some thinking and effort.
14168  */
14169  if (subtype == AT_AlterColumnType)
14170  ereport(ERROR,
14171  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14172  errmsg("cannot alter type of a column used by a generated column"),
14173  errdetail("Column \"%s\" is used by generated column \"%s\".",
14174  colName,
14175  get_attname(col.objectId,
14176  col.objectSubId,
14177  false))));
14178  }
14179  break;
14180  }
14181 
14182  case StatisticExtRelationId:
14183 
14184  /*
14185  * Give the extended-stats machinery a chance to fix anything
14186  * that this column type change would break.
14187  */
14188  RememberStatisticsForRebuilding(foundObject.objectId, tab);
14189  break;
14190 
14191  default:
14192 
14193  /*
14194  * We don't expect any other sorts of objects to depend on a
14195  * column.
14196  */
14197  elog(ERROR, "unexpected object depending on column: %s",
14198  getObjectDescription(&foundObject, false));
14199  break;
14200  }
14201  }
14202 
14203  systable_endscan(scan);
14204  table_close(depRel, NoLock);
14205 }
ObjectAddress GetAttrDefaultColumnAddress(Oid attrdefoid)
Definition: pg_attrdef.c:381
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:14283
static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
Definition: tablecmds.c:14334
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
Definition: tablecmds.c:14243

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

14228 {
14229  if (!get_index_isclustered(indoid))
14230  return;
14231 
14232  if (tab->clusterOnIndex)
14233  elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14234 
14235  tab->clusterOnIndex = get_rel_name(indoid);
14236 }
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3601

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

14244 {
14245  /*
14246  * This de-duplication check is critical for two independent reasons: we
14247  * mustn't try to recreate the same constraint twice, and if a constraint
14248  * depends on more than one column whose type is to be altered, we must
14249  * capture its definition string before applying any of the column type
14250  * changes. ruleutils.c will get confused if we ask again later.
14251  */
14252  if (!list_member_oid(tab->changedConstraintOids, conoid))
14253  {
14254  /* OK, capture the constraint's existing definition string */
14255  char *defstring = pg_get_constraintdef_command(conoid);
14256  Oid indoid;
14257 
14259  conoid);
14261  defstring);
14262 
14263  /*
14264  * For the index of a constraint, if any, remember if it is used for
14265  * the table's replica identity or if it is a clustered index, so that
14266  * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14267  * those properties.
14268  */
14269  indoid = get_constraint_index(conoid);
14270  if (OidIsValid(indoid))
14271  {
14273  RememberClusterOnForRebuilding(indoid, tab);
14274  }
14275  }
14276 }
char * pg_get_constraintdef_command(Oid constraintId)
Definition: ruleutils.c:2168
static void RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:14212
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:14227

References AlteredTableInfo::changedConstraintDefs, AlteredTableInfo::changedConstraintOids, get_constraint_index(), lappend(), lappend_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 14283 of file tablecmds.c.

14284 {
14285  /*
14286  * This de-duplication check is critical for two independent reasons: we
14287  * mustn't try to recreate the same index twice, and if an index depends
14288  * on more than one column whose type is to be altered, we must capture
14289  * its definition string before applying any of the column type changes.
14290  * ruleutils.c will get confused if we ask again later.
14291  */
14292  if (!list_member_oid(tab->changedIndexOids, indoid))
14293  {
14294  /*
14295  * Before adding it as an index-to-rebuild, we'd better see if it
14296  * belongs to a constraint, and if so rebuild the constraint instead.
14297  * Typically this check fails, because constraint indexes normally
14298  * have only dependencies on their constraint. But it's possible for
14299  * such an index to also have direct dependencies on table columns,
14300  * for example with a partial exclusion constraint.
14301  */
14302  Oid conoid = get_index_constraint(indoid);
14303 
14304  if (OidIsValid(conoid))
14305  {
14306  RememberConstraintForRebuilding(conoid, tab);
14307  }
14308  else
14309  {
14310  /* OK, capture the index's existing definition string */
14311  char *defstring = pg_get_indexdef_string(indoid);
14312 
14314  indoid);
14316  defstring);
14317 
14318  /*
14319  * Remember if this index is used for the table's replica identity
14320  * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14321  * can queue up commands necessary to restore those properties.
14322  */
14324  RememberClusterOnForRebuilding(indoid, tab);
14325  }
14326  }
14327 }
Oid get_index_constraint(Oid indexId)
Definition: pg_depend.c:968
char * pg_get_indexdef_string(Oid indexrelid)
Definition: ruleutils.c:1209

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

14213 {
14214  if (!get_index_isreplident(indoid))
14215  return;
14216 
14217  if (tab->replicaIdentityIndex)
14218  elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14219 
14220  tab->replicaIdentityIndex = get_rel_name(indoid);
14221 }
bool get_index_isreplident(Oid index_oid)
Definition: lsyscache.c:3555

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

14335 {
14336  /*
14337  * This de-duplication check is critical for two independent reasons: we
14338  * mustn't try to recreate the same statistics object twice, and if the
14339  * statistics object depends on more than one column whose type is to be
14340  * altered, we must capture its definition string before applying any of
14341  * the type changes. ruleutils.c will get confused if we ask again later.
14342  */
14343  if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14344  {
14345  /* OK, capture the statistics object's existing definition string */
14346  char *defstring = pg_get_statisticsobjdef_string(stxoid);
14347 
14349  stxoid);
14351  defstring);
14352  }
14353 }
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition: ruleutils.c:1611

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

18218 {
18219  ListCell *l;
18220 
18221  foreach(l, on_commits)
18222  {
18223  OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18224 
18225  if (oc->relid == relid)
18226  {
18228  break;
18229  }
18230  }
18231 }

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

16862 {
16863  Relation catalogRelation;
16864  SysScanDesc scan;
16865  ScanKeyData key[3];
16866  HeapTuple attributeTuple,
16867  constraintTuple;
16868  List *connames;
16869  List *nncolumns;
16870  bool found;
16871  bool is_partitioning;
16872 
16873  is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16874 
16875  found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16876  RelationGetRelid(parent_rel),
16877  expect_detached,
16878  RelationGetRelationName(child_rel));
16879  if (!found)
16880  {
16881  if (is_partitioning)
16882  ereport(ERROR,
16884  errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16885  RelationGetRelationName(child_rel),
16886  RelationGetRelationName(parent_rel))));
16887  else
16888  ereport(ERROR,
16890  errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16891  RelationGetRelationName(parent_rel),
16892  RelationGetRelationName(child_rel))));
16893  }
16894 
16895  /*
16896  * Search through child columns looking for ones matching parent rel
16897  */
16898  catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
16899  ScanKeyInit(&key[0],
16900  Anum_pg_attribute_attrelid,
16901  BTEqualStrategyNumber, F_OIDEQ,
16902  ObjectIdGetDatum(RelationGetRelid(child_rel)));
16903  scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16904  true, NULL, 1, key);
16905  while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16906  {
16907  Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16908 
16909  /* Ignore if dropped or not inherited */
16910  if (att->attisdropped)
16911  continue;
16912  if (att->attinhcount <= 0)
16913  continue;
16914 
16916  NameStr(att->attname)))
16917  {
16918  /* Decrement inhcount and possibly set islocal to true */
16919  HeapTuple copyTuple = heap_copytuple(attributeTuple);
16920  Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16921 
16922  copy_att->attinhcount--;
16923  if (copy_att->attinhcount == 0)
16924  copy_att->attislocal = true;
16925 
16926  CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
16927  heap_freetuple(copyTuple);
16928  }
16929  }
16930  systable_endscan(scan);
16931  table_close(catalogRelation, RowExclusiveLock);
16932 
16933  /*
16934  * Likewise, find inherited check constraints and disinherit them. To do
16935  * this, we first need a list of the names of the parent's check
16936  * constraints. (We cheat a bit by only checking for name matches,
16937  * assuming that the expressions will match.)
16938  *
16939  * For NOT NULL columns, we store column numbers to match.
16940  */
16941  catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
16942  ScanKeyInit(&key[0],
16943  Anum_pg_constraint_conrelid,
16944  BTEqualStrategyNumber, F_OIDEQ,
16945  ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16946  scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16947  true, NULL, 1, key);
16948 
16949  connames = NIL;
16950  nncolumns = NIL;
16951 
16952  while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16953  {
16954  Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16955 
16956  if (con->contype == CONSTRAINT_CHECK)
16957  connames = lappend(connames, pstrdup(NameStr(con->conname)));
16958  if (con->contype == CONSTRAINT_NOTNULL)
16959  nncolumns = lappend_int(nncolumns, extractNotNullColumn(constraintTuple));
16960  }
16961 
16962  systable_endscan(scan);
16963 
16964  /* Now scan the child's constraints */
16965  ScanKeyInit(&key[0],
16966  Anum_pg_constraint_conrelid,
16967  BTEqualStrategyNumber, F_OIDEQ,
16968  ObjectIdGetDatum(RelationGetRelid(child_rel)));
16969  scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16970  true, NULL, 1, key);
16971 
16972  while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16973  {
16974  Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16975  bool match = false;
16976  ListCell *lc;
16977 
16978  /*
16979  * Match CHECK constraints by name, not-null constraints by column
16980  * number, and ignore all others.
16981  */
16982  if (con->contype == CONSTRAINT_CHECK)
16983  {
16984  foreach(lc, connames)
16985  {
16986  if (con->contype == CONSTRAINT_CHECK &&
16987  strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
16988  {
16989  match = true;
16990  break;
16991  }
16992  }
16993  }
16994  else if (con->contype == CONSTRAINT_NOTNULL)
16995  {
16996  AttrNumber child_attno = extractNotNullColumn(constraintTuple);
16997 
16998  foreach(lc, nncolumns)
16999  {
17000  if (lfirst_int(lc) == child_attno)
17001  {
17002  match = true;
17003  break;
17004  }
17005  }
17006  }
17007  else
17008  continue;
17009 
17010  if (match)
17011  {
17012  /* Decrement inhcount and possibly set islocal to true */
17013  HeapTuple copyTuple = heap_copytuple(constraintTuple);
17014  Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
17015 
17016  if (copy_con->coninhcount <= 0) /* shouldn't happen */
17017  elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
17018  RelationGetRelid(child_rel), NameStr(copy_con->conname));
17019 
17020  copy_con->coninhcount--;
17021  if (copy_con->coninhcount == 0)
17022  copy_con->conislocal = true;
17023 
17024  CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
17025  heap_freetuple(copyTuple);
17026  }
17027  }
17028 
17029  systable_endscan(scan);
17030  table_close(catalogRelation, RowExclusiveLock);
17031 
17033  RelationRelationId,
17034  RelationGetRelid(parent_rel),
17035  child_dependency_type(is_partitioning));
17036 
17037  /*
17038  * Post alter hook of this inherits. Since object_access_hook doesn't take
17039  * multiple object identifiers, we relay oid of parent relation using
17040  * auxiliary_id argument.
17041  */
17042  InvokeObjectPostAlterHookArg(InheritsRelationId,
17043  RelationGetRelid(child_rel), 0,
17044  RelationGetRelid(parent_rel), false);
17045 }
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:401
#define child_dependency_type(child_is_partition)
Definition: tablecmds.c:354

References BTEqualStrategyNumber, CatalogTupleUpdate(), child_dependency_type, DeleteInheritsTuple(), drop_parent_dependency(), elog, ereport, errcode(), ERRCODE_UNDEFINED_TABLE, errmsg(), ERROR, extractNotNullColumn(), GETSTRUCT, heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHookArg, sort-test::key, lappend(), lappend_int(), lfirst, lfirst_int, NameStr, NIL, ObjectIdGetDatum(), pstrdup(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), SearchSysCacheExistsAttName(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecDetachPartition(), ATExecDropInherit(), ATExecMergePartitions(), ATExecSplitPartition(), and DetachPartitionFinalize().

◆ RemoveRelations()

void RemoveRelations ( DropStmt drop)

Definition at line 1484 of file tablecmds.c.

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

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

3987 {
3988  Relation targetrelation = NULL;
3989  Oid constraintOid;
3990  HeapTuple tuple;
3991  Form_pg_constraint con;
3992  ObjectAddress address;
3993 
3994  Assert(!myrelid || !mytypid);
3995 
3996  if (mytypid)
3997  {
3998  constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
3999  }
4000  else
4001  {
4002  targetrelation = relation_open(myrelid, AccessExclusiveLock);
4003 
4004  /*
4005  * don't tell it whether we're recursing; we allow changing typed
4006  * tables here
4007  */
4008  renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4009 
4010  constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4011  }
4012 
4013  tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4014  if (!HeapTupleIsValid(tuple))
4015  elog(ERROR, "cache lookup failed for constraint %u",
4016  constraintOid);
4017  con = (Form_pg_constraint) GETSTRUCT(tuple);
4018 
4019  if (myrelid &&
4020  (con->contype == CONSTRAINT_CHECK ||
4021  con->contype == CONSTRAINT_NOTNULL) &&
4022  !con->connoinherit)
4023  {
4024  if (recurse)
4025  {
4026  List *child_oids,
4027  *child_numparents;
4028  ListCell *lo,
4029  *li;
4030 
4031  child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4032  &child_numparents);
4033 
4034  forboth(lo, child_oids, li, child_numparents)
4035  {
4036  Oid childrelid = lfirst_oid(lo);
4037  int numparents = lfirst_int(li);
4038 
4039  if (childrelid == myrelid)
4040  continue;
4041 
4042  rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4043  }
4044  }
4045  else
4046  {
4047  if (expected_parents == 0 &&
4048  find_inheritance_children(myrelid, NoLock) != NIL)
4049  ereport(ERROR,
4050  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4051  errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4052  oldconname)));
4053  }
4054 
4055  if (con->coninhcount > expected_parents)
4056  ereport(ERROR,
4057  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4058  errmsg("cannot rename inherited constraint \"%s\"",
4059  oldconname)));
4060  }
4061 
4062  if (con->conindid
4063  && (con->contype == CONSTRAINT_PRIMARY
4064  || con->contype == CONSTRAINT_UNIQUE
4065  || con->contype == CONSTRAINT_EXCLUSION))
4066  /* rename the index; this renames the constraint as well */
4067  RenameRelationInternal(con->conindid, newconname, false, true);
4068  else
4069  RenameConstraintById(constraintOid, newconname);
4070 
4071  ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4072 
4073  ReleaseSysCache(tuple);
4074 
4075  if (targetrelation)
4076  {
4077  /*
4078  * Invalidate relcache so as others can see the new constraint name.
4079  */
4080  CacheInvalidateRelcache(targetrelation);
4081 
4082  relation_close(targetrelation, NoLock); /* close rel but keep lock */
4083  }
4084 
4085  return address;
4086 }
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:3980

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(), renameatt_check(), RenameConstraintById(), RenameRelationInternal(), and SearchSysCache1().

Referenced by RenameConstraint().

◆ renameatt()

ObjectAddress renameatt ( RenameStmt stmt)

Definition at line 3942 of file tablecmds.c.

3943 {
3944  Oid relid;
3946  ObjectAddress address;
3947 
3948  /* lock level taken here should match renameatt_internal */
3950  stmt->missing_ok ? RVR_MISSING_OK : 0,
3952  NULL);
3953 
3954  if (!OidIsValid(relid))
3955  {
3956  ereport(NOTICE,
3957  (errmsg("relation \"%s\" does not exist, skipping",
3958  stmt->relation->relname)));
3959  return InvalidObjectAddress;
3960  }
3961 
3962  attnum =
3963  renameatt_internal(relid,
3964  stmt->subname, /* old att name */
3965  stmt->newname, /* new att name */
3966  stmt->relation->inh, /* recursive? */
3967  false, /* recursing? */
3968  0, /* expected inhcount */
3969  stmt->behavior);
3970 
3971  ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3972 
3973  return address;
3974 }
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition: tablecmds.c:3777
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:3922

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

3729 {
3730  char relkind = classform->relkind;
3731 
3732  if (classform->reloftype && !recursing)
3733  ereport(ERROR,
3734  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3735  errmsg("cannot rename column of typed table")));
3736 
3737  /*
3738  * Renaming the columns of sequences or toast tables doesn't actually
3739  * break anything from the system's point of view, since internal
3740  * references are by attnum. But it doesn't seem right to allow users to
3741  * change names that are hardcoded into the system, hence the following
3742  * restriction.
3743  */
3744  if (relkind != RELKIND_RELATION &&
3745  relkind != RELKIND_VIEW &&
3746  relkind != RELKIND_MATVIEW &&
3747  relkind != RELKIND_COMPOSITE_TYPE &&
3748  relkind != RELKIND_INDEX &&
3749  relkind != RELKIND_PARTITIONED_INDEX &&
3750  relkind != RELKIND_FOREIGN_TABLE &&
3751  relkind != RELKIND_PARTITIONED_TABLE)
3752  ereport(ERROR,
3753  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3754  errmsg("cannot rename columns of relation \"%s\"",
3755  NameStr(classform->relname)),
3757 
3758  /*
3759  * permissions checking. only the owner of a class can change its schema.
3760  */
3761  if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3763  NameStr(classform->relname));
3764  if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3765  ereport(ERROR,
3766  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3767  errmsg("permission denied: \"%s\" is a system catalog",
3768  NameStr(classform->relname))));
3769 }

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

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

Referenced by renameatt().

◆ RenameConstraint()

ObjectAddress RenameConstraint ( RenameStmt stmt)

Definition at line 4089 of file tablecmds.c.

4090 {
4091  Oid relid = InvalidOid;
4092  Oid typid = InvalidOid;
4093 
4094  if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4095  {
4096  Relation rel;
4097  HeapTuple tup;
4098 
4099  typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4100  rel = table_open(TypeRelationId, RowExclusiveLock);
4101  tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4102  if (!HeapTupleIsValid(tup))
4103  elog(ERROR, "cache lookup failed for type %u", typid);
4104  checkDomainOwner(tup);
4105  ReleaseSysCache(tup);
4106  table_close(rel, NoLock);
4107  }
4108  else
4109  {
4110  /* lock level taken here should match rename_constraint_internal */
4112  stmt->missing_ok ? RVR_MISSING_OK : 0,
4114  NULL);
4115  if (!OidIsValid(relid))
4116  {
4117  ereport(NOTICE,
4118  (errmsg("relation \"%s\" does not exist, skipping",
4119  stmt->relation->relname)));
4120  return InvalidObjectAddress;
4121  }
4122  }
4123 
4124  return
4125  rename_constraint_internal(relid, typid,
4126  stmt->subname,
4127  stmt->newname,
4128  (stmt->relation &&
4129  stmt->relation->inh), /* recursive? */
4130  false, /* recursing? */
4131  0 /* expected inhcount */ );
4132 }
void checkDomainOwner(HeapTuple tup)
Definition: typecmds.c:3490

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

4140 {
4141  bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4142  Oid relid;
4143  ObjectAddress address;
4144 
4145  /*
4146  * Grab an exclusive lock on the target table, index, sequence, view,
4147  * materialized view, or foreign table, which we will NOT release until
4148  * end of transaction.
4149  *
4150  * Lock level used here should match RenameRelationInternal, to avoid lock
4151  * escalation. However, because ALTER INDEX can be used with any relation
4152  * type, we mustn't believe without verification.
4153  */
4154  for (;;)
4155  {
4156  LOCKMODE lockmode;
4157  char relkind;
4158  bool obj_is_index;
4159 
4160  lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4161 
4162  relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4163  stmt->missing_ok ? RVR_MISSING_OK : 0,
4165  (void *) stmt);
4166 
4167  if (!OidIsValid(relid))
4168  {
4169  ereport(NOTICE,
4170  (errmsg("relation \"%s\" does not exist, skipping",
4171  stmt->relation->relname)));
4172  return InvalidObjectAddress;
4173  }
4174 
4175  /*
4176  * We allow mismatched statement and object types (e.g., ALTER INDEX
4177  * to rename a table), but we might've used the wrong lock level. If
4178  * that happens, retry with the correct lock level. We don't bother
4179  * if we already acquired AccessExclusiveLock with an index, however.
4180  */
4181  relkind = get_rel_relkind(relid);
4182  obj_is_index = (relkind == RELKIND_INDEX ||
4183  relkind == RELKIND_PARTITIONED_INDEX);
4184  if (obj_is_index || is_index_stmt == obj_is_index)
4185  break;
4186 
4187  UnlockRelationOid(relid, lockmode);
4188  is_index_stmt = obj_is_index;
4189  }
4190 
4191  /* Do the work */
4192  RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4193 
4194  ObjectAddressSet(address, RelationRelationId, relid);
4195 
4196  return address;
4197 }

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

4204 {
4205  Relation targetrelation;
4206  Relation relrelation; /* for RELATION relation */
4207  HeapTuple reltup;
4208  Form_pg_class relform;
4209  Oid namespaceId;
4210 
4211  /*
4212  * Grab a lock on the target relation, which we will NOT release until end
4213  * of transaction. We need at least a self-exclusive lock so that
4214  * concurrent DDL doesn't overwrite the rename if they start updating
4215  * while still seeing the old version. The lock also guards against
4216  * triggering relcache reloads in concurrent sessions, which might not
4217  * handle this information changing under them. For indexes, we can use a
4218  * reduced lock level because RelationReloadIndexInfo() handles indexes
4219  * specially.
4220  */
4221  targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4222  namespaceId = RelationGetNamespace(targetrelation);
4223 
4224  /*
4225  * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4226  */
4227  relrelation = table_open(RelationRelationId, RowExclusiveLock);
4228 
4229  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4230  if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4231  elog(ERROR, "cache lookup failed for relation %u", myrelid);
4232  relform = (Form_pg_class) GETSTRUCT(reltup);
4233 
4234  if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4235  ereport(ERROR,
4236  (errcode(ERRCODE_DUPLICATE_TABLE),
4237  errmsg("relation \"%s\" already exists",
4238  newrelname)));
4239 
4240  /*
4241  * RenameRelation is careful not to believe the caller's idea of the
4242  * relation kind being handled. We don't have to worry about this, but
4243  * let's not be totally oblivious to it. We can process an index as
4244  * not-an-index, but not the other way around.
4245  */
4246  Assert(!is_index ||
4247  is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4248  targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4249 
4250  /*
4251  * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4252  * because it's a copy...)
4253  */
4254  namestrcpy(&(relform->relname), newrelname);
4255 
4256  CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4257 
4258  InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4259  InvalidOid, is_internal);
4260 
4261  heap_freetuple(reltup);
4262  table_close(relrelation, RowExclusiveLock);
4263 
4264  /*
4265  * Also rename the associated type, if any.
4266  */
4267  if (OidIsValid(targetrelation->rd_rel->reltype))
4268  RenameTypeInternal(targetrelation->rd_rel->reltype,
4269  newrelname, namespaceId);
4270 
4271  /*
4272  * Also rename the associated constraint, if any.
4273  */
4274  if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4275  targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4276  {
4277  Oid constraintId = get_index_constraint(myrelid);
4278 
4279  if (OidIsValid(constraintId))
4280  RenameConstraintById(constraintId, newrelname);
4281  }
4282 
4283  /*
4284  * Close rel, but keep lock!
4285  */
4286  relation_close(targetrelation, NoLock);
4287 }
void RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
Definition: pg_type.c:765

References AccessExclusiveLock, Assert, CatalogTupleUpdate(), elog, ereport, errcode(), errmsg(), ERROR, get_index_constraint(), get_relname_relid(), GETSTRUCT, heap_freetuple(), HeapTupleIsValid, InvalidOid, InvokeObjectPostAlterHookArg, namestrcpy(), NoLock, ObjectIdGetDatum(), OidIsValid, RelationData::rd_rel, relation_close(), relation_open(), RelationGetNamespace, RenameConstraintById(), RenameTypeInternal(), RowExclusiveLock, SearchSysCacheCopy1, ShareUpdateExclusiveLock, HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecAddIndexConstraint(), ATExecMergePartitions(), ATExecSplitPartition(), finish_heap_swap(), rename_constraint_internal(), RenameRelation(), and RenameType().

◆ ResetRelRewrite()

void ResetRelRewrite ( Oid  myrelid)

Definition at line 4293 of file tablecmds.c.

4294 {
4295  Relation relrelation; /* for RELATION relation */
4296  HeapTuple reltup;
4297  Form_pg_class relform;
4298 
4299  /*
4300  * Find relation's pg_class tuple.
4301  */
4302  relrelation = table_open(RelationRelationId, RowExclusiveLock);
4303 
4304  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4305  if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4306  elog(ERROR, "cache lookup failed for relation %u", myrelid);
4307  relform = (Form_pg_class) GETSTRUCT(reltup);
4308 
4309  /*
4310  * Update pg_class tuple.
4311  */
4312  relform->relrewrite = InvalidOid;
4313 
4314  CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4315 
4316  heap_freetuple(reltup);
4317  table_close(relrelation, RowExclusiveLock);
4318 }

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 bool set_attnotnull ( List **  wqueue,
Relation  rel,
AttrNumber  attnum,
bool  recurse,
LOCKMODE  lockmode 
)
static

Definition at line 7732 of file tablecmds.c.

7734 {
7735  HeapTuple tuple;
7736  Form_pg_attribute attForm;
7737  bool retval = false;
7738 
7739  /* Guard against stack overflow due to overly deep inheritance tree. */
7741 
7743  if (!HeapTupleIsValid(tuple))
7744  elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7745  attnum, RelationGetRelid(rel));
7746  attForm = (Form_pg_attribute) GETSTRUCT(tuple);
7747  if (!attForm->attnotnull)
7748  {
7749  Relation attr_rel;
7750 
7751  attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7752 
7753  attForm->attnotnull = true;
7754  CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7755 
7756  table_close(attr_rel, RowExclusiveLock);
7757 
7758  /*
7759  * And set up for existing values to be checked, unless another
7760  * constraint already proves this.
7761  */
7762  if (wqueue && !NotNullImpliedByRelConstraints(rel, attForm))
7763  {
7764  AlteredTableInfo *tab;
7765 
7766  tab = ATGetQueueEntry(wqueue, rel);
7767  tab->verify_new_notnull = true;
7768  }
7769 
7770  retval = true;
7771  }
7772 
7773  if (recurse)
7774  {
7775  List *children;
7776  ListCell *lc;
7777 
7778  /* Make above update visible, for multiple inheritance cases */
7779  if (retval)
7781 
7782  children = find_inheritance_children(RelationGetRelid(rel), lockmode);
7783  foreach(lc, children)
7784  {
7785  Oid childrelid = lfirst_oid(lc);
7786  Relation childrel;
7787  AttrNumber childattno;
7788 
7789  /* find_inheritance_children already got lock */
7790  childrel = table_open(childrelid, NoLock);
7791  CheckTableNotInUse(childrel, "ALTER TABLE");
7792 
7793  childattno = get_attnum(RelationGetRelid(childrel),
7795  false));
7796  retval |= set_attnotnull(wqueue, childrel, childattno,
7797  recurse, lockmode);
7798  table_close(childrel, NoLock);
7799  }
7800  }
7801 
7802  return retval;
7803 }
HeapTuple SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
Definition: syscache.c:445
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition: tablecmds.c:8049

References ATGetQueueEntry(), attnum, CatalogTupleUpdate(), check_stack_depth(), CheckTableNotInUse(), CommandCounterIncrement(), elog, ERROR, find_inheritance_children(), get_attname(), get_attnum(), GETSTRUCT, HeapTupleIsValid, lfirst_oid, NoLock, NotNullImpliedByRelConstraints(), RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttNum(), HeapTupleData::t_self, table_close(), table_open(), and AlteredTableInfo::verify_new_notnull.

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

◆ SetIndexStorageProperties()

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

Definition at line 9002 of file tablecmds.c.

9007 {
9008  ListCell *lc;
9009 
9010  foreach(lc, RelationGetIndexList(rel))
9011  {
9012  Oid indexoid = lfirst_oid(lc);
9013  Relation indrel;
9014  AttrNumber indattnum = 0;
9015  HeapTuple tuple;
9016 
9017  indrel = index_open(indexoid, lockmode);
9018 
9019  for (int i = 0; i < indrel->rd_index->indnatts; i++)
9020  {
9021  if (indrel->rd_index->indkey.values[i] == attnum)
9022  {
9023  indattnum = i + 1;
9024  break;
9025  }
9026  }
9027 
9028  if (indattnum == 0)
9029  {
9030  index_close(indrel, lockmode);
9031  continue;
9032  }
9033 
9034  tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9035 
9036  if (HeapTupleIsValid(tuple))
9037  {
9038  Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9039 
9040  if (setstorage)
9041  attrtuple->attstorage = newstorage;
9042 
9043  if (setcompression)
9044  attrtuple->attcompression = newcompression;
9045 
9046  CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9047 
9048  InvokeObjectPostAlterHook(RelationRelationId,
9049  RelationGetRelid(rel),
9050  attrtuple->attnum);
9051 
9052  heap_freetuple(tuple);
9053  }
9054 
9055  index_close(indrel, lockmode);
9056  }
9057 }

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

3589 {
3590  Relation relationRelation;
3591  HeapTuple tuple;
3592  Form_pg_class classtuple;
3593 
3594  /*
3595  * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3596  */
3597  relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3598  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3599  if (!HeapTupleIsValid(tuple))
3600  elog(ERROR, "cache lookup failed for relation %u", relationId);
3601  classtuple = (Form_pg_class) GETSTRUCT(tuple);
3602 
3603  if (classtuple->relhassubclass != relhassubclass)
3604  {
3605  classtuple->relhassubclass = relhassubclass;
3606  CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3607  }
3608  else
3609  {
3610  /* no need to change tuple, but force relcache rebuild anyway */
3612  }
3613 
3614  heap_freetuple(tuple);
3615  table_close(relationRelation, RowExclusiveLock);
3616 }
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1396

References CacheInvalidateRelcacheByTuple(), CatalogTupleUpdate(), elog, ERROR, GETSTRUCT, heap_freetuple(), HeapTupleIsValid, ObjectIdGetDatum(), RowExclusiveLock, SearchSysCacheCopy1, 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 3686 of file tablecmds.c.

3689 {
3690  Relation pg_class;
3691  HeapTuple tuple;
3692  Form_pg_class rd_rel;
3693  Oid reloid = RelationGetRelid(rel);
3694 
3695  Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3696 
3697  /* Get a modifiable copy of the relation's pg_class row. */
3698  pg_class = table_open(RelationRelationId, RowExclusiveLock);
3699 
3700  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3701  if (!HeapTupleIsValid(tuple))
3702  elog(ERROR, "cache lookup failed for relation %u", reloid);
3703  rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3704 
3705  /* Update the pg_class row. */
3706  rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3707  InvalidOid : newTableSpaceId;
3708  if (RelFileNumberIsValid(newRelFilenumber))
3709  rd_rel->relfilenode = newRelFilenumber;
3710  CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3711 
3712  /*
3713  * Record dependency on tablespace. This is only required for relations
3714  * that have no physical storage.
3715  */
3716  if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3717  changeDependencyOnTablespace(RelationRelationId, reloid,
3718  rd_rel->reltablespace);
3719 
3720  heap_freetuple(tuple);
3721  table_close(pg_class, RowExclusiveLock);
3722 }
void changeDependencyOnTablespace(Oid classId, Oid objectId, Oid newTablespaceId)
Definition: pg_shdepend.c:377

References Assert, CatalogTupleUpdate(), changeDependencyOnTablespace(), CheckRelationTableSpaceMove(), elog, ERROR, GETSTRUCT, heap_freetuple(), HeapTupleIsValid, InvalidOid, MyDatabaseTableSpace, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetRelid, RelFileNumberIsValid, RowExclusiveLock, SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), and table_open().

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

◆ storage_name()

static const char * storage_name ( char  c)
static

Definition at line 2407 of file tablecmds.c.

2408 {
2409  switch (c)
2410  {
2411  case TYPSTORAGE_PLAIN:
2412  return "PLAIN";
2413  case TYPSTORAGE_EXTERNAL:
2414  return "EXTERNAL";
2415  case TYPSTORAGE_EXTENDED:
2416  return "EXTENDED";
2417  case TYPSTORAGE_MAIN:
2418  return "MAIN";
2419  default:
2420  return "???";
2421  }
2422 }
char * c

Referenced by MergeChildAttribute(), and MergeInheritedAttribute().

◆ StoreCatalogInheritance()

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

Definition at line 3469 of file tablecmds.c.

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

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

3516 {
3517  ObjectAddress childobject,
3518  parentobject;
3519 
3520  /* store the pg_inherits row */
3521  StoreSingleInheritance(relationId, parentOid, seqNumber);
3522 
3523  /*
3524  * Store a dependency too
3525  */
3526  parentobject.classId = RelationRelationId;
3527  parentobject.objectId = parentOid;
3528  parentobject.objectSubId = 0;
3529  childobject.classId = RelationRelationId;
3530  childobject.objectId = relationId;
3531  childobject.objectSubId = 0;
3532 
3533  recordDependencyOn(&childobject, &parentobject,
3534  child_dependency_type(child_is_partition));
3535 
3536  /*
3537  * Post creation hook of this inheritance. Since object_access_hook
3538  * doesn't take multiple object identifiers, we relay oid of parent
3539  * relation using auxiliary_id argument.
3540  */
3541  InvokeObjectPostAlterHookArg(InheritsRelationId,
3542  relationId, 0,
3543  parentOid, false);
3544 
3545  /*
3546  * Mark the parent as having subclasses.
3547  */
3548  SetRelationHasSubclass(parentOid, true);
3549 }
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
Definition: pg_inherits.c:508
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition: tablecmds.c:3588

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 
)
static

Definition at line 12189 of file tablecmds.c.

12191 {
12192  ListCell *l;
12193  int attnum;
12194 
12195  attnum = 0;
12196  foreach(l, colList)
12197  {
12198  char *attname = strVal(lfirst(l));
12199  HeapTuple atttuple;
12200  Form_pg_attribute attform;
12201 
12202  atttuple = SearchSysCacheAttName(relId, attname);
12203  if (!HeapTupleIsValid(atttuple))
12204  ereport(ERROR,
12205  (errcode(ERRCODE_UNDEFINED_COLUMN),
12206  errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12207  attname)));
12208  attform = (Form_pg_attribute) GETSTRUCT(atttuple);
12209  if (attform->attnum < 0)
12210  ereport(ERROR,
12211  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12212  errmsg("system columns cannot be used in foreign keys")));
12213  if (attnum >= INDEX_MAX_KEYS)
12214  ereport(ERROR,
12215  (errcode(ERRCODE_TOO_MANY_COLUMNS),
12216  errmsg("cannot have more than %d keys in a foreign key",
12217  INDEX_MAX_KEYS)));
12218  attnums[attnum] = attform->attnum;
12219  if (atttypids != NULL)
12220  atttypids[attnum] = attform->atttypid;
12221  ReleaseSysCache(atttuple);
12222  attnum++;
12223  }
12224 
12225  return attnum;
12226 }

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

12348 {
12349  Oid indexoid = InvalidOid;
12350  bool found = false;
12351  bool found_deferrable = false;
12352  List *indexoidlist;
12353  ListCell *indexoidscan;
12354  int i,
12355  j;
12356 
12357  /*
12358  * Reject duplicate appearances of columns in the referenced-columns list.
12359  * Such a case is forbidden by the SQL standard, and even if we thought it
12360  * useful to allow it, there would be ambiguity about how to match the
12361  * list to unique indexes (in particular, it'd be unclear which index
12362  * opclass goes with which FK column).
12363  */
12364  for (i = 0; i < numattrs; i++)
12365  {
12366  for (j = i + 1; j < numattrs; j++)
12367  {
12368  if (attnums[i] == attnums[j])
12369  ereport(ERROR,
12370  (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12371  errmsg("foreign key referenced-columns list must not contain duplicates")));
12372  }
12373  }
12374 
12375  /*
12376  * Get the list of index OIDs for the table from the relcache, and look up
12377  * each one in the pg_index syscache, and match unique indexes to the list
12378  * of attnums we are given.
12379  */
12380  indexoidlist = RelationGetIndexList(pkrel);
12381 
12382  foreach(indexoidscan, indexoidlist)
12383  {
12384  HeapTuple indexTuple;
12385  Form_pg_index indexStruct;
12386 
12387  indexoid = lfirst_oid(indexoidscan);
12388  indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12389  if (!HeapTupleIsValid(indexTuple))
12390  elog(ERROR, "cache lookup failed for index %u", indexoid);
12391  indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12392 
12393  /*
12394  * Must have the right number of columns; must be unique (or if
12395  * temporal then exclusion instead) and not a partial index; forget it
12396  * if there are any expressions, too. Invalid indexes are out as well.
12397  */
12398  if (indexStruct->indnkeyatts == numattrs &&
12399  (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
12400  indexStruct->indisvalid &&
12401  heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
12402  heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
12403  {
12404  Datum indclassDatum;
12405  oidvector *indclass;
12406 
12407  /* Must get indclass the hard way */
12408  indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12409  Anum_pg_index_indclass);
12410  indclass = (oidvector *) DatumGetPointer(indclassDatum);
12411 
12412  /*
12413  * The given attnum list may match the index columns in any order.
12414  * Check for a match, and extract the appropriate opclasses while
12415  * we're at it.
12416  *
12417  * We know that attnums[] is duplicate-free per the test at the
12418  * start of this function, and we checked above that the number of
12419  * index columns agrees, so if we find a match for each attnums[]
12420  * entry then we must have a one-to-one match in some order.
12421  */
12422  for (i = 0; i < numattrs; i++)
12423  {
12424  found = false;
12425  for (j = 0; j < numattrs; j++)
12426  {
12427  if (attnums[i] == indexStruct->indkey.values[j])
12428  {
12429  opclasses[i] = indclass->values[j];
12430  found = true;
12431  break;
12432  }
12433  }
12434  if (!found)
12435  break;
12436  }
12437  /* The last attribute in the index must be the PERIOD FK part */
12438  if (found && with_period)
12439  {
12440  int16 periodattnum = attnums[numattrs - 1];
12441 
12442  found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
12443  }
12444 
12445  /*
12446  * Refuse to use a deferrable unique/primary key. This is per SQL
12447  * spec, and there would be a lot of interesting semantic problems
12448  * if we tried to allow it.
12449  */
12450  if (found && !indexStruct->indimmediate)
12451  {
12452  /*
12453  * Remember that we found an otherwise matching index, so that
12454  * we can generate a more appropriate error message.
12455  */
12456  found_deferrable = true;
12457  found = false;
12458  }
12459 
12460  /* We need to know whether the index has WITHOUT OVERLAPS */
12461  if (found)
12462  *pk_has_without_overlaps = indexStruct->indisexclusion;
12463  }
12464  ReleaseSysCache(indexTuple);
12465  if (found)
12466  break;
12467  }
12468 
12469  if (!found)
12470  {
12471  if (found_deferrable)
12472  ereport(ERROR,
12473  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12474  errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12475  RelationGetRelationName(pkrel))));
12476  else
12477  ereport(ERROR,
12478  (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12479  errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12480  RelationGetRelationName(pkrel))));
12481  }
12482 
12483  list_free(indexoidlist);
12484 
12485  return indexoid;
12486 }
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:455
Definition: c.h:726
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:733

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 opclasses,
bool pk_has_without_overlaps 
)
static

Definition at line 12242 of file tablecmds.c.

12246 {
12247  List *indexoidlist;
12248  ListCell *indexoidscan;
12249  HeapTuple indexTuple = NULL;
12250  Form_pg_index indexStruct = NULL;
12251  Datum indclassDatum;
12252  oidvector *indclass;
12253  int i;
12254 
12255  /*
12256  * Get the list of index OIDs for the table from the relcache, and look up
12257  * each one in the pg_index syscache until we find one marked primary key
12258  * (hopefully there isn't more than one such). Insist it's valid, too.
12259  */
12260  *indexOid = InvalidOid;
12261 
12262  indexoidlist = RelationGetIndexList(pkrel);
12263 
12264  foreach(indexoidscan, indexoidlist)
12265  {
12266  Oid indexoid = lfirst_oid(indexoidscan);
12267 
12268  indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12269  if (!HeapTupleIsValid(indexTuple))
12270  elog(ERROR, "cache lookup failed for index %u", indexoid);
12271  indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12272  if (indexStruct->indisprimary && indexStruct->indisvalid)
12273  {
12274  /*
12275  * Refuse to use a deferrable primary key. This is per SQL spec,
12276  * and there would be a lot of interesting semantic problems if we
12277  * tried to allow it.
12278  */
12279  if (!indexStruct->indimmediate)
12280  ereport(ERROR,
12281  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12282  errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12283  RelationGetRelationName(pkrel))));
12284 
12285  *indexOid = indexoid;
12286  break;
12287  }
12288  ReleaseSysCache(indexTuple);
12289  }
12290 
12291  list_free(indexoidlist);
12292 
12293  /*
12294  * Check that we found it
12295  */
12296  if (!OidIsValid(*indexOid))
12297  ereport(ERROR,
12298  (errcode(ERRCODE_UNDEFINED_OBJECT),
12299  errmsg("there is no primary key for referenced table \"%s\"",
12300  RelationGetRelationName(pkrel))));
12301 
12302  /* Must get indclass the hard way */
12303  indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12304  Anum_pg_index_indclass);
12305  indclass = (oidvector *) DatumGetPointer(indclassDatum);
12306 
12307  /*
12308  * Now build the list of PK attributes from the indkey definition (we
12309  * assume a primary key cannot have expressional elements)
12310  */
12311  *attnamelist = NIL;
12312  for (i = 0; i < indexStruct->indnkeyatts; i++)
12313  {
12314  int pkattno = indexStruct->indkey.values[i];
12315 
12316  attnums[i] = pkattno;
12317  atttypids[i] = attnumTypeId(pkrel, pkattno);
12318  opclasses[i] = indclass->values[i];
12319  *attnamelist = lappend(*attnamelist,
12320  makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
12321  }
12322 
12323  *pk_has_without_overlaps = indexStruct->indisexclusion;
12324 
12325  ReleaseSysCache(indexTuple);
12326 
12327  return i;
12328 }
const NameData * attnumAttName(Relation rd, int attid)
Oid attnumTypeId(Relation rd, int attid)

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

18648 {
18649  PartitionSpec *newspec;
18650  ParseState *pstate;
18651  ParseNamespaceItem *nsitem;
18652  ListCell *l;
18653 
18654  newspec = makeNode(PartitionSpec);
18655 
18656  newspec->strategy = partspec->strategy;
18657  newspec->partParams = NIL;
18658  newspec->location = partspec->location;
18659 
18660  /* Check valid number of columns for strategy */
18661  if (partspec->strategy == PARTITION_STRATEGY_LIST &&
18662  list_length(partspec->partParams) != 1)
18663  ereport(ERROR,
18664  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18665  errmsg("cannot use \"list\" partition strategy with more than one column")));
18666 
18667  /*
18668  * Create a dummy ParseState and insert the target relation as its sole
18669  * rangetable entry. We need a ParseState for transformExpr.
18670  */
18671  pstate = make_parsestate(NULL);
18672  nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
18673  NULL, false, true);
18674  addNSItemToQuery(pstate, nsitem, true, true, true);
18675 
18676  /* take care of any partition expressions */
18677  foreach(l, partspec->partParams)
18678  {
18680 
18681  if (pelem->expr)
18682  {
18683  /* Copy, to avoid scribbling on the input */
18684  pelem = copyObject(pelem);
18685 
18686  /* Now do parse transformation of the expression */
18687  pelem->expr = transformExpr(pstate, pelem->expr,
18689 
18690  /* we have to fix its collations too */
18691  assign_expr_collations(pstate, pelem->expr);
18692  }
18693 
18694  newspec->partParams = lappend(newspec->partParams, pelem);
18695  }
18696 
18697  return newspec;
18698 }
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:121
@ EXPR_KIND_PARTITION_EXPRESSION
Definition: parse_node.h:80
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:872
List * partParams
Definition: parsenodes.h:886
ParseLoc location
Definition: parsenodes.h:887
PartitionStrategy strategy
Definition: parsenodes.h:885

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

2385 {
2386  /*
2387  * Don't allow truncate on temp tables of other backends ... their local
2388  * buffer manager is not going to cope.
2389  */
2390  if (RELATION_IS_OTHER_TEMP(rel))
2391  ereport(ERROR,
2392  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2393  errmsg("cannot truncate temporary tables of other sessions")));
2394 
2395  /*
2396  * Also check for active uses of the relation in the current transaction,
2397  * including open scans and pending AFTER trigger events.
2398  */
2399  CheckTableNotInUse(rel, "TRUNCATE");
2400 }

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

2367 {
2368  char *relname = NameStr(reltuple->relname);
2369  AclResult aclresult;
2370 
2371  /* Permissions checks */
2372  aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2373  if (aclresult != ACLCHECK_OK)
2374  aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2375  relname);
2376 }
#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 2318 of file tablecmds.c.

2319 {
2320  char *relname = NameStr(reltuple->relname);
2321 
2322  /*
2323  * Only allow truncate on regular tables, foreign tables using foreign
2324  * data wrappers supporting TRUNCATE and partitioned tables (although, the
2325  * latter are only being included here for the following checks; no
2326  * physical truncation will occur in their case.).
2327  */
2328  if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2329  {
2330  Oid serverid = GetForeignServerIdByRelId(relid);
2331  FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2332 
2333  if (!fdwroutine->ExecForeignTruncate)
2334  ereport(ERROR,
2335  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2336  errmsg("cannot truncate foreign table \"%s\"",
2337  relname)));
2338  }
2339  else if (reltuple->relkind != RELKIND_RELATION &&
2340  reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2341  ereport(ERROR,
2342  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2343  errmsg("\"%s\" is not a table", relname)));
2344 
2345  /*
2346  * Most system catalogs can't be truncated at all, or at least not unless
2347  * allow_system_table_mods=on. As an exception, however, we allow
2348  * pg_largeobject to be truncated as part of pg_upgrade, because we need
2349  * to change its relfilenode to match the old cluster, and allowing a
2350  * TRUNCATE command to be executed is the easiest way of doing that.
2351  */
2352  if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2353  && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2354  ereport(ERROR,
2355  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2356  errmsg("permission denied: \"%s\" is a system catalog",
2357  relname)));
2358 
2359  InvokeObjectTruncateHook(relid);
2360 }
bool IsBinaryUpgrade
Definition: globals.c:118
#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 ( ForeignKeyCacheInfo fk,
Oid  partRelid,
Oid  parentConstrOid,
int  numfks,
AttrNumber mapped_conkey,
AttrNumber confkey,
Oid conpfeqop,
Oid  parentInsTrigger,
Oid  parentUpdTrigger,
Relation  trigrel 
)
static

Definition at line 11442 of file tablecmds.c.

11452 {
11453  HeapTuple parentConstrTup;
11454  Form_pg_constraint parentConstr;
11455  HeapTuple partcontup;
11456  Form_pg_constraint partConstr;
11457  ScanKeyData key;
11458  SysScanDesc scan;
11459  HeapTuple trigtup;
11460  Oid insertTriggerOid,
11461  updateTriggerOid;
11462 
11463  parentConstrTup = SearchSysCache1(CONSTROID,
11464  ObjectIdGetDatum(parentConstrOid));
11465  if (!HeapTupleIsValid(parentConstrTup))
11466  elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11467  parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11468 
11469  /*
11470  * Do some quick & easy initial checks. If any of these fail, we cannot
11471  * use this constraint.
11472  */
11473  if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11474  {
11475  ReleaseSysCache(parentConstrTup);
11476  return false;
11477  }
11478  for (int i = 0; i < numfks; i++)
11479  {
11480  if (fk->conkey[i] != mapped_conkey[i] ||
11481  fk->confkey[i] != confkey[i] ||
11482  fk->conpfeqop[i] != conpfeqop[i])
11483  {
11484  ReleaseSysCache(parentConstrTup);
11485  return false;
11486  }
11487  }
11488 
11489  /*
11490  * Looks good so far; do some more extensive checks. Presumably the check
11491  * for 'convalidated' could be dropped, since we don't really care about
11492  * that, but let's be careful for now.
11493  */
11494  partcontup = SearchSysCache1(CONSTROID,
11495  ObjectIdGetDatum(fk->conoid));
11496  if (!HeapTupleIsValid(partcontup))
11497  elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11498  partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11499  if (OidIsValid(partConstr->conparentid) ||
11500  !partConstr->convalidated ||
11501  partConstr->condeferrable != parentConstr->condeferrable ||
11502  partConstr->condeferred != parentConstr->condeferred ||
11503  partConstr->confupdtype != parentConstr->confupdtype ||
11504  partConstr->confdeltype != parentConstr->confdeltype ||
11505  partConstr->confmatchtype != parentConstr->confmatchtype)
11506  {
11507  ReleaseSysCache(parentConstrTup);
11508  ReleaseSysCache(partcontup);
11509  return false;
11510  }
11511 
11512  ReleaseSysCache(partcontup);
11513  ReleaseSysCache(parentConstrTup);
11514 
11515  /*
11516  * Looks good! Attach this constraint. The action triggers in the new
11517  * partition become redundant -- the parent table already has equivalent
11518  * ones, and those will be able to reach the partition. Remove the ones
11519  * in the partition. We identify them because they have our constraint
11520  * OID, as well as being on the referenced rel.
11521  */
11522  ScanKeyInit(&key,
11523  Anum_pg_trigger_tgconstraint,
11524  BTEqualStrategyNumber, F_OIDEQ,
11525  ObjectIdGetDatum(fk->conoid));
11526  scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11527  NULL, 1, &key);
11528  while ((trigtup = systable_getnext(scan)) != NULL)
11529  {
11530  Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11531  ObjectAddress trigger;
11532 
11533  if (trgform->tgconstrrelid != fk->conrelid)
11534  continue;
11535  if (trgform->tgrelid != fk->confrelid)
11536  continue;
11537 
11538  /*
11539  * The constraint is originally set up to contain this trigger as an
11540  * implementation object, so there's a dependency record that links
11541  * the two; however, since the trigger is no longer needed, we remove
11542  * the dependency link in order to be able to drop the trigger while
11543  * keeping the constraint intact.
11544  */
11545  deleteDependencyRecordsFor(TriggerRelationId,
11546  trgform->oid,
11547  false);
11548  /* make dependency deletion visible to performDeletion */
11550  ObjectAddressSet(trigger, TriggerRelationId,
11551  trgform->oid);
11552  performDeletion(&trigger, DROP_RESTRICT, 0);
11553  /* make trigger drop visible, in case the loop iterates */
11555  }
11556 
11557  systable_endscan(scan);
11558 
11559  ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
11560 
11561  /*
11562  * Like the constraint, attach partition's "check" triggers to the
11563  * corresponding parent triggers.
11564  */
11566  fk->conoid, fk->confrelid, fk->conrelid,
11567  &insertTriggerOid, &updateTriggerOid);
11568  Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11569  TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11570  partRelid);
11571  Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11572  TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11573  partRelid);
11574 
11576  return true;
11577 }

References Assert, BTEqualStrategyNumber, CommandCounterIncrement(), ForeignKeyCacheInfo::confrelid, ForeignKeyCacheInfo::conoid, ForeignKeyCacheInfo::conrelid, ConstraintSetParentConstraint(), deleteDependencyRecordsFor(), DROP_RESTRICT, elog, ERROR, GetForeignKeyCheckTriggers(), GETSTRUCT, HeapTupleIsValid, i, sort-test::key, ForeignKeyCacheInfo::nkeys, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, performDeletion(), ReleaseSysCache(), ScanKeyInit(), SearchSysCache1(), systable_beginscan(), systable_endscan(), systable_getnext(), and TriggerSetParentTrigger().

Referenced by addFkRecurseReferencing(), and CloneFkReferencing().

◆ TryReuseForeignKey()

static void TryReuseForeignKey ( Oid  oldId,
Constraint con 
)
static

Definition at line 14825 of file tablecmds.c.

14826 {
14827  HeapTuple tup;
14828  Datum adatum;
14829  ArrayType *arr;
14830  Oid *rawarr;
14831  int numkeys;
14832  int i;
14833 
14834  Assert(con->contype == CONSTR_FOREIGN);
14835  Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14836 
14837  tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14838  if (!HeapTupleIsValid(tup)) /* should not happen */
14839  elog(ERROR, "cache lookup failed for constraint %u", oldId);
14840 
14841  adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14842  Anum_pg_constraint_conpfeqop);
14843  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14844  numkeys = ARR_DIMS(arr)[0];
14845  /* test follows the one in ri_FetchConstraintInfo() */
14846  if (ARR_NDIM(arr) != 1 ||
14847  ARR_HASNULL(arr) ||
14848  ARR_ELEMTYPE(arr) != OIDOID)
14849  elog(ERROR, "conpfeqop is not a 1-D Oid array");
14850  rawarr = (Oid *) ARR_DATA_PTR(arr);
14851 
14852  /* stash a List of the operator Oids in our Constraint node */
14853  for (i = 0; i < numkeys; i++)
14854  con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14855 
14856  ReleaseSysCache(tup);
14857 }

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

14797 {
14798  if (CheckIndexCompatible(oldId,
14799  stmt->accessMethod,
14800  stmt->indexParams,
14801  stmt->excludeOpNames,
14802  stmt->iswithoutoverlaps))
14803  {
14804  Relation irel = index_open(oldId, NoLock);
14805 
14806  /* If it's a partitioned index, there is no storage to share. */
14807  if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14808  {
14809  stmt->oldNumber = irel->rd_locator.relNumber;
14810  stmt->oldCreateSubid = irel->rd_createSubid;
14811  stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14812  }
14813  index_close(irel, NoLock);
14814  }
14815 }
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
Definition: indexcmds.c:177

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

Referenced by ATPostAlterTypeParse().

◆ validateFkOnDeleteSetColumns()

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

Definition at line 10420 of file tablecmds.c.

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

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

12559 {
12560  TupleTableSlot *slot;
12561  TableScanDesc scan;
12562  Trigger trig = {0};
12563  Snapshot snapshot;
12564  MemoryContext oldcxt;
12565  MemoryContext perTupCxt;
12566 
12567  ereport(DEBUG1,
12568  (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12569 
12570  /*
12571  * Build a trigger call structure; we'll need it either way.
12572  */
12573  trig.tgoid = InvalidOid;
12574  trig.tgname = conname;
12576  trig.tgisinternal = true;
12577  trig.tgconstrrelid = RelationGetRelid(pkrel);
12578  trig.tgconstrindid = pkindOid;
12579  trig.tgconstraint = constraintOid;
12580  trig.tgdeferrable = false;
12581  trig.tginitdeferred = false;
12582  /* we needn't fill in remaining fields */
12583 
12584  /*
12585  * See if we can do it with a single LEFT JOIN query. A false result
12586  * indicates we must proceed with the fire-the-trigger method. We can't do
12587  * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
12588  * left joins.
12589  */
12590  if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
12591  return;
12592 
12593  /*
12594  * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12595  * if that tuple had just been inserted. If any of those fail, it should
12596  * ereport(ERROR) and that's that.
12597  */
12598  snapshot = RegisterSnapshot(GetLatestSnapshot());
12599  slot = table_slot_create(rel, NULL);
12600  scan = table_beginscan(rel, snapshot, 0, NULL);
12601 
12603  "validateForeignKeyConstraint",
12605  oldcxt = MemoryContextSwitchTo(perTupCxt);
12606 
12607  while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12608  {
12609  LOCAL_FCINFO(fcinfo, 0);
12610  TriggerData trigdata = {0};
12611 
12613 
12614  /*
12615  * Make a call to the trigger function
12616  *
12617  * No parameters are passed, but we do set a context
12618  */
12619  MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12620 
12621  /*
12622  * We assume RI_FKey_check_ins won't look at flinfo...
12623  */
12624  trigdata.type = T_TriggerData;
12626  trigdata.tg_relation = rel;
12627  trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12628  trigdata.tg_trigslot = slot;
12629  trigdata.tg_trigger = &trig;
12630 
12631  fcinfo->context = (Node *) &trigdata;
12632 
12633  RI_FKey_check_ins(fcinfo);
12634 
12635  MemoryContextReset(perTupCxt);
12636  }
12637 
12638  MemoryContextSwitchTo(oldcxt);
12639  MemoryContextDelete(perTupCxt);
12640  table_endscan(scan);
12641  UnregisterSnapshot(snapshot);
12643 }
#define MemSet(start, val, len)
Definition: c.h:1020
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1731
#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:471
bool RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1443
NodeTag type
Definition: trigger.h:33
Relation tg_relation
Definition: trigger.h:35
TriggerEvent tg_event
Definition: trigger.h:34
TupleTableSlot * tg_trigslot
Definition: trigger.h:39
Trigger * tg_trigger
Definition: trigger.h:38
HeapTuple tg_trigtuple
Definition: trigger.h:36
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:91
#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 20637 of file tablecmds.c.

20638 {
20639  Relation inheritsRel;
20640  SysScanDesc scan;
20641  ScanKeyData key;
20642  int tuples = 0;
20643  HeapTuple inhTup;
20644  bool updated = false;
20645 
20646  Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
20647 
20648  /*
20649  * Scan pg_inherits for this parent index. Count each valid index we find
20650  * (verifying the pg_index entry for each), and if we reach the total
20651  * amount we expect, we can mark this parent index as valid.
20652  */
20653  inheritsRel = table_open(InheritsRelationId, AccessShareLock);
20654  ScanKeyInit(&key, Anum_pg_inherits_inhparent,
20655  BTEqualStrategyNumber, F_OIDEQ,
20656  ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20657  scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
20658  NULL, 1, &key);
20659  while ((inhTup = systable_getnext(scan)) != NULL)
20660  {
20661  Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
20662  HeapTuple indTup;
20663  Form_pg_index indexForm;
20664 
20665  indTup = SearchSysCache1(INDEXRELID,
20666  ObjectIdGetDatum(inhForm->inhrelid));
20667  if (!HeapTupleIsValid(indTup))
20668  elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
20669  indexForm = (Form_pg_index) GETSTRUCT(indTup);
20670  if (indexForm->indisvalid)
20671  tuples += 1;
20672  ReleaseSysCache(indTup);
20673  }
20674 
20675  /* Done with pg_inherits */
20676  systable_endscan(scan);
20677  table_close(inheritsRel, AccessShareLock);
20678 
20679  /*
20680  * If we found as many inherited indexes as the partitioned table has
20681  * partitions, we're good; update pg_index to set indisvalid.
20682  */
20683  if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
20684  {
20685  Relation idxRel;
20686  HeapTuple indTup;
20687  Form_pg_index indexForm;
20688 
20689  idxRel = table_open(IndexRelationId, RowExclusiveLock);
20690  indTup = SearchSysCacheCopy1(INDEXRELID,
20691  ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20692  if (!HeapTupleIsValid(indTup))
20693  elog(ERROR, "cache lookup failed for index %u",
20694  RelationGetRelid(partedIdx));
20695  indexForm = (Form_pg_index) GETSTRUCT(indTup);
20696 
20697  indexForm->indisvalid = true;
20698  updated = true;
20699 
20700  CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
20701 
20702  table_close(idxRel, RowExclusiveLock);
20703  heap_freetuple(indTup);
20704  }
20705 
20706  /*
20707  * If this index is in turn a partition of a larger index, validating it
20708  * might cause the parent to become valid also. Try that.
20709  */
20710  if (updated && partedIdx->rd_rel->relispartition)
20711  {
20712  Oid parentIdxId,
20713  parentTblId;
20714  Relation parentIdx,
20715  parentTbl;
20716 
20717  /* make sure we see the validation we just did */
20719 
20720  parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
20721  parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
20722  parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
20723  parentTbl = relation_open(parentTblId, AccessExclusiveLock);
20724  Assert(!parentIdx->rd_index->indisvalid);
20725 
20726  validatePartitionedIndex(parentIdx, parentTbl);
20727 
20728  relation_close(parentIdx, AccessExclusiveLock);
20729  relation_close(parentTbl, AccessExclusiveLock);
20730  }
20731 }

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

Referenced by ATExecAttachPartitionIdx().

◆ verifyPartitionIndexNotNull()

static void verifyPartitionIndexNotNull ( IndexInfo iinfo,
Relation  partIdx 
)
static

Definition at line 20739 of file tablecmds.c.

20740 {
20741  for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
20742  {
20744  iinfo->ii_IndexAttrNumbers[i] - 1);
20745 
20746  if (!att->attnotnull)
20747  ereport(ERROR,
20748  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
20749  errmsg("invalid primary key definition"),
20750  errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20751  NameStr(att->attname),
20752  RelationGetRelationName(partition)));
20753  }
20754 }

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

Referenced by DropErrorMsgNonExistent(), and DropErrorMsgWrongType().

◆ on_commits