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

◆ ATT_COMPOSITE_TYPE

#define ATT_COMPOSITE_TYPE   0x0010

Definition at line 331 of file tablecmds.c.

◆ ATT_FOREIGN_TABLE

#define ATT_FOREIGN_TABLE   0x0020

Definition at line 332 of file tablecmds.c.

◆ ATT_INDEX

#define ATT_INDEX   0x0008

Definition at line 330 of file tablecmds.c.

◆ ATT_MATVIEW

#define ATT_MATVIEW   0x0004

Definition at line 329 of file tablecmds.c.

◆ ATT_PARTITIONED_INDEX

#define ATT_PARTITIONED_INDEX   0x0040

Definition at line 333 of file tablecmds.c.

◆ ATT_SEQUENCE

#define ATT_SEQUENCE   0x0080

Definition at line 334 of file tablecmds.c.

◆ ATT_TABLE

#define ATT_TABLE   0x0001

Definition at line 327 of file tablecmds.c.

◆ ATT_VIEW

#define ATT_VIEW   0x0002

Definition at line 328 of file tablecmds.c.

◆ child_dependency_type

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

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

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

Function Documentation

◆ add_column_collation_dependency()

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

Definition at line 7546 of file tablecmds.c.

7547 {
7548  ObjectAddress myself,
7549  referenced;
7550 
7551  /* We know the default collation is pinned, so don't bother recording it */
7552  if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7553  {
7554  myself.classId = RelationRelationId;
7555  myself.objectId = relid;
7556  myself.objectSubId = attnum;
7557  referenced.classId = CollationRelationId;
7558  referenced.objectId = collid;
7559  referenced.objectSubId = 0;
7560  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7561  }
7562 }
#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 7528 of file tablecmds.c.

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

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

10492 {
10493  ObjectAddress address;
10494  Oid constrOid;
10495  char *conname;
10496  bool conislocal;
10497  int coninhcount;
10498  bool connoinherit;
10499  Oid deleteTriggerOid,
10500  updateTriggerOid;
10501 
10502  /*
10503  * Verify relkind for each referenced partition. At the top level, this
10504  * is redundant with a previous check, but we need it when recursing.
10505  */
10506  if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10507  pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10508  ereport(ERROR,
10509  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10510  errmsg("referenced relation \"%s\" is not a table",
10511  RelationGetRelationName(pkrel))));
10512 
10513  /*
10514  * Caller supplies us with a constraint name; however, it may be used in
10515  * this partition, so come up with a different one in that case.
10516  */
10518  RelationGetRelid(rel),
10519  fkconstraint->conname))
10522  "fkey",
10523  RelationGetNamespace(rel), NIL);
10524  else
10525  conname = fkconstraint->conname;
10526 
10527  if (OidIsValid(parentConstr))
10528  {
10529  conislocal = false;
10530  coninhcount = 1;
10531  connoinherit = false;
10532  }
10533  else
10534  {
10535  conislocal = true;
10536  coninhcount = 0;
10537 
10538  /*
10539  * always inherit for partitioned tables, never for legacy inheritance
10540  */
10541  connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10542  }
10543 
10544  /*
10545  * Record the FK constraint in pg_constraint.
10546  */
10547  constrOid = CreateConstraintEntry(conname,
10548  RelationGetNamespace(rel),
10549  CONSTRAINT_FOREIGN,
10550  fkconstraint->deferrable,
10551  fkconstraint->initdeferred,
10552  fkconstraint->initially_valid,
10553  parentConstr,
10554  RelationGetRelid(rel),
10555  fkattnum,
10556  numfks,
10557  numfks,
10558  InvalidOid, /* not a domain constraint */
10559  indexOid,
10560  RelationGetRelid(pkrel),
10561  pkattnum,
10562  pfeqoperators,
10563  ppeqoperators,
10564  ffeqoperators,
10565  numfks,
10566  fkconstraint->fk_upd_action,
10567  fkconstraint->fk_del_action,
10568  fkdelsetcols,
10569  numfkdelsetcols,
10570  fkconstraint->fk_matchtype,
10571  NULL, /* no exclusion constraint */
10572  NULL, /* no check constraint */
10573  NULL,
10574  conislocal, /* islocal */
10575  coninhcount, /* inhcount */
10576  connoinherit, /* conNoInherit */
10577  with_period, /* conPeriod */
10578  false); /* is_internal */
10579 
10580  ObjectAddressSet(address, ConstraintRelationId, constrOid);
10581 
10582  /*
10583  * Mark the child constraint as part of the parent constraint; it must not
10584  * be dropped on its own. (This constraint is deleted when the partition
10585  * is detached, but a special check needs to occur that the partition
10586  * contains no referenced values.)
10587  */
10588  if (OidIsValid(parentConstr))
10589  {
10590  ObjectAddress referenced;
10591 
10592  ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10593  recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10594  }
10595 
10596  /* make new constraint visible, in case we add more */
10598 
10599  /*
10600  * Create the action triggers that enforce the constraint.
10601  */
10603  fkconstraint,
10604  constrOid, indexOid,
10605  parentDelTrigger, parentUpdTrigger,
10606  &deleteTriggerOid, &updateTriggerOid);
10607 
10608  /*
10609  * If the referenced table is partitioned, recurse on ourselves to handle
10610  * each partition. We need one pg_constraint row created for each
10611  * partition in addition to the pg_constraint row for the parent table.
10612  */
10613  if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10614  {
10615  PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10616 
10617  for (int i = 0; i < pd->nparts; i++)
10618  {
10619  Relation partRel;
10620  AttrMap *map;
10621  AttrNumber *mapped_pkattnum;
10622  Oid partIndexId;
10623 
10624  partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10625 
10626  /*
10627  * Map the attribute numbers in the referenced side of the FK
10628  * definition to match the partition's column layout.
10629  */
10631  RelationGetDescr(pkrel),
10632  false);
10633  if (map)
10634  {
10635  mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10636  for (int j = 0; j < numfks; j++)
10637  mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10638  }
10639  else
10640  mapped_pkattnum = pkattnum;
10641 
10642  /* do the deed */
10643  partIndexId = index_get_partition(partRel, indexOid);
10644  if (!OidIsValid(partIndexId))
10645  elog(ERROR, "index for %u not found in partition %s",
10646  indexOid, RelationGetRelationName(partRel));
10647  addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
10648  partIndexId, constrOid, numfks,
10649  mapped_pkattnum, fkattnum,
10650  pfeqoperators, ppeqoperators, ffeqoperators,
10651  numfkdelsetcols, fkdelsetcols,
10652  old_check_ok,
10653  deleteTriggerOid, updateTriggerOid,
10654  with_period);
10655 
10656  /* Done -- clean up (but keep the lock) */
10657  table_close(partRel, NoLock);
10658  if (map)
10659  {
10660  pfree(mapped_pkattnum);
10661  free_attrmap(map);
10662  }
10663  }
10664  }
10665 
10666  return address;
10667 }
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:9687
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:10483
static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:12717
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 10706 of file tablecmds.c.

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

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

6465 {
6466  switch (cmdtype)
6467  {
6468  case AT_AddColumn:
6469  case AT_AddColumnToView:
6470  return "ADD COLUMN";
6471  case AT_ColumnDefault:
6473  return "ALTER COLUMN ... SET DEFAULT";
6474  case AT_DropNotNull:
6475  return "ALTER COLUMN ... DROP NOT NULL";
6476  case AT_SetNotNull:
6477  return "ALTER COLUMN ... SET NOT NULL";
6478  case AT_SetAttNotNull:
6479  return NULL; /* not real grammar */
6480  case AT_SetExpression:
6481  return "ALTER COLUMN ... SET EXPRESSION";
6482  case AT_DropExpression:
6483  return "ALTER COLUMN ... DROP EXPRESSION";
6484  case AT_SetStatistics:
6485  return "ALTER COLUMN ... SET STATISTICS";
6486  case AT_SetOptions:
6487  return "ALTER COLUMN ... SET";
6488  case AT_ResetOptions:
6489  return "ALTER COLUMN ... RESET";
6490  case AT_SetStorage:
6491  return "ALTER COLUMN ... SET STORAGE";
6492  case AT_SetCompression:
6493  return "ALTER COLUMN ... SET COMPRESSION";
6494  case AT_DropColumn:
6495  return "DROP COLUMN";
6496  case AT_AddIndex:
6497  case AT_ReAddIndex:
6498  return NULL; /* not real grammar */
6499  case AT_AddConstraint:
6500  case AT_ReAddConstraint:
6502  case AT_AddIndexConstraint:
6503  return "ADD CONSTRAINT";
6504  case AT_AlterConstraint:
6505  return "ALTER CONSTRAINT";
6506  case AT_ValidateConstraint:
6507  return "VALIDATE CONSTRAINT";
6508  case AT_DropConstraint:
6509  return "DROP CONSTRAINT";
6510  case AT_ReAddComment:
6511  return NULL; /* not real grammar */
6512  case AT_AlterColumnType:
6513  return "ALTER COLUMN ... SET DATA TYPE";
6515  return "ALTER COLUMN ... OPTIONS";
6516  case AT_ChangeOwner:
6517  return "OWNER TO";
6518  case AT_ClusterOn:
6519  return "CLUSTER ON";
6520  case AT_DropCluster:
6521  return "SET WITHOUT CLUSTER";
6522  case AT_SetAccessMethod:
6523  return "SET ACCESS METHOD";
6524  case AT_SetLogged:
6525  return "SET LOGGED";
6526  case AT_SetUnLogged:
6527  return "SET UNLOGGED";
6528  case AT_DropOids:
6529  return "SET WITHOUT OIDS";
6530  case AT_SetTableSpace:
6531  return "SET TABLESPACE";
6532  case AT_SetRelOptions:
6533  return "SET";
6534  case AT_ResetRelOptions:
6535  return "RESET";
6536  case AT_ReplaceRelOptions:
6537  return NULL; /* not real grammar */
6538  case AT_EnableTrig:
6539  return "ENABLE TRIGGER";
6540  case AT_EnableAlwaysTrig:
6541  return "ENABLE ALWAYS TRIGGER";
6542  case AT_EnableReplicaTrig:
6543  return "ENABLE REPLICA TRIGGER";
6544  case AT_DisableTrig:
6545  return "DISABLE TRIGGER";
6546  case AT_EnableTrigAll:
6547  return "ENABLE TRIGGER ALL";
6548  case AT_DisableTrigAll:
6549  return "DISABLE TRIGGER ALL";
6550  case AT_EnableTrigUser:
6551  return "ENABLE TRIGGER USER";
6552  case AT_DisableTrigUser:
6553  return "DISABLE TRIGGER USER";
6554  case AT_EnableRule:
6555  return "ENABLE RULE";
6556  case AT_EnableAlwaysRule:
6557  return "ENABLE ALWAYS RULE";
6558  case AT_EnableReplicaRule:
6559  return "ENABLE REPLICA RULE";
6560  case AT_DisableRule:
6561  return "DISABLE RULE";
6562  case AT_AddInherit:
6563  return "INHERIT";
6564  case AT_DropInherit:
6565  return "NO INHERIT";
6566  case AT_AddOf:
6567  return "OF";
6568  case AT_DropOf:
6569  return "NOT OF";
6570  case AT_ReplicaIdentity:
6571  return "REPLICA IDENTITY";
6572  case AT_EnableRowSecurity:
6573  return "ENABLE ROW SECURITY";
6574  case AT_DisableRowSecurity:
6575  return "DISABLE ROW SECURITY";
6576  case AT_ForceRowSecurity:
6577  return "FORCE ROW SECURITY";
6578  case AT_NoForceRowSecurity:
6579  return "NO FORCE ROW SECURITY";
6580  case AT_GenericOptions:
6581  return "OPTIONS";
6582  case AT_AttachPartition:
6583  return "ATTACH PARTITION";
6584  case AT_DetachPartition:
6585  return "DETACH PARTITION";
6587  return "DETACH PARTITION ... FINALIZE";
6588  case AT_SplitPartition:
6589  return "SPLIT PARTITION";
6590  case AT_MergePartitions:
6591  return "MERGE PARTITIONS";
6592  case AT_AddIdentity:
6593  return "ALTER COLUMN ... ADD IDENTITY";
6594  case AT_SetIdentity:
6595  return "ALTER COLUMN ... SET";
6596  case AT_DropIdentity:
6597  return "ALTER COLUMN ... DROP IDENTITY";
6598  case AT_ReAddStatistics:
6599  return NULL; /* not real grammar */
6600  }
6601 
6602  return NULL;
6603 }
@ 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 18077 of file tablecmds.c.

18079 {
18080  List *indexList;
18081  ListCell *l;
18082 
18083  indexList = RelationGetIndexList(rel);
18084 
18085  foreach(l, indexList)
18086  {
18087  Oid indexOid = lfirst_oid(l);
18088  ObjectAddress thisobj;
18089 
18090  thisobj.classId = RelationRelationId;
18091  thisobj.objectId = indexOid;
18092  thisobj.objectSubId = 0;
18093 
18094  /*
18095  * Note: currently, the index will not have its own dependency on the
18096  * namespace, so we don't need to do changeDependencyFor(). There's no
18097  * row type in pg_type, either.
18098  *
18099  * XXX this objsMoved test may be pointless -- surely we have a single
18100  * dependency link from a relation to each index?
18101  */
18102  if (!object_address_present(&thisobj, objsMoved))
18103  {
18104  AlterRelationNamespaceInternal(classRel, indexOid,
18105  oldNspOid, newNspOid,
18106  false, objsMoved);
18107  add_exact_object_address(&thisobj, objsMoved);
18108  }
18109  }
18110 
18111  list_free(indexList);
18112 }
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:18007

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

18011 {
18012  HeapTuple classTup;
18013  Form_pg_class classForm;
18014  ObjectAddress thisobj;
18015  bool already_done = false;
18016 
18017  classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
18018  if (!HeapTupleIsValid(classTup))
18019  elog(ERROR, "cache lookup failed for relation %u", relOid);
18020  classForm = (Form_pg_class) GETSTRUCT(classTup);
18021 
18022  Assert(classForm->relnamespace == oldNspOid);
18023 
18024  thisobj.classId = RelationRelationId;
18025  thisobj.objectId = relOid;
18026  thisobj.objectSubId = 0;
18027 
18028  /*
18029  * If the object has already been moved, don't move it again. If it's
18030  * already in the right place, don't move it, but still fire the object
18031  * access hook.
18032  */
18033  already_done = object_address_present(&thisobj, objsMoved);
18034  if (!already_done && oldNspOid != newNspOid)
18035  {
18036  /* check for duplicate name (more friendly than unique-index failure) */
18037  if (get_relname_relid(NameStr(classForm->relname),
18038  newNspOid) != InvalidOid)
18039  ereport(ERROR,
18040  (errcode(ERRCODE_DUPLICATE_TABLE),
18041  errmsg("relation \"%s\" already exists in schema \"%s\"",
18042  NameStr(classForm->relname),
18043  get_namespace_name(newNspOid))));
18044 
18045  /* classTup is a copy, so OK to scribble on */
18046  classForm->relnamespace = newNspOid;
18047 
18048  CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
18049 
18050  /* Update dependency on schema if caller said so */
18051  if (hasDependEntry &&
18052  changeDependencyFor(RelationRelationId,
18053  relOid,
18054  NamespaceRelationId,
18055  oldNspOid,
18056  newNspOid) != 1)
18057  elog(ERROR, "could not change schema dependency for relation \"%s\"",
18058  NameStr(classForm->relname));
18059  }
18060  if (!already_done)
18061  {
18062  add_exact_object_address(&thisobj, objsMoved);
18063 
18064  InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
18065  }
18066 
18067  heap_freetuple(classTup);
18068 }
#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 18122 of file tablecmds.c.

18125 {
18126  Relation depRel;
18127  SysScanDesc scan;
18128  ScanKeyData key[2];
18129  HeapTuple tup;
18130 
18131  /*
18132  * SERIAL sequences are those having an auto dependency on one of the
18133  * table's columns (we don't care *which* column, exactly).
18134  */
18135  depRel = table_open(DependRelationId, AccessShareLock);
18136 
18137  ScanKeyInit(&key[0],
18138  Anum_pg_depend_refclassid,
18139  BTEqualStrategyNumber, F_OIDEQ,
18140  ObjectIdGetDatum(RelationRelationId));
18141  ScanKeyInit(&key[1],
18142  Anum_pg_depend_refobjid,
18143  BTEqualStrategyNumber, F_OIDEQ,
18145  /* we leave refobjsubid unspecified */
18146 
18147  scan = systable_beginscan(depRel, DependReferenceIndexId, true,
18148  NULL, 2, key);
18149 
18150  while (HeapTupleIsValid(tup = systable_getnext(scan)))
18151  {
18152  Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
18153  Relation seqRel;
18154 
18155  /* skip dependencies other than auto dependencies on columns */
18156  if (depForm->refobjsubid == 0 ||
18157  depForm->classid != RelationRelationId ||
18158  depForm->objsubid != 0 ||
18159  !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
18160  continue;
18161 
18162  /* Use relation_open just in case it's an index */
18163  seqRel = relation_open(depForm->objid, lockmode);
18164 
18165  /* skip non-sequence relations */
18166  if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
18167  {
18168  /* No need to keep the lock */
18169  relation_close(seqRel, lockmode);
18170  continue;
18171  }
18172 
18173  /* Fix the pg_class and pg_depend entries */
18174  AlterRelationNamespaceInternal(classRel, depForm->objid,
18175  oldNspOid, newNspOid,
18176  true, objsMoved);
18177 
18178  /*
18179  * Sequences used to have entries in pg_type, but no longer do. If we
18180  * ever re-instate that, we'll need to move the pg_type entry to the
18181  * new namespace, too (using AlterTypeNamespaceInternal).
18182  */
18183  Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
18184 
18185  /* Now we can close it. Keep the lock till end of transaction. */
18186  relation_close(seqRel, NoLock);
18187  }
18188 
18189  systable_endscan(scan);
18190 
18192 }
@ 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 4434 of file tablecmds.c.

4436 {
4437  Relation rel;
4438 
4439  /* Caller is required to provide an adequate lock. */
4440  rel = relation_open(context->relid, NoLock);
4441 
4442  CheckTableNotInUse(rel, "ALTER TABLE");
4443 
4444  ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4445 }
#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:4779

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

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4508 of file tablecmds.c.

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

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

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4375 of file tablecmds.c.

4376 {
4377  return RangeVarGetRelidExtended(stmt->relation, lockmode,
4378  stmt->missing_ok ? RVR_MISSING_OK : 0,
4380  (void *) stmt);
4381 }
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:18532

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

Referenced by ProcessUtilitySlow().

◆ AlterTableMoveAll()

Oid AlterTableMoveAll ( AlterTableMoveAllStmt stmt)

Definition at line 15916 of file tablecmds.c.

15917 {
15918  List *relations = NIL;
15919  ListCell *l;
15920  ScanKeyData key[1];
15921  Relation rel;
15922  TableScanDesc scan;
15923  HeapTuple tuple;
15924  Oid orig_tablespaceoid;
15925  Oid new_tablespaceoid;
15926  List *role_oids = roleSpecsToIds(stmt->roles);
15927 
15928  /* Ensure we were not asked to move something we can't */
15929  if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15930  stmt->objtype != OBJECT_MATVIEW)
15931  ereport(ERROR,
15932  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15933  errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15934 
15935  /* Get the orig and new tablespace OIDs */
15936  orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15937  new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15938 
15939  /* Can't move shared relations in to or out of pg_global */
15940  /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15941  if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15942  new_tablespaceoid == GLOBALTABLESPACE_OID)
15943  ereport(ERROR,
15944  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15945  errmsg("cannot move relations in to or out of pg_global tablespace")));
15946 
15947  /*
15948  * Must have CREATE rights on the new tablespace, unless it is the
15949  * database default tablespace (which all users implicitly have CREATE
15950  * rights on).
15951  */
15952  if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15953  {
15954  AclResult aclresult;
15955 
15956  aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15957  ACL_CREATE);
15958  if (aclresult != ACLCHECK_OK)
15959  aclcheck_error(aclresult, OBJECT_TABLESPACE,
15960  get_tablespace_name(new_tablespaceoid));
15961  }
15962 
15963  /*
15964  * Now that the checks are done, check if we should set either to
15965  * InvalidOid because it is our database's default tablespace.
15966  */
15967  if (orig_tablespaceoid == MyDatabaseTableSpace)
15968  orig_tablespaceoid = InvalidOid;
15969 
15970  if (new_tablespaceoid == MyDatabaseTableSpace)
15971  new_tablespaceoid = InvalidOid;
15972 
15973  /* no-op */
15974  if (orig_tablespaceoid == new_tablespaceoid)
15975  return new_tablespaceoid;
15976 
15977  /*
15978  * Walk the list of objects in the tablespace and move them. This will
15979  * only find objects in our database, of course.
15980  */
15981  ScanKeyInit(&key[0],
15982  Anum_pg_class_reltablespace,
15983  BTEqualStrategyNumber, F_OIDEQ,
15984  ObjectIdGetDatum(orig_tablespaceoid));
15985 
15986  rel = table_open(RelationRelationId, AccessShareLock);
15987  scan = table_beginscan_catalog(rel, 1, key);
15988  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
15989  {
15990  Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
15991  Oid relOid = relForm->oid;
15992 
15993  /*
15994  * Do not move objects in pg_catalog as part of this, if an admin
15995  * really wishes to do so, they can issue the individual ALTER
15996  * commands directly.
15997  *
15998  * Also, explicitly avoid any shared tables, temp tables, or TOAST
15999  * (TOAST will be moved with the main table).
16000  */
16001  if (IsCatalogNamespace(relForm->relnamespace) ||
16002  relForm->relisshared ||
16003  isAnyTempNamespace(relForm->relnamespace) ||
16004  IsToastNamespace(relForm->relnamespace))
16005  continue;
16006 
16007  /* Only move the object type requested */
16008  if ((stmt->objtype == OBJECT_TABLE &&
16009  relForm->relkind != RELKIND_RELATION &&
16010  relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
16011  (stmt->objtype == OBJECT_INDEX &&
16012  relForm->relkind != RELKIND_INDEX &&
16013  relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
16014  (stmt->objtype == OBJECT_MATVIEW &&
16015  relForm->relkind != RELKIND_MATVIEW))
16016  continue;
16017 
16018  /* Check if we are only moving objects owned by certain roles */
16019  if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
16020  continue;
16021 
16022  /*
16023  * Handle permissions-checking here since we are locking the tables
16024  * and also to avoid doing a bunch of work only to fail part-way. Note
16025  * that permissions will also be checked by AlterTableInternal().
16026  *
16027  * Caller must be considered an owner on the table to move it.
16028  */
16029  if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
16031  NameStr(relForm->relname));
16032 
16033  if (stmt->nowait &&
16035  ereport(ERROR,
16036  (errcode(ERRCODE_OBJECT_IN_USE),
16037  errmsg("aborting because lock on relation \"%s.%s\" is not available",
16038  get_namespace_name(relForm->relnamespace),
16039  NameStr(relForm->relname))));
16040  else
16042 
16043  /* Add to our list of objects to move */
16044  relations = lappend_oid(relations, relOid);
16045  }
16046 
16047  table_endscan(scan);
16049 
16050  if (relations == NIL)
16051  ereport(NOTICE,
16052  (errcode(ERRCODE_NO_DATA_FOUND),
16053  errmsg("no matching relations in tablespace \"%s\" found",
16054  orig_tablespaceoid == InvalidOid ? "(database default)" :
16055  get_tablespace_name(orig_tablespaceoid))));
16056 
16057  /* Everything is locked, loop through and move all of the relations. */
16058  foreach(l, relations)
16059  {
16060  List *cmds = NIL;
16062 
16063  cmd->subtype = AT_SetTableSpace;
16064  cmd->name = stmt->new_tablespacename;
16065 
16066  cmds = lappend(cmds, cmd);
16067 
16069  /* OID is set by AlterTableInternal */
16070  AlterTableInternal(lfirst_oid(l), cmds, false);
16072  }
16073 
16074  return new_tablespaceoid;
16075 }
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:4463

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

17903 {
17904  Relation rel;
17905  Oid relid;
17906  Oid oldNspOid;
17907  Oid nspOid;
17908  RangeVar *newrv;
17909  ObjectAddresses *objsMoved;
17910  ObjectAddress myself;
17911 
17913  stmt->missing_ok ? RVR_MISSING_OK : 0,
17915  (void *) stmt);
17916 
17917  if (!OidIsValid(relid))
17918  {
17919  ereport(NOTICE,
17920  (errmsg("relation \"%s\" does not exist, skipping",
17921  stmt->relation->relname)));
17922  return InvalidObjectAddress;
17923  }
17924 
17925  rel = relation_open(relid, NoLock);
17926 
17927  oldNspOid = RelationGetNamespace(rel);
17928 
17929  /* If it's an owned sequence, disallow moving it by itself. */
17930  if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17931  {
17932  Oid tableId;
17933  int32 colId;
17934 
17935  if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17936  sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17937  ereport(ERROR,
17938  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17939  errmsg("cannot move an owned sequence into another schema"),
17940  errdetail("Sequence \"%s\" is linked to table \"%s\".",
17942  get_rel_name(tableId))));
17943  }
17944 
17945  /* Get and lock schema OID and check its permissions. */
17946  newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17947  nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17948 
17949  /* common checks on switching namespaces */
17950  CheckSetNamespace(oldNspOid, nspOid);
17951 
17952  objsMoved = new_object_addresses();
17953  AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17954  free_object_addresses(objsMoved);
17955 
17956  ObjectAddressSet(myself, RelationRelationId, relid);
17957 
17958  if (oldschema)
17959  *oldschema = oldNspOid;
17960 
17961  /* close rel, but keep lock until commit */
17962  relation_close(rel, NoLock);
17963 
17964  return myself;
17965 }
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:17973

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

17975 {
17976  Relation classRel;
17977 
17978  Assert(objsMoved != NULL);
17979 
17980  /* OK, modify the pg_class row and pg_depend entry */
17981  classRel = table_open(RelationRelationId, RowExclusiveLock);
17982 
17983  AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
17984  nspOid, true, objsMoved);
17985 
17986  /* Fix the table's row type too, if it has one */
17987  if (OidIsValid(rel->rd_rel->reltype))
17988  AlterTypeNamespaceInternal(rel->rd_rel->reltype,
17989  nspOid, false, false, objsMoved);
17990 
17991  /* Fix other dependent stuff */
17992  AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
17993  AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
17994  objsMoved, AccessExclusiveLock);
17995  AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
17996  false, objsMoved);
17997 
17998  table_close(classRel, RowExclusiveLock);
17999 }
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:18122
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:18077
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 9729 of file tablecmds.c.

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

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

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

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

13664 {
13665  Assert(expr != NULL);
13666 
13667  for (;;)
13668  {
13669  /* only one varno, so no need to check that */
13670  if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13671  return false;
13672  else if (IsA(expr, RelabelType))
13673  expr = (Node *) ((RelabelType *) expr)->arg;
13674  else if (IsA(expr, CoerceToDomain))
13675  {
13676  CoerceToDomain *d = (CoerceToDomain *) expr;
13677 
13679  return true;
13680  expr = (Node *) d->arg;
13681  }
13682  else if (IsA(expr, FuncExpr))
13683  {
13684  FuncExpr *f = (FuncExpr *) expr;
13685 
13686  switch (f->funcid)
13687  {
13688  case F_TIMESTAMPTZ_TIMESTAMP:
13689  case F_TIMESTAMP_TIMESTAMPTZ:
13691  return true;
13692  else
13693  expr = linitial(f->args);
13694  break;
13695  default:
13696  return true;
13697  }
13698  }
13699  else
13700  return true;
13701  }
13702 }
bool TimestampTimestampTzRequiresRewrite(void)
Definition: timestamp.c:6273
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 4779 of file tablecmds.c.

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

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

20841 {
20842  List *constraints;
20843  ListCell *cell;
20844 
20845  constraints = GetParentedForeignKeyRefs(partition);
20846 
20847  foreach(cell, constraints)
20848  {
20849  Oid constrOid = lfirst_oid(cell);
20850  HeapTuple tuple;
20851  Form_pg_constraint constrForm;
20852  Relation rel;
20853  Trigger trig = {0};
20854 
20855  tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
20856  if (!HeapTupleIsValid(tuple))
20857  elog(ERROR, "cache lookup failed for constraint %u", constrOid);
20858  constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20859 
20860  Assert(OidIsValid(constrForm->conparentid));
20861  Assert(constrForm->confrelid == RelationGetRelid(partition));
20862 
20863  /* prevent data changes into the referencing table until commit */
20864  rel = table_open(constrForm->conrelid, ShareLock);
20865 
20866  trig.tgoid = InvalidOid;
20867  trig.tgname = NameStr(constrForm->conname);
20869  trig.tgisinternal = true;
20870  trig.tgconstrrelid = RelationGetRelid(partition);
20871  trig.tgconstrindid = constrForm->conindid;
20872  trig.tgconstraint = constrForm->oid;
20873  trig.tgdeferrable = false;
20874  trig.tginitdeferred = false;
20875  /* we needn't fill in remaining fields */
20876 
20877  RI_PartitionRemove_Check(&trig, rel, partition);
20878 
20879  ReleaseSysCache(tuple);
20880 
20881  table_close(rel, NoLock);
20882  }
20883 }
#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:20787
#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 18405 of file tablecmds.c.

18407 {
18408  ListCell *cur_item;
18409 
18410  foreach(cur_item, on_commits)
18411  {
18412  OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18413 
18414  if (!isCommit && oc->creating_subid == mySubid)
18415  {
18416  /* cur_item must be removed */
18418  pfree(oc);
18419  }
18420  else
18421  {
18422  /* cur_item must be preserved */
18423  if (oc->creating_subid == mySubid)
18424  oc->creating_subid = parentSubid;
18425  if (oc->deleting_subid == mySubid)
18426  oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18427  }
18428  }
18429 }
#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:125
SubTransactionId deleting_subid
Definition: tablecmds.c:126
static List * on_commits
Definition: tablecmds.c:129

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

18374 {
18375  ListCell *cur_item;
18376 
18377  foreach(cur_item, on_commits)
18378  {
18379  OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18380 
18381  if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18383  {
18384  /* cur_item must be removed */
18386  pfree(oc);
18387  }
18388  else
18389  {
18390  /* cur_item must be preserved */
18393  }
18394  }
18395 }

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

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

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

9616 {
9618 
9619  Assert(IsA(newConstraint, Constraint));
9620 
9621  /*
9622  * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9623  * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9624  * parse_utilcmd.c).
9625  */
9626  switch (newConstraint->contype)
9627  {
9628  case CONSTR_CHECK:
9629  case CONSTR_NOTNULL:
9630  address =
9631  ATAddCheckNNConstraint(wqueue, tab, rel,
9632  newConstraint, recurse, false, is_readd,
9633  lockmode);
9634  break;
9635 
9636  case CONSTR_FOREIGN:
9637 
9638  /*
9639  * Assign or validate constraint name
9640  */
9641  if (newConstraint->conname)
9642  {
9644  RelationGetRelid(rel),
9645  newConstraint->conname))
9646  ereport(ERROR,
9648  errmsg("constraint \"%s\" for relation \"%s\" already exists",
9649  newConstraint->conname,
9650  RelationGetRelationName(rel))));
9651  }
9652  else
9653  newConstraint->conname =
9656  "fkey",
9657  RelationGetNamespace(rel),
9658  NIL);
9659 
9660  address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9661  newConstraint,
9662  recurse, false,
9663  lockmode);
9664  break;
9665 
9666  default:
9667  elog(ERROR, "unrecognized constraint type: %d",
9668  (int) newConstraint->contype);
9669  }
9670 
9671  return address;
9672 }
@ 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:9887

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

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

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

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

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

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

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

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

9502 {
9503  ObjectAddress address;
9504 
9506 
9507  /* The CreateStatsStmt has already been through transformStatsStmt */
9508  Assert(stmt->transformed);
9509 
9510  address = CreateStatistics(stmt);
9511 
9512  return address;
9513 }
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 14891 of file tablecmds.c.

14895 {
14896  Relation ftrel;
14897  Relation attrel;
14898  ForeignServer *server;
14899  ForeignDataWrapper *fdw;
14900  HeapTuple tuple;
14901  HeapTuple newtuple;
14902  bool isnull;
14903  Datum repl_val[Natts_pg_attribute];
14904  bool repl_null[Natts_pg_attribute];
14905  bool repl_repl[Natts_pg_attribute];
14906  Datum datum;
14907  Form_pg_foreign_table fttableform;
14908  Form_pg_attribute atttableform;
14910  ObjectAddress address;
14911 
14912  if (options == NIL)
14913  return InvalidObjectAddress;
14914 
14915  /* First, determine FDW validator associated to the foreign table. */
14916  ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14917  tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14918  if (!HeapTupleIsValid(tuple))
14919  ereport(ERROR,
14920  (errcode(ERRCODE_UNDEFINED_OBJECT),
14921  errmsg("foreign table \"%s\" does not exist",
14922  RelationGetRelationName(rel))));
14923  fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14924  server = GetForeignServer(fttableform->ftserver);
14925  fdw = GetForeignDataWrapper(server->fdwid);
14926 
14927  table_close(ftrel, AccessShareLock);
14928  ReleaseSysCache(tuple);
14929 
14930  attrel = table_open(AttributeRelationId, RowExclusiveLock);
14931  tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14932  if (!HeapTupleIsValid(tuple))
14933  ereport(ERROR,
14934  (errcode(ERRCODE_UNDEFINED_COLUMN),
14935  errmsg("column \"%s\" of relation \"%s\" does not exist",
14936  colName, RelationGetRelationName(rel))));
14937 
14938  /* Prevent them from altering a system attribute */
14939  atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14940  attnum = atttableform->attnum;
14941  if (attnum <= 0)
14942  ereport(ERROR,
14943  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14944  errmsg("cannot alter system column \"%s\"", colName)));
14945 
14946 
14947  /* Initialize buffers for new tuple values */
14948  memset(repl_val, 0, sizeof(repl_val));
14949  memset(repl_null, false, sizeof(repl_null));
14950  memset(repl_repl, false, sizeof(repl_repl));
14951 
14952  /* Extract the current options */
14953  datum = SysCacheGetAttr(ATTNAME,
14954  tuple,
14955  Anum_pg_attribute_attfdwoptions,
14956  &isnull);
14957  if (isnull)
14958  datum = PointerGetDatum(NULL);
14959 
14960  /* Transform the options */
14961  datum = transformGenericOptions(AttributeRelationId,
14962  datum,
14963  options,
14964  fdw->fdwvalidator);
14965 
14966  if (PointerIsValid(DatumGetPointer(datum)))
14967  repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
14968  else
14969  repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
14970 
14971  repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
14972 
14973  /* Everything looks good - update the tuple */
14974 
14975  newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
14976  repl_val, repl_null, repl_repl);
14977 
14978  CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
14979 
14980  InvokeObjectPostAlterHook(RelationRelationId,
14981  RelationGetRelid(rel),
14982  atttableform->attnum);
14983  ObjectAddressSubSet(address, RelationRelationId,
14984  RelationGetRelid(rel), attnum);
14985 
14986  ReleaseSysCache(tuple);
14987 
14988  table_close(attrel, RowExclusiveLock);
14989 
14990  heap_freetuple(newtuple);
14991 
14992  return address;
14993 }
#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 13710 of file tablecmds.c.

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

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

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

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

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

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

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

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

15384 {
15385  Oid indexOid;
15386  ObjectAddress address;
15387 
15388  indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15389 
15390  if (!OidIsValid(indexOid))
15391  ereport(ERROR,
15392  (errcode(ERRCODE_UNDEFINED_OBJECT),
15393  errmsg("index \"%s\" for table \"%s\" does not exist",
15394  indexName, RelationGetRelationName(rel))));
15395 
15396  /* Check index is valid to cluster on */
15397  check_index_is_clusterable(rel, indexOid, lockmode);
15398 
15399  /* And do the work */
15400  mark_index_clustered(rel, indexOid, false);
15401 
15402  ObjectAddressSet(address,
15403  RelationRelationId, indexOid);
15404 
15405  return address;
15406 }
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 5279 of file tablecmds.c.

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

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

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

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

19867 {
19868  Relation partRel;
19869  ObjectAddress address;
19870  Oid defaultPartOid;
19871 
19872  /*
19873  * We must lock the default partition, because detaching this partition
19874  * will change its partition constraint.
19875  */
19876  defaultPartOid =
19878  if (OidIsValid(defaultPartOid))
19879  {
19880  /*
19881  * Concurrent detaching when a default partition exists is not
19882  * supported. The main problem is that the default partition
19883  * constraint would change. And there's a definitional problem: what
19884  * should happen to the tuples that are being inserted that belong to
19885  * the partition being detached? Putting them on the partition being
19886  * detached would be wrong, since they'd become "lost" after the
19887  * detaching completes but we cannot put them in the default partition
19888  * either until we alter its partition constraint.
19889  *
19890  * I think we could solve this problem if we effected the constraint
19891  * change before committing the first transaction. But the lock would
19892  * have to remain AEL and it would cause concurrent query planning to
19893  * be blocked, so changing it that way would be even worse.
19894  */
19895  if (concurrent)
19896  ereport(ERROR,
19897  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19898  errmsg("cannot detach partitions concurrently when a default partition exists")));
19899  LockRelationOid(defaultPartOid, AccessExclusiveLock);
19900  }
19901 
19902  /*
19903  * In concurrent mode, the partition is locked with share-update-exclusive
19904  * in the first transaction. This allows concurrent transactions to be
19905  * doing DML to the partition.
19906  */
19907  partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19909 
19910  /*
19911  * Check inheritance conditions and either delete the pg_inherits row (in
19912  * non-concurrent mode) or just set the inhdetachpending flag.
19913  */
19914  if (!concurrent)
19915  RemoveInheritance(partRel, rel, false);
19916  else
19917  MarkInheritDetached(partRel, rel);
19918 
19919  /*
19920  * Ensure that foreign keys still hold after this detach. This keeps
19921  * locks on the referencing tables, which prevents concurrent transactions
19922  * from adding rows that we wouldn't see. For this to work in concurrent
19923  * mode, it is critical that the partition appears as no longer attached
19924  * for the RI queries as soon as the first transaction commits.
19925  */
19927 
19928  /*
19929  * Concurrent mode has to work harder; first we add a new constraint to
19930  * the partition that matches the partition constraint. Then we close our
19931  * existing transaction, and in a new one wait for all processes to catch
19932  * up on the catalog updates we've done so far; at that point we can
19933  * complete the operation.
19934  */
19935  if (concurrent)
19936  {
19937  Oid partrelid,
19938  parentrelid;
19939  LOCKTAG tag;
19940  char *parentrelname;
19941  char *partrelname;
19942 
19943  /*
19944  * Add a new constraint to the partition being detached, which
19945  * supplants the partition constraint (unless there is one already).
19946  */
19947  DetachAddConstraintIfNeeded(wqueue, partRel);
19948 
19949  /*
19950  * We're almost done now; the only traces that remain are the
19951  * pg_inherits tuple and the partition's relpartbounds. Before we can
19952  * remove those, we need to wait until all transactions that know that
19953  * this is a partition are gone.
19954  */
19955 
19956  /*
19957  * Remember relation OIDs to re-acquire them later; and relation names
19958  * too, for error messages if something is dropped in between.
19959  */
19960  partrelid = RelationGetRelid(partRel);
19961  parentrelid = RelationGetRelid(rel);
19962  parentrelname = MemoryContextStrdup(PortalContext,
19964  partrelname = MemoryContextStrdup(PortalContext,
19965  RelationGetRelationName(partRel));
19966 
19967  /* Invalidate relcache entries for the parent -- must be before close */
19969 
19970  table_close(partRel, NoLock);
19971  table_close(rel, NoLock);
19972  tab->rel = NULL;
19973 
19974  /* Make updated catalog entry visible */
19977 
19979 
19980  /*
19981  * Now wait. This ensures that all queries that were planned
19982  * including the partition are finished before we remove the rest of
19983  * catalog entries. We don't need or indeed want to acquire this
19984  * lock, though -- that would block later queries.
19985  *
19986  * We don't need to concern ourselves with waiting for a lock on the
19987  * partition itself, since we will acquire AccessExclusiveLock below.
19988  */
19989  SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
19991 
19992  /*
19993  * Now acquire locks in both relations again. Note they may have been
19994  * removed in the meantime, so care is required.
19995  */
19996  rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
19997  partRel = try_relation_open(partrelid, AccessExclusiveLock);
19998 
19999  /* If the relations aren't there, something bad happened; bail out */
20000  if (rel == NULL)
20001  {
20002  if (partRel != NULL) /* shouldn't happen */
20003  elog(WARNING, "dangling partition \"%s\" remains, can't fix",
20004  partrelname);
20005  ereport(ERROR,
20006  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20007  errmsg("partitioned table \"%s\" was removed concurrently",
20008  parentrelname)));
20009  }
20010  if (partRel == NULL)
20011  ereport(ERROR,
20012  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20013  errmsg("partition \"%s\" was removed concurrently", partrelname)));
20014 
20015  tab->rel = rel;
20016  }
20017 
20018  /* Do the final part of detaching */
20019  DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
20020 
20021  ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20022 
20023  /* keep our lock until commit */
20024  table_close(partRel, NoLock);
20025 
20026  return address;
20027 }
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:16804
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:16887
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:20036
static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
Definition: tablecmds.c:20310
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:20840
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 20275 of file tablecmds.c.

20276 {
20277  Relation partRel;
20278  ObjectAddress address;
20279  Snapshot snap = GetActiveSnapshot();
20280 
20282 
20283  /*
20284  * Wait until existing snapshots are gone. This is important if the
20285  * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20286  * user could immediately run DETACH FINALIZE without actually waiting for
20287  * existing transactions. We must not complete the detach action until
20288  * all such queries are complete (otherwise we would present them with an
20289  * inconsistent view of catalogs).
20290  */
20291  WaitForOlderSnapshots(snap->xmin, false);
20292 
20293  DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20294 
20295  ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20296 
20297  table_close(partRel, NoLock);
20298 
20299  return address;
20300 }
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 15415 of file tablecmds.c.

15416 {
15417  mark_index_clustered(rel, InvalidOid, false);
15418 }

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

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

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

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

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

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

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

16751 {
16752  ObjectAddress address;
16753  Relation parent_rel;
16754 
16755  if (rel->rd_rel->relispartition)
16756  ereport(ERROR,
16757  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16758  errmsg("cannot change inheritance of a partition")));
16759 
16760  /*
16761  * AccessShareLock on the parent is probably enough, seeing that DROP
16762  * TABLE doesn't lock parent tables at all. We need some lock since we'll
16763  * be inspecting the parent's schema.
16764  */
16765  parent_rel = table_openrv(parent, AccessShareLock);
16766 
16767  /*
16768  * We don't bother to check ownership of the parent table --- ownership of
16769  * the child is presumed enough rights.
16770  */
16771 
16772  /* Off to RemoveInheritance() where most of the work happens */
16773  RemoveInheritance(rel, parent_rel, false);
16774 
16775  /*
16776  * If parent_rel has a primary key, then child_rel has not-null
16777  * constraints that make these columns as non nullable. Mark those
16778  * constraints as no longer inherited by this parent.
16779  */
16780  ATInheritAdjustNotNulls(parent_rel, rel, -1);
16781 
16782  /*
16783  * If the parent has a primary key, then we decrement counts for all NOT
16784  * NULL constraints
16785  */
16786 
16787  ObjectAddressSet(address, RelationRelationId,
16788  RelationGetRelid(parent_rel));
16789 
16790  /* keep our lock on the parent relation until commit */
16791  table_close(parent_rel, NoLock);
16792 
16793  return address;
16794 }

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

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

17324 {
17325  Oid relid = RelationGetRelid(rel);
17326  Relation relationRelation;
17327  HeapTuple tuple;
17328 
17329  if (!OidIsValid(rel->rd_rel->reloftype))
17330  ereport(ERROR,
17331  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17332  errmsg("\"%s\" is not a typed table",
17333  RelationGetRelationName(rel))));
17334 
17335  /*
17336  * We don't bother to check ownership of the type --- ownership of the
17337  * table is presumed enough rights. No lock required on the type, either.
17338  */
17339 
17340  drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17342 
17343  /* Clear pg_class.reloftype */
17344  relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17345  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17346  if (!HeapTupleIsValid(tuple))
17347  elog(ERROR, "cache lookup failed for relation %u", relid);
17348  ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17349  CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17350 
17351  InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17352 
17353  heap_freetuple(tuple);
17354  table_close(relationRelation, RowExclusiveLock);
17355 }

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

16155 {
16156  EnableDisableRule(rel, rulename, fires_when);
16157 
16158  InvokeObjectPostAlterHook(RelationRelationId,
16159  RelationGetRelid(rel), 0);
16160 }
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 16135 of file tablecmds.c.

16138 {
16139  EnableDisableTrigger(rel, trigname, InvalidOid,
16140  fires_when, skip_system, recurse,
16141  lockmode);
16142 
16143  InvokeObjectPostAlterHook(RelationRelationId,
16144  RelationGetRelid(rel), 0);
16145 }
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 17593 of file tablecmds.c.

17594 {
17595  Relation pg_class;
17596  Oid relid;
17597  HeapTuple tuple;
17598 
17599  relid = RelationGetRelid(rel);
17600 
17601  pg_class = table_open(RelationRelationId, RowExclusiveLock);
17602 
17603  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17604 
17605  if (!HeapTupleIsValid(tuple))
17606  elog(ERROR, "cache lookup failed for relation %u", relid);
17607 
17608  ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
17609  CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17610 
17611  InvokeObjectPostAlterHook(RelationRelationId,
17612  RelationGetRelid(rel), 0);
17613 
17614  table_close(pg_class, RowExclusiveLock);
17615  heap_freetuple(tuple);
17616 }

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

17623 {
17624  Relation ftrel;
17625  ForeignServer *server;
17626  ForeignDataWrapper *fdw;
17627  HeapTuple tuple;
17628  bool isnull;
17629  Datum repl_val[Natts_pg_foreign_table];
17630  bool repl_null[Natts_pg_foreign_table];
17631  bool repl_repl[Natts_pg_foreign_table];
17632  Datum datum;
17633  Form_pg_foreign_table tableform;
17634 
17635  if (options == NIL)
17636  return;
17637 
17638  ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
17639 
17640  tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
17641  ObjectIdGetDatum(rel->rd_id));
17642  if (!HeapTupleIsValid(tuple))
17643  ereport(ERROR,
17644  (errcode(ERRCODE_UNDEFINED_OBJECT),
17645  errmsg("foreign table \"%s\" does not exist",
17646  RelationGetRelationName(rel))));
17647  tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
17648  server = GetForeignServer(tableform->ftserver);
17649  fdw = GetForeignDataWrapper(server->fdwid);
17650 
17651  memset(repl_val, 0, sizeof(repl_val));
17652  memset(repl_null, false, sizeof(repl_null));
17653  memset(repl_repl, false, sizeof(repl_repl));
17654 
17655  /* Extract the current options */
17656  datum = SysCacheGetAttr(FOREIGNTABLEREL,
17657  tuple,
17658  Anum_pg_foreign_table_ftoptions,
17659  &isnull);
17660  if (isnull)
17661  datum = PointerGetDatum(NULL);
17662 
17663  /* Transform the options */
17664  datum = transformGenericOptions(ForeignTableRelationId,
17665  datum,
17666  options,
17667  fdw->fdwvalidator);
17668 
17669  if (PointerIsValid(DatumGetPointer(datum)))
17670  repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
17671  else
17672  repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
17673 
17674  repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
17675 
17676  /* Everything looks good - update the tuple */
17677 
17678  tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
17679  repl_val, repl_null, repl_repl);
17680 
17681  CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17682 
17683  /*
17684  * Invalidate relcache so that all sessions will refresh any cached plans
17685  * that might depend on the old options.
17686  */
17688 
17689  InvokeObjectPostAlterHook(ForeignTableRelationId,
17690  RelationGetRelid(rel), 0);
17691 
17692  table_close(ftrel, RowExclusiveLock);
17693 
17694  heap_freetuple(tuple);
17695 }

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

21501 {
21502  Relation newPartRel;
21503  ListCell *listptr;
21504  List *mergingPartitionsList = NIL;
21505  Oid defaultPartOid;
21506  char tmpRelName[NAMEDATALEN];
21507  RangeVar *mergePartName = cmd->name;
21508  bool isSameName = false;
21509 
21510  /*
21511  * Lock all merged partitions, check them and create list with partitions
21512  * contexts.
21513  */
21514  foreach(listptr, cmd->partlist)
21515  {
21516  RangeVar *name = (RangeVar *) lfirst(listptr);
21517  Relation mergingPartition;
21518 
21519  /*
21520  * We are going to detach and remove this partition: need to use
21521  * exclusive lock for preventing DML-queries to the partition.
21522  */
21523  mergingPartition = table_openrv(name, AccessExclusiveLock);
21524 
21525  /*
21526  * Checking that two partitions have the same name was before, in
21527  * function transformPartitionCmdForMerge().
21528  */
21529  if (equal(name, cmd->name))
21530  /* One new partition can have the same name as merged partition. */
21531  isSameName = true;
21532 
21533  /* Store a next merging partition into the list. */
21534  mergingPartitionsList = lappend(mergingPartitionsList,
21535  mergingPartition);
21536  }
21537 
21538  /* Detach all merged partitions. */
21539  defaultPartOid =
21541  foreach(listptr, mergingPartitionsList)
21542  {
21543  Relation mergingPartition = (Relation) lfirst(listptr);
21544 
21545  /* Remove the pg_inherits row first. */
21546  RemoveInheritance(mergingPartition, rel, false);
21547  /* Do the final part of detaching. */
21548  DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid);
21549  }
21550 
21551  /* Create table for new partition, use partitioned table as model. */
21552  if (isSameName)
21553  {
21554  /* Create partition table with generated temporary name. */
21555  sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
21557  tmpRelName, -1);
21558  }
21559  createPartitionTable(mergePartName,
21561  RelationGetRelationName(rel), -1),
21562  context);
21563 
21564  /*
21565  * Open the new partition and acquire exclusive lock on it. This will
21566  * stop all the operations with partitioned table. This might seem
21567  * excessive, but this is the way we make sure nobody is planning queries
21568  * involving merging partitions.
21569  */
21570  newPartRel = table_openrv(mergePartName, AccessExclusiveLock);
21571 
21572  /* Copy data from merged partitions to new partition. */
21573  moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);
21574 
21575  /*
21576  * Attach a new partition to the partitioned table. wqueue = NULL:
21577  * verification for each cloned constraint is not need.
21578  */
21579  attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
21580 
21581  /* Unlock and drop merged partitions. */
21582  foreach(listptr, mergingPartitionsList)
21583  {
21584  ObjectAddress object;
21585  Relation mergingPartition = (Relation) lfirst(listptr);
21586 
21587  /* Get relation id before table_close() call. */
21588  object.objectId = RelationGetRelid(mergingPartition);
21589  object.classId = RelationRelationId;
21590  object.objectSubId = 0;
21591 
21592  /* Keep the lock until commit. */
21593  table_close(mergingPartition, NoLock);
21594 
21595  performDeletion(&object, DROP_RESTRICT, 0);
21596  }
21597  list_free(mergingPartitionsList);
21598 
21599  /* Rename new partition if it is needed. */
21600  if (isSameName)
21601  {
21602  /*
21603  * We must bump the command counter to make the new partition tuple
21604  * visible for rename.
21605  */
21607  /* Rename partition. */
21609  cmd->name->relname, false, false);
21610  }
21611  /* Keep the lock until commit. */
21612  table_close(newPartRel, NoLock);
21613 }
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:21218
static void moveMergedTablesRows(Relation rel, List *mergingPartitionsList, Relation newPartRel)
Definition: tablecmds.c:21402

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

17456 {
17457  Oid indexOid;
17458  Relation indexRel;
17459  int key;
17460 
17461  if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17462  {
17463  relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17464  return;
17465  }
17466  else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17467  {
17468  relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17469  return;
17470  }
17471  else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17472  {
17473  relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17474  return;
17475  }
17476  else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17477  {
17478  /* fallthrough */ ;
17479  }
17480  else
17481  elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17482 
17483  /* Check that the index exists */
17484  indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17485  if (!OidIsValid(indexOid))
17486  ereport(ERROR,
17487  (errcode(ERRCODE_UNDEFINED_OBJECT),
17488  errmsg("index \"%s\" for table \"%s\" does not exist",
17489  stmt->name, RelationGetRelationName(rel))));
17490 
17491  indexRel = index_open(indexOid, ShareLock);
17492 
17493  /* Check that the index is on the relation we're altering. */
17494  if (indexRel->rd_index == NULL ||
17495  indexRel->rd_index->indrelid != RelationGetRelid(rel))
17496  ereport(ERROR,
17497  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17498  errmsg("\"%s\" is not an index for table \"%s\"",
17499  RelationGetRelationName(indexRel),
17500  RelationGetRelationName(rel))));
17501  /* The AM must support uniqueness, and the index must in fact be unique. */
17502  if (!indexRel->rd_indam->amcanunique ||
17503  !indexRel->rd_index->indisunique)
17504  ereport(ERROR,
17505  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17506  errmsg("cannot use non-unique index \"%s\" as replica identity",
17507  RelationGetRelationName(indexRel))));
17508  /* Deferred indexes are not guaranteed to be always unique. */
17509  if (!indexRel->rd_index->indimmediate)
17510  ereport(ERROR,
17511  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17512  errmsg("cannot use non-immediate index \"%s\" as replica identity",
17513  RelationGetRelationName(indexRel))));
17514  /* Expression indexes aren't supported. */
17515  if (RelationGetIndexExpressions(indexRel) != NIL)
17516  ereport(ERROR,
17517  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17518  errmsg("cannot use expression index \"%s\" as replica identity",
17519  RelationGetRelationName(indexRel))));
17520  /* Predicate indexes aren't supported. */
17521  if (RelationGetIndexPredicate(indexRel) != NIL)
17522  ereport(ERROR,
17523  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17524  errmsg("cannot use partial index \"%s\" as replica identity",
17525  RelationGetRelationName(indexRel))));
17526 
17527  /* Check index for nullable columns. */
17528  for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17529  {
17530  int16 attno = indexRel->rd_index->indkey.values[key];
17531  Form_pg_attribute attr;
17532 
17533  /*
17534  * Reject any other system columns. (Going forward, we'll disallow
17535  * indexes containing such columns in the first place, but they might
17536  * exist in older branches.)
17537  */
17538  if (attno <= 0)
17539  ereport(ERROR,
17540  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17541  errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17542  RelationGetRelationName(indexRel), attno)));
17543 
17544  attr = TupleDescAttr(rel->rd_att, attno - 1);
17545  if (!attr->attnotnull)
17546  ereport(ERROR,
17547  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17548  errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17549  RelationGetRelationName(indexRel),
17550  NameStr(attr->attname))));
17551  }
17552 
17553  /* This index is suitable for use as a replica identity. Mark it. */
17554  relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17555 
17556  index_close(indexRel, NoLock);
17557 }
#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:17367

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

15462 {
15463  Relation pg_class;
15464  Oid oldAccessMethodId;
15465  HeapTuple tuple;
15466  Form_pg_class rd_rel;
15467  Oid reloid = RelationGetRelid(rel);
15468 
15469  /*
15470  * Shouldn't be called on relations having storage; these are processed in
15471  * phase 3.
15472  */
15473  Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15474 
15475  /* Get a modifiable copy of the relation's pg_class row. */
15476  pg_class = table_open(RelationRelationId, RowExclusiveLock);
15477 
15478  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15479  if (!HeapTupleIsValid(tuple))
15480  elog(ERROR, "cache lookup failed for relation %u", reloid);
15481  rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15482 
15483  /* Update the pg_class row. */
15484  oldAccessMethodId = rd_rel->relam;
15485  rd_rel->relam = newAccessMethodId;
15486 
15487  /* Leave if no update required */
15488  if (rd_rel->relam == oldAccessMethodId)
15489  {
15490  heap_freetuple(tuple);
15491  table_close(pg_class, RowExclusiveLock);
15492  return;
15493  }
15494 
15495  CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15496 
15497  /*
15498  * Update the dependency on the new access method. No dependency is added
15499  * if the new access method is InvalidOid (default case). Be very careful
15500  * that this has to compare the previous value stored in pg_class with the
15501  * new one.
15502  */
15503  if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15504  {
15505  ObjectAddress relobj,
15506  referenced;
15507 
15508  /*
15509  * New access method is defined and there was no dependency
15510  * previously, so record a new one.
15511  */
15512  ObjectAddressSet(relobj, RelationRelationId, reloid);
15513  ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15514  recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15515  }
15516  else if (OidIsValid(oldAccessMethodId) &&
15517  !OidIsValid(rd_rel->relam))
15518  {
15519  /*
15520  * There was an access method defined, and no new one, so just remove
15521  * the existing dependency.
15522  */
15523  deleteDependencyRecordsForClass(RelationRelationId, reloid,
15524  AccessMethodRelationId,
15526  }
15527  else
15528  {
15529  Assert(OidIsValid(oldAccessMethodId) &&
15530  OidIsValid(rd_rel->relam));
15531 
15532  /* Both are valid, so update the dependency */
15533  changeDependencyFor(RelationRelationId, reloid,
15534  AccessMethodRelationId,
15535  oldAccessMethodId, rd_rel->relam);
15536  }
15537 
15538  /* make the relam and dependency changes visible */
15540 
15541  InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15542 
15543  heap_freetuple(tuple);
15544  table_close(pg_class, RowExclusiveLock);
15545 }

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

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

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

17707 {
17708  Relation attrel;
17709  HeapTuple tuple;
17710  Form_pg_attribute atttableform;
17712  char *compression;
17713  char cmethod;
17714  ObjectAddress address;
17715 
17716  compression = strVal(newValue);
17717 
17718  attrel = table_open(AttributeRelationId, RowExclusiveLock);
17719 
17720  /* copy the cache entry so we can scribble on it below */
17721  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
17722  if (!HeapTupleIsValid(tuple))
17723  ereport(ERROR,
17724  (errcode(ERRCODE_UNDEFINED_COLUMN),
17725  errmsg("column \"%s\" of relation \"%s\" does not exist",
17726  column, RelationGetRelationName(rel))));
17727 
17728  /* prevent them from altering a system attribute */
17729  atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
17730  attnum = atttableform->attnum;
17731  if (attnum <= 0)
17732  ereport(ERROR,
17733  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17734  errmsg("cannot alter system column \"%s\"", column)));
17735 
17736  /*
17737  * Check that column type is compressible, then get the attribute
17738  * compression method code
17739  */
17740  cmethod = GetAttributeCompression(atttableform->atttypid, compression);
17741 
17742  /* update pg_attribute entry */
17743  atttableform->attcompression = cmethod;
17744  CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
17745 
17746  InvokeObjectPostAlterHook(RelationRelationId,
17747  RelationGetRelid(rel),
17748  attnum);
17749 
17750  /*
17751  * Apply the change to indexes as well (only for simple index columns,
17752  * matching behavior of index.c ConstructTupleDescriptor()).
17753  */
17754  SetIndexStorageProperties(rel, attrel, attnum,
17755  false, 0,
17756  true, cmethod,
17757  lockmode);
17758 
17759  heap_freetuple(tuple);
17760 
17761  table_close(attrel, RowExclusiveLock);
17762 
17763  /* make changes visible */
17765 
17766  ObjectAddressSubSet(address, RelationRelationId,
17767  RelationGetRelid(rel), attnum);
17768  return address;
17769 }
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:20889
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:9003

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

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

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

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

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

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

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

17564 {
17565  Relation pg_class;
17566  Oid relid;
17567  HeapTuple tuple;
17568 
17569  relid = RelationGetRelid(rel);
17570 
17571  /* Pull the record for this relation and update it */
17572  pg_class = table_open(RelationRelationId, RowExclusiveLock);
17573 
17574  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17575 
17576  if (!HeapTupleIsValid(tuple))
17577  elog(ERROR, "cache lookup failed for relation %u", relid);
17578 
17579  ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
17580  CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17581 
17582  InvokeObjectPostAlterHook(RelationRelationId,
17583  RelationGetRelid(rel), 0);
17584 
17585  table_close(pg_class, RowExclusiveLock);
17586  heap_freetuple(tuple);
17587 }

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

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

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

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

15785 {
15786  Relation rel;
15787  Oid reltoastrelid;
15788  RelFileNumber newrelfilenumber;
15789  RelFileLocator newrlocator;
15790  List *reltoastidxids = NIL;
15791  ListCell *lc;
15792 
15793  /*
15794  * Need lock here in case we are recursing to toast table or index
15795  */
15796  rel = relation_open(tableOid, lockmode);
15797 
15798  /* Check first if relation can be moved to new tablespace */
15799  if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15800  {
15801  InvokeObjectPostAlterHook(RelationRelationId,
15802  RelationGetRelid(rel), 0);
15803  relation_close(rel, NoLock);
15804  return;
15805  }
15806 
15807  reltoastrelid = rel->rd_rel->reltoastrelid;
15808  /* Fetch the list of indexes on toast relation if necessary */
15809  if (OidIsValid(reltoastrelid))
15810  {
15811  Relation toastRel = relation_open(reltoastrelid, lockmode);
15812 
15813  reltoastidxids = RelationGetIndexList(toastRel);
15814  relation_close(toastRel, lockmode);
15815  }
15816 
15817  /*
15818  * Relfilenumbers are not unique in databases across tablespaces, so we
15819  * need to allocate a new one in the new tablespace.
15820  */
15821  newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15822  rel->rd_rel->relpersistence);
15823 
15824  /* Open old and new relation */
15825  newrlocator = rel->rd_locator;
15826  newrlocator.relNumber = newrelfilenumber;
15827  newrlocator.spcOid = newTableSpace;
15828 
15829  /* hand off to AM to actually create new rel storage and copy the data */
15830  if (rel->rd_rel->relkind == RELKIND_INDEX)
15831  {
15832  index_copy_data(rel, newrlocator);
15833  }
15834  else
15835  {
15836  Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15837  table_relation_copy_data(rel, &newrlocator);
15838  }
15839 
15840  /*
15841  * Update the pg_class row.
15842  *
15843  * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15844  * executed on pg_class or its indexes (the above copy wouldn't contain
15845  * the updated pg_class entry), but that's forbidden with
15846  * CheckRelationTableSpaceMove().
15847  */
15848  SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15849 
15850  InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15851 
15853 
15854  relation_close(rel, NoLock);
15855 
15856  /* Make sure the reltablespace change is visible */
15858 
15859  /* Move associated toast relation and/or indexes, too */
15860  if (OidIsValid(reltoastrelid))
15861  ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15862  foreach(lc, reltoastidxids)
15863  ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15864 
15865  /* Clean up */
15866  list_free(reltoastidxids);
15867 }
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:15784
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3630
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3687
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:16078

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

15878 {
15879  /*
15880  * Shouldn't be called on relations having storage; these are processed in
15881  * phase 3.
15882  */
15883  Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15884 
15885  /* check if relation can be moved to its new tablespace */
15886  if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15887  {
15888  InvokeObjectPostAlterHook(RelationRelationId,
15889  RelationGetRelid(rel),
15890  0);
15891  return;
15892  }
15893 
15894  /* Update can be done, so change reltablespace */
15895  SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15896 
15897  InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15898 
15899  /* Make sure the reltablespace change is visible */
15901 }

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

21270 {
21271  Relation splitRel;
21272  Oid splitRelOid;
21273  char relname[NAMEDATALEN];
21274  Oid namespaceId;
21275  ListCell *listptr,
21276  *listptr2;
21277  bool isSameName = false;
21278  char tmpRelName[NAMEDATALEN];
21279  List *newPartRels = NIL;
21280  ObjectAddress object;
21281  RangeVar *parentName;
21282  Oid defaultPartOid;
21283 
21284  defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
21285 
21286  /*
21287  * We are going to detach and remove this partition: need to use exclusive
21288  * lock for preventing DML-queries to the partition.
21289  */
21290  splitRel = table_openrv(cmd->name, AccessExclusiveLock);
21291 
21292  splitRelOid = RelationGetRelid(splitRel);
21293 
21294  /* Check descriptions of new partitions. */
21295  foreach(listptr, cmd->partlist)
21296  {
21297  Oid existing_relid;
21298  SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21299 
21301 
21302  /*
21303  * Look up the namespace in which we are supposed to create the
21304  * partition, check we have permission to create there, lock it
21305  * against concurrent drop, and mark stmt->relation as
21306  * RELPERSISTENCE_TEMP if a temporary namespace is selected.
21307  */
21308  namespaceId =
21310 
21311  /*
21312  * This would fail later on anyway if the relation already exists. But
21313  * by catching it here we can emit a nicer error message.
21314  */
21315  existing_relid = get_relname_relid(relname, namespaceId);
21316  if (existing_relid == splitRelOid && !isSameName)
21317  /* One new partition can have the same name as split partition. */
21318  isSameName = true;
21319  else if (existing_relid != InvalidOid)
21320  ereport(ERROR,
21321  (errcode(ERRCODE_DUPLICATE_TABLE),
21322  errmsg("relation \"%s\" already exists", relname)));
21323  }
21324 
21325  /* Detach split partition. */
21326  RemoveInheritance(splitRel, rel, false);
21327  /* Do the final part of detaching. */
21328  DetachPartitionFinalize(rel, splitRel, false, defaultPartOid);
21329 
21330  /*
21331  * If new partition has the same name as split partition then we should
21332  * rename split partition for reusing name.
21333  */
21334  if (isSameName)
21335  {
21336  /*
21337  * We must bump the command counter to make the split partition tuple
21338  * visible for renaming.
21339  */
21341  /* Rename partition. */
21342  sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
21343  RenameRelationInternal(splitRelOid, tmpRelName, false, false);
21344 
21345  /*
21346  * We must bump the command counter to make the split partition tuple
21347  * visible after renaming.
21348  */
21350  }
21351 
21352  /* Create new partitions (like split partition), without indexes. */
21354  RelationGetRelationName(rel), -1);
21355  foreach(listptr, cmd->partlist)
21356  {
21357  SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21358  Relation newPartRel;
21359 
21360  createPartitionTable(sps->name, parentName, context);
21361 
21362  /* Open the new partition and acquire exclusive lock on it. */
21363  newPartRel = table_openrv(sps->name, AccessExclusiveLock);
21364 
21365  newPartRels = lappend(newPartRels, newPartRel);
21366  }
21367 
21368  /* Copy data from split partition to new partitions. */
21369  moveSplitTableRows(rel, splitRel, cmd->partlist, newPartRels, defaultPartOid);
21370  /* Keep the lock until commit. */
21371  table_close(splitRel, NoLock);
21372 
21373  /* Attach new partitions to partitioned table. */
21374  forboth(listptr, cmd->partlist, listptr2, newPartRels)
21375  {
21376  SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21377  Relation newPartRel = (Relation) lfirst(listptr2);
21378 
21379  /*
21380  * wqueue = NULL: verification for each cloned constraint is not
21381  * needed.
21382  */
21383  attachPartitionTable(NULL, rel, newPartRel, sps->bound);
21384  /* Keep the lock until commit. */
21385  table_close(newPartRel, NoLock);
21386  }
21387 
21388  /* Drop split partition. */
21389  object.classId = RelationRelationId;
21390  object.objectId = splitRelOid;
21391  object.objectSubId = 0;
21392  /* Probably DROP_CASCADE is not needed. */
21393  performDeletion(&object, DROP_RESTRICT, 0);
21394 }
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:21022

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

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

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

17085 {
17086  Bitmapset *pkattnos;
17087 
17088  /* Quick exit when parent has no PK */
17089  if (!parent_rel->rd_rel->relhasindex)
17090  return;
17091 
17092  pkattnos = RelationGetIndexAttrBitmap(parent_rel,
17094  if (pkattnos != NULL)
17095  {
17096  Bitmapset *childattnums = NULL;
17097  AttrMap *attmap;
17098  int i;
17099 
17100  attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17101  RelationGetDescr(child_rel), true);
17102 
17103  i = -1;
17104  while ((i = bms_next_member(pkattnos, i)) >= 0)
17105  {
17106  childattnums = bms_add_member(childattnums,
17108  }
17109 
17110  /*
17111  * CCI is needed in case there's a NOT NULL PRIMARY KEY column in the
17112  * parent: the relevant not-null constraint in the child already had
17113  * its inhcount modified earlier.
17114  */
17116  AdjustNotNullInheritance(RelationGetRelid(child_rel), childattnums,
17117  inhcount);
17118  }
17119 }
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 5631 of file tablecmds.c.

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

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

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

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

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

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

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

16171 {
16172  if (child_rel->rd_rel->reloftype)
16173  ereport(ERROR,
16174  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16175  errmsg("cannot change inheritance of typed table")));
16176 
16177  if (child_rel->rd_rel->relispartition)
16178  ereport(ERROR,
16179  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16180  errmsg("cannot change inheritance of a partition")));
16181 
16182  if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16183  ereport(ERROR,
16184  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16185  errmsg("cannot change inheritance of partitioned table")));
16186 }

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

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

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

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

17784 {
17785  Relation pg_constraint;
17786  HeapTuple tuple;
17787  SysScanDesc scan;
17788  ScanKeyData skey[1];
17789 
17790  /*
17791  * Disallow changing status for a temp table. Also verify whether we can
17792  * get away with doing nothing; in such cases we don't need to run the
17793  * checks below, either.
17794  */
17795  switch (rel->rd_rel->relpersistence)
17796  {
17797  case RELPERSISTENCE_TEMP:
17798  ereport(ERROR,
17799  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17800  errmsg("cannot change logged status of table \"%s\" because it is temporary",
17802  errtable(rel)));
17803  break;
17804  case RELPERSISTENCE_PERMANENT:
17805  if (toLogged)
17806  /* nothing to do */
17807  return false;
17808  break;
17809  case RELPERSISTENCE_UNLOGGED:
17810  if (!toLogged)
17811  /* nothing to do */
17812  return false;
17813  break;
17814  }
17815 
17816  /*
17817  * Check that the table is not part of any publication when changing to
17818  * UNLOGGED, as UNLOGGED tables can't be published.
17819  */
17820  if (!toLogged &&
17822  ereport(ERROR,
17823  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17824  errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17826  errdetail("Unlogged relations cannot be replicated.")));
17827 
17828  /*
17829  * Check existing foreign key constraints to preserve the invariant that
17830  * permanent tables cannot reference unlogged ones. Self-referencing
17831  * foreign keys can safely be ignored.
17832  */
17833  pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17834 
17835  /*
17836  * Scan conrelid if changing to permanent, else confrelid. This also
17837  * determines whether a useful index exists.
17838  */
17839  ScanKeyInit(&skey[0],
17840  toLogged ? Anum_pg_constraint_conrelid :
17841  Anum_pg_constraint_confrelid,
17842  BTEqualStrategyNumber, F_OIDEQ,
17844  scan = systable_beginscan(pg_constraint,
17845  toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
17846  true, NULL, 1, skey);
17847 
17848  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17849  {
17851 
17852  if (con->contype == CONSTRAINT_FOREIGN)
17853  {
17854  Oid foreignrelid;
17855  Relation foreignrel;
17856 
17857  /* the opposite end of what we used as scankey */
17858  foreignrelid = toLogged ? con->confrelid : con->conrelid;
17859 
17860  /* ignore if self-referencing */
17861  if (RelationGetRelid(rel) == foreignrelid)
17862  continue;
17863 
17864  foreignrel = relation_open(foreignrelid, AccessShareLock);
17865 
17866  if (toLogged)
17867  {
17868  if (!RelationIsPermanent(foreignrel))
17869  ereport(ERROR,
17870  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17871  errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17873  RelationGetRelationName(foreignrel)),
17874  errtableconstraint(rel, NameStr(con->conname))));
17875  }
17876  else
17877  {
17878  if (RelationIsPermanent(foreignrel))
17879  ereport(ERROR,
17880  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17881  errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17883  RelationGetRelationName(foreignrel)),
17884  errtableconstraint(rel, NameStr(con->conname))));
17885  }
17886 
17887  relation_close(foreignrel, AccessShareLock);
17888  }
17889  }
17890 
17891  systable_endscan(scan);
17892 
17893  table_close(pg_constraint, AccessShareLock);
17894 
17895  return true;
17896 }
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 4814 of file tablecmds.c.

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

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

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

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

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

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

15428 {
15429  Oid amoid;
15430 
15431  /*
15432  * Look up the access method name and check that it differs from the
15433  * table's current AM. If DEFAULT was specified for a partitioned table
15434  * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15435  */
15436  if (amname != NULL)
15437  amoid = get_table_am_oid(amname, false);
15438  else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15439  amoid = InvalidOid;
15440  else
15442 
15443  /* if it's a match, phase 3 doesn't need to do anything */
15444  if (rel->rd_rel->relam == amoid)
15445  return;
15446 
15447  /* Save info for Phase 3 to do the real work */
15449  tab->newAccessMethod = amoid;
15450  tab->chgAccessMethod = true;
15451 }
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 15551 of file tablecmds.c.

15552 {
15553  Oid tablespaceId;
15554 
15555  /* Check that the tablespace exists */
15556  tablespaceId = get_tablespace_oid(tablespacename, false);
15557 
15558  /* Check permissions except when moving to database's default */
15559  if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
15560  {
15561  AclResult aclresult;
15562 
15563  aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
15564  if (aclresult != ACLCHECK_OK)
15565  aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
15566  }
15567 
15568  /* Save info for Phase 3 to do the real work */
15569  if (OidIsValid(tab->newTableSpace))
15570  ereport(ERROR,
15571  (errcode(ERRCODE_SYNTAX_ERROR),
15572  errmsg("cannot have multiple SET TABLESPACE subcommands")));
15573 
15574  tab->newTableSpace = tablespaceId;
15575 }

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

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

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

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

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

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

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

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

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

19505 {
19506  List *idxes;
19507  List *attachRelIdxs;
19508  Relation *attachrelIdxRels;
19509  IndexInfo **attachInfos;
19510  ListCell *cell;
19511  MemoryContext cxt;
19512  MemoryContext oldcxt;
19513 
19515  "AttachPartitionEnsureIndexes",
19517  oldcxt = MemoryContextSwitchTo(cxt);
19518 
19519  idxes = RelationGetIndexList(rel);
19520  attachRelIdxs = RelationGetIndexList(attachrel);
19521  attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19522  attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19523 
19524  /* Build arrays of all existing indexes and their IndexInfos */
19525  foreach(cell, attachRelIdxs)
19526  {
19527  Oid cldIdxId = lfirst_oid(cell);
19528  int i = foreach_current_index(cell);
19529 
19530  attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19531  attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19532  }
19533 
19534  /*
19535  * If we're attaching a foreign table, we must fail if any of the indexes
19536  * is a constraint index; otherwise, there's nothing to do here. Do this
19537  * before starting work, to avoid wasting the effort of building a few
19538  * non-unique indexes before coming across a unique one.
19539  */
19540  if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19541  {
19542  foreach(cell, idxes)
19543  {
19544  Oid idx = lfirst_oid(cell);
19546 
19547  if (idxRel->rd_index->indisunique ||
19548  idxRel->rd_index->indisprimary)
19549  ereport(ERROR,
19550  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19551  errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19552  RelationGetRelationName(attachrel),
19554  errdetail("Partitioned table \"%s\" contains unique indexes.",
19555  RelationGetRelationName(rel))));
19556  index_close(idxRel, AccessShareLock);
19557  }
19558 
19559  goto out;
19560  }
19561 
19562  /*
19563  * For each index on the partitioned table, find a matching one in the
19564  * partition-to-be; if one is not found, create one.
19565  */
19566  foreach(cell, idxes)
19567  {
19568  Oid idx = lfirst_oid(cell);
19570  IndexInfo *info;
19571  AttrMap *attmap;
19572  bool found = false;
19573  Oid constraintOid;
19574 
19575  /*
19576  * Ignore indexes in the partitioned table other than partitioned
19577  * indexes.
19578  */
19579  if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
19580  {
19581  index_close(idxRel, AccessShareLock);
19582  continue;
19583  }
19584 
19585  /* construct an indexinfo to compare existing indexes against */
19586  info = BuildIndexInfo(idxRel);
19587  attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
19588  RelationGetDescr(rel),
19589  false);
19590  constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
19591 
19592  /*
19593  * Scan the list of existing indexes in the partition-to-be, and mark
19594  * the first matching, valid, unattached one we find, if any, as
19595  * partition of the parent index. If we find one, we're done.
19596  */
19597  for (int i = 0; i < list_length(attachRelIdxs); i++)
19598  {
19599  Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
19600  Oid cldConstrOid = InvalidOid;
19601 
19602  /* does this index have a parent? if so, can't use it */
19603  if (attachrelIdxRels[i]->rd_rel->relispartition)
19604  continue;
19605 
19606  /* If this index is invalid, can't use it */
19607  if (!attachrelIdxRels[i]->rd_index->indisvalid)
19608  continue;
19609 
19610  if (CompareIndexInfo(attachInfos[i], info,
19611  attachrelIdxRels[i]->rd_indcollation,
19612  idxRel->rd_indcollation,
19613  attachrelIdxRels[i]->rd_opfamily,
19614  idxRel->rd_opfamily,
19615  attmap))
19616  {
19617  /*
19618  * If this index is being created in the parent because of a
19619  * constraint, then the child needs to have a constraint also,
19620  * so look for one. If there is no such constraint, this
19621  * index is no good, so keep looking.
19622  */
19623  if (OidIsValid(constraintOid))
19624  {
19625  cldConstrOid =
19627  cldIdxId);
19628  /* no dice */
19629  if (!OidIsValid(cldConstrOid))
19630  continue;
19631 
19632  /* Ensure they're both the same type of constraint */
19633  if (get_constraint_type(constraintOid) !=
19634  get_constraint_type(cldConstrOid))
19635  continue;
19636  }
19637 
19638  /* bingo. */
19639  IndexSetParentIndex(attachrelIdxRels[i], idx);
19640  if (OidIsValid(constraintOid))
19641  ConstraintSetParentConstraint(cldConstrOid, constraintOid,
19642  RelationGetRelid(attachrel));
19643  found = true;
19644 
19646  break;
19647  }
19648  }
19649 
19650  /*
19651  * If no suitable index was found in the partition-to-be, create one
19652  * now.
19653  */
19654  if (!found)
19655  {
19656  IndexStmt *stmt;
19657  Oid conOid;
19658 
19660  idxRel, attmap,
19661  &conOid);
19662 
19663  /*
19664  * If the index is a primary key, mark all columns as NOT NULL if
19665  * they aren't already.
19666  */
19667  if (stmt->primary)
19668  {
19669  MemoryContextSwitchTo(oldcxt);
19670  for (int j = 0; j < info->ii_NumIndexKeyAttrs; j++)
19671  {
19672  AttrNumber childattno;
19673 
19674  childattno = get_attnum(RelationGetRelid(attachrel),
19676  info->ii_IndexAttrNumbers[j],
19677  false));
19678  set_attnotnull(wqueue, attachrel, childattno,
19679  true, AccessExclusiveLock);
19680  }
19681  MemoryContextSwitchTo(cxt);
19682  }
19683 
19685  RelationGetRelid(idxRel),
19686  conOid,
19687  -1,
19688  true, false, false, false, false);
19689  }
19690 
19691  index_close(idxRel, AccessShareLock);
19692  }
19693 
19694 out:
19695  /* Clean up. */
19696  for (int i = 0; i < list_length(attachRelIdxs); i++)
19697  index_close(attachrelIdxRels[i], AccessShareLock);
19698  MemoryContextSwitchTo(oldcxt);
19699  MemoryContextDelete(cxt);
19700 }
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 19176 of file tablecmds.c.

19177 {
19178  /* OK to create inheritance. Rest of the checks performed there */
19179  CreateInheritance(attachrel, rel, true);
19180 
19181  /* Update the pg_class entry. */
19182  StorePartitionBound(attachrel, rel, bound);
19183 
19184  /* Ensure there exists a correct set of indexes in the partition. */
19185  AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19186 
19187  /* and triggers */
19188  CloneRowTriggersToPartition(rel, attachrel);
19189 
19190  /*
19191  * Clone foreign key constraints. Callee is responsible for setting up
19192  * for phase 3 constraint verification.
19193  */
19194  CloneForeignKeyConstraints(wqueue, rel, attachrel);
19195 }
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition: heap.c:3840
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
Definition: tablecmds.c:19708
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:19504
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:10934

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

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

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

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

15250 {
15251  Relation attRelation;
15252  SysScanDesc scan;
15253  ScanKeyData key[1];
15254  HeapTuple attributeTuple;
15255 
15256  attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15257  ScanKeyInit(&key[0],
15258  Anum_pg_attribute_attrelid,
15259  BTEqualStrategyNumber, F_OIDEQ,
15260  ObjectIdGetDatum(relationOid));
15261  scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15262  true, NULL, 1, key);
15263  while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15264  {
15265  Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15266  Datum repl_val[Natts_pg_attribute];
15267  bool repl_null[Natts_pg_attribute];
15268  bool repl_repl[Natts_pg_attribute];
15269  Acl *newAcl;
15270  Datum aclDatum;
15271  bool isNull;
15272  HeapTuple newtuple;
15273 
15274  /* Ignore dropped columns */
15275  if (att->attisdropped)
15276  continue;
15277 
15278  aclDatum = heap_getattr(attributeTuple,
15279  Anum_pg_attribute_attacl,
15280  RelationGetDescr(attRelation),
15281  &isNull);
15282  /* Null ACLs do not require changes */
15283  if (isNull)
15284  continue;
15285 
15286  memset(repl_null, false, sizeof(repl_null));
15287  memset(repl_repl, false, sizeof(repl_repl));
15288 
15289  newAcl = aclnewowner(DatumGetAclP(aclDatum),
15290  oldOwnerId, newOwnerId);
15291  repl_repl[Anum_pg_attribute_attacl - 1] = true;
15292  repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15293 
15294  newtuple = heap_modify_tuple(attributeTuple,
15295  RelationGetDescr(attRelation),
15296  repl_val, repl_null, repl_repl);
15297 
15298  CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15299 
15300  heap_freetuple(newtuple);
15301  }
15302  systable_endscan(scan);
15303  table_close(attRelation, RowExclusiveLock);
15304 }

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

15315 {
15316  Relation depRel;
15317  SysScanDesc scan;
15318  ScanKeyData key[2];
15319  HeapTuple tup;
15320 
15321  /*
15322  * SERIAL sequences are those having an auto dependency on one of the
15323  * table's columns (we don't care *which* column, exactly).
15324  */
15325  depRel = table_open(DependRelationId, AccessShareLock);
15326 
15327  ScanKeyInit(&key[0],
15328  Anum_pg_depend_refclassid,
15329  BTEqualStrategyNumber, F_OIDEQ,
15330  ObjectIdGetDatum(RelationRelationId));
15331  ScanKeyInit(&key[1],
15332  Anum_pg_depend_refobjid,
15333  BTEqualStrategyNumber, F_OIDEQ,
15334  ObjectIdGetDatum(relationOid));
15335  /* we leave refobjsubid unspecified */
15336 
15337  scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15338  NULL, 2, key);
15339 
15340  while (HeapTupleIsValid(tup = systable_getnext(scan)))
15341  {
15342  Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15343  Relation seqRel;
15344 
15345  /* skip dependencies other than auto dependencies on columns */
15346  if (depForm->refobjsubid == 0 ||
15347  depForm->classid != RelationRelationId ||
15348  depForm->objsubid != 0 ||
15349  !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15350  continue;
15351 
15352  /* Use relation_open just in case it's an index */
15353  seqRel = relation_open(depForm->objid, lockmode);
15354 
15355  /* skip non-sequence relations */
15356  if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15357  {
15358  /* No need to keep the lock */
15359  relation_close(seqRel, lockmode);
15360  continue;
15361  }
15362 
15363  /* We don't need to close the sequence while we alter it. */
15364  ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15365 
15366  /* Now we can close it. Keep the lock till end of transaction. */
15367  relation_close(seqRel, NoLock);
15368  }
15369 
15370  systable_endscan(scan);
15371 
15373 }

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

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

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

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

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

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

4348 {
4349  int expected_refcnt;
4350 
4351  expected_refcnt = rel->rd_isnailed ? 2 : 1;
4352  if (rel->rd_refcnt != expected_refcnt)
4353  ereport(ERROR,
4354  (errcode(ERRCODE_OBJECT_IN_USE),
4355  /* translator: first %s is a SQL command, eg ALTER TABLE */
4356  errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4357  stmt, RelationGetRelationName(rel))));
4358 
4359  if (rel->rd_rel->relkind != RELKIND_INDEX &&
4360  rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4362  ereport(ERROR,
4363  (errcode(ERRCODE_OBJECT_IN_USE),
4364  /* translator: first %s is a SQL command, eg ALTER TABLE */
4365  errmsg("cannot %s \"%s\" because it has pending trigger events",
4366  stmt, RelationGetRelationName(rel))));
4367 }
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 9687 of file tablecmds.c.

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

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

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

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

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

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

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

19709 {
19710  Relation pg_trigger;
19711  ScanKeyData key;
19712  SysScanDesc scan;
19713  HeapTuple tuple;
19714  MemoryContext perTupCxt;
19715 
19716  ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19717  F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
19718  pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
19719  scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
19720  true, NULL, 1, &key);
19721 
19723  "clone trig", ALLOCSET_SMALL_SIZES);
19724 
19725  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19726  {
19727  Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
19728  CreateTrigStmt *trigStmt;
19729  Node *qual = NULL;
19730  Datum value;
19731  bool isnull;
19732  List *cols = NIL;
19733  List *trigargs = NIL;
19734  MemoryContext oldcxt;
19735 
19736  /*
19737  * Ignore statement-level triggers; those are not cloned.
19738  */
19739  if (!TRIGGER_FOR_ROW(trigForm->tgtype))
19740  continue;
19741 
19742  /*
19743  * Don't clone internal triggers, because the constraint cloning code
19744  * will.
19745  */
19746  if (trigForm->tgisinternal)
19747  continue;
19748 
19749  /*
19750  * Complain if we find an unexpected trigger type.
19751  */
19752  if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
19753  !TRIGGER_FOR_AFTER(trigForm->tgtype))
19754  elog(ERROR, "unexpected trigger \"%s\" found",
19755  NameStr(trigForm->tgname));
19756 
19757  /* Use short-lived context for CREATE TRIGGER */
19758  oldcxt = MemoryContextSwitchTo(perTupCxt);
19759 
19760  /*
19761  * If there is a WHEN clause, generate a 'cooked' version of it that's
19762  * appropriate for the partition.
19763  */
19764  value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
19765  RelationGetDescr(pg_trigger), &isnull);
19766  if (!isnull)
19767  {
19769  qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
19770  partition, parent);
19771  qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
19772  partition, parent);
19773  }
19774 
19775  /*
19776  * If there is a column list, transform it to a list of column names.
19777  * Note we don't need to map this list in any way ...
19778  */
19779  if (trigForm->tgattr.dim1 > 0)
19780  {
19781  int i;
19782 
19783  for (i = 0; i < trigForm->tgattr.dim1; i++)
19784  {
19785  Form_pg_attribute col;
19786 
19787  col = TupleDescAttr(parent->rd_att,
19788  trigForm->tgattr.values[i] - 1);
19789  cols = lappend(cols,
19790  makeString(pstrdup(NameStr(col->attname))));
19791  }
19792  }
19793 
19794  /* Reconstruct trigger arguments list. */
19795  if (trigForm->tgnargs > 0)
19796  {
19797  char *p;
19798 
19799  value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
19800  RelationGetDescr(pg_trigger), &isnull);
19801  if (isnull)
19802  elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19803  NameStr(trigForm->tgname), RelationGetRelationName(partition));
19804 
19805  p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
19806 
19807  for (int i = 0; i < trigForm->tgnargs; i++)
19808  {
19809  trigargs = lappend(trigargs, makeString(pstrdup(p)));
19810  p += strlen(p) + 1;
19811  }
19812  }
19813 
19814  trigStmt = makeNode(CreateTrigStmt);
19815  trigStmt->replace = false;
19816  trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
19817  trigStmt->trigname = NameStr(trigForm->tgname);
19818  trigStmt->relation = NULL;
19819  trigStmt->funcname = NULL; /* passed separately */
19820  trigStmt->args = trigargs;
19821  trigStmt->row = true;
19822  trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
19823  trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
19824  trigStmt->columns = cols;
19825  trigStmt->whenClause = NULL; /* passed separately */
19826  trigStmt->transitionRels = NIL; /* not supported at present */
19827  trigStmt->deferrable = trigForm->tgdeferrable;
19828  trigStmt->initdeferred = trigForm->tginitdeferred;
19829  trigStmt->constrrel = NULL; /* passed separately */
19830 
19831  CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
19832  trigForm->tgconstrrelid, InvalidOid, InvalidOid,
19833  trigForm->tgfoid, trigForm->oid, qual,
19834  false, true, trigForm->tgenabled);
19835 
19836  MemoryContextSwitchTo(oldcxt);
19837  MemoryContextReset(perTupCxt);
19838  }
19839 
19840  MemoryContextDelete(perTupCxt);
19841 
19842  systable_endscan(scan);
19843  table_close(pg_trigger, RowExclusiveLock);
19844 }
#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 18731 of file tablecmds.c.

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

19043 {
19044  List *existConstraint = list_copy(provenConstraint);
19045  TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19046  int num_check,
19047  i;
19048 
19049  num_check = (constr != NULL) ? constr->num_check : 0;
19050  for (i = 0; i < num_check; i++)
19051  {
19052  Node *cexpr;
19053 
19054  /*
19055  * If this constraint hasn't been fully validated yet, we must ignore
19056  * it here.
19057  */
19058  if (!constr->check[i].ccvalid)
19059  continue;
19060 
19061  cexpr = stringToNode(constr->check[i].ccbin);
19062 
19063  /*
19064  * Run each expression through const-simplification and
19065  * canonicalization. It is necessary, because we will be comparing it
19066  * to similarly-processed partition constraint expressions, and may
19067  * fail to detect valid matches without this.
19068  */
19069  cexpr = eval_const_expressions(NULL, cexpr);
19070  cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
19071 
19072  existConstraint = list_concat(existConstraint,
19073  make_ands_implicit((Expr *) cexpr));
19074  }
19075 
19076  /*
19077  * Try to make the proof. Since we are comparing CHECK constraints, we
19078  * need to use weak implication, i.e., we assume existConstraint is
19079  * not-false and try to prove the same for testConstraint.
19080  *
19081  * Note that predicate_implied_by assumes its first argument is known
19082  * immutable. That should always be true for both NOT NULL and partition
19083  * constraints, so we don't test it here.
19084  */
19085  return predicate_implied_by(testConstraint, existConstraint, true);
19086 }
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 16405 of file tablecmds.c.

16406 {
16409 
16410  if (acon->condeferrable != bcon->condeferrable ||
16411  acon->condeferred != bcon->condeferred ||
16412  strcmp(decompile_conbin(a, tupleDesc),
16413  decompile_conbin(b, tupleDesc)) != 0)
16414  return false;
16415  else
16416  return true;
16417 }
int b
Definition: isn.c:70
int a
Definition: isn.c:69
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:16380

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

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

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

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

References CreateFKCheckTrigger().

Referenced by addFkRecurseReferencing().

◆ CreateInheritance()

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

Definition at line 16311 of file tablecmds.c.

16312 {
16313  Relation catalogRelation;
16314  SysScanDesc scan;
16315  ScanKeyData key;
16316  HeapTuple inheritsTuple;
16317  int32 inhseqno;
16318 
16319  /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16320  catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16321 
16322  /*
16323  * Check for duplicates in the list of parents, and determine the highest
16324  * inhseqno already present; we'll use the next one for the new parent.
16325  * Also, if proposed child is a partition, it cannot already be
16326  * inheriting.
16327  *
16328  * Note: we do not reject the case where the child already inherits from
16329  * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16330  */
16331  ScanKeyInit(&key,
16332  Anum_pg_inherits_inhrelid,
16333  BTEqualStrategyNumber, F_OIDEQ,
16334  ObjectIdGetDatum(RelationGetRelid(child_rel)));
16335  scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16336  true, NULL, 1, &key);
16337 
16338  /* inhseqno sequences start at 1 */
16339  inhseqno = 0;
16340  while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16341  {
16342  Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16343 
16344  if (inh->inhparent == RelationGetRelid(parent_rel))
16345  ereport(ERROR,
16346  (errcode(ERRCODE_DUPLICATE_TABLE),
16347  errmsg("relation \"%s\" would be inherited from more than once",
16348  RelationGetRelationName(parent_rel))));
16349 
16350  if (inh->inhseqno > inhseqno)
16351  inhseqno = inh->inhseqno;
16352  }
16353  systable_endscan(scan);
16354 
16355  /* Match up the columns and bump attinhcount as needed */
16356  MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16357 
16358  /* Match up the constraints and bump coninhcount as needed */
16359  MergeConstraintsIntoExisting(child_rel, parent_rel);
16360 
16361  /*
16362  * OK, it looks valid. Make the catalog entries that show inheritance.
16363  */
16365  RelationGetRelid(parent_rel),
16366  inhseqno + 1,
16367  catalogRelation,
16368  parent_rel->rd_rel->relkind ==
16369  RELKIND_PARTITIONED_TABLE);
16370 
16371  /* Now we're done with pg_inherits */
16372  table_close(catalogRelation, RowExclusiveLock);
16373 }
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:16434
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3514
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:16564

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

21220 {
21221  CreateStmt *createStmt;
21222  TableLikeClause *tlc;
21223  PlannedStmt *wrapper;
21224 
21225  createStmt = makeNode(CreateStmt);
21226  createStmt->relation = newPartName;
21227  createStmt->tableElts = NIL;
21228  createStmt->inhRelations = NIL;
21229  createStmt->constraints = NIL;
21230  createStmt->options = NIL;
21231  createStmt->oncommit = ONCOMMIT_NOOP;
21232  createStmt->tablespacename = NULL;
21233  createStmt->if_not_exists = false;
21234 
21235  tlc = makeNode(TableLikeClause);
21236  tlc->relation = modelRelName;
21237 
21238  /*
21239  * Indexes will be inherited on "attach new partitions" stage, after data
21240  * moving.
21241  */
21243  tlc->relationOid = InvalidOid;
21244  createStmt->tableElts = lappend(createStmt->tableElts, tlc);
21245 
21246  /* Need to make a wrapper PlannedStmt. */
21247  wrapper = makeNode(PlannedStmt);
21248  wrapper->commandType = CMD_UTILITY;
21249  wrapper->canSetTag = false;
21250  wrapper->utilityStmt = (Node *) createStmt;
21251  wrapper->stmt_location = context->pstmt->stmt_location;
21252  wrapper->stmt_len = context->pstmt->stmt_len;
21253 
21254  ProcessUtility(wrapper,
21255  context->queryString,
21256  false,
21258  NULL,
21259  NULL,
21260  None_Receiver,
21261  NULL);
21262 }
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 20977 of file tablecmds.c.

20978 {
20980 
20982  pc->partRel = partRel;
20983 
20984  /*
20985  * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
20986  * don't bother using it.
20987  */
20988  pc->bistate = GetBulkInsertState();
20989 
20990  /* Create tuple slot for new partition. */
20994 
20995  return pc;
20996 }
TupleTableSlot * dstslot
Definition: tablecmds.c:20968
BulkInsertState bistate
Definition: tablecmds.c:20967

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

16381 {
16382  Form_pg_constraint con;
16383  bool isnull;
16384  Datum attr;
16385  Datum expr;
16386 
16387  con = (Form_pg_constraint) GETSTRUCT(contup);
16388  attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16389  if (isnull)
16390  elog(ERROR, "null conbin for constraint %u", con->oid);
16391 
16392  expr = DirectFunctionCall2(pg_get_expr, attr,
16393  ObjectIdGetDatum(con->conrelid));
16394  return TextDatumGetCString(expr);
16395 }
#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 701 of file tablecmds.c.

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

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

20311 {
20312  List *constraintExpr;
20313 
20314  constraintExpr = RelationGetPartitionQual(partRel);
20315  constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20316 
20317  /*
20318  * Avoid adding a new constraint if the needed constraint is implied by an
20319  * existing constraint
20320  */
20321  if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20322  {
20323  AlteredTableInfo *tab;
20324  Constraint *n;
20325 
20326  tab = ATGetQueueEntry(wqueue, partRel);
20327 
20328  /* Add constraint on partition, equivalent to the partition constraint */
20329  n = makeNode(Constraint);
20330  n->contype = CONSTR_CHECK;
20331  n->conname = NULL;
20332  n->location = -1;
20333  n->is_no_inherit = false;
20334  n->raw_expr = NULL;
20335  n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20336  n->initially_valid = true;
20337  n->skip_validation = true;
20338  /* It's a re-add, since it nominally already exists */
20339  ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20340  true, false, true, ShareUpdateExclusiveLock);
20341  }
20342 }
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:18989

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

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

17131 {
17132  Relation catalogRelation;
17133  SysScanDesc scan;
17134  ScanKeyData key[3];
17135  HeapTuple depTuple;
17136 
17137  catalogRelation = table_open(DependRelationId, RowExclusiveLock);
17138 
17139  ScanKeyInit(&key[0],
17140  Anum_pg_depend_classid,
17141  BTEqualStrategyNumber, F_OIDEQ,
17142  ObjectIdGetDatum(RelationRelationId));
17143  ScanKeyInit(&key[1],
17144  Anum_pg_depend_objid,
17145  BTEqualStrategyNumber, F_OIDEQ,
17146  ObjectIdGetDatum(relid));
17147  ScanKeyInit(&key[2],
17148  Anum_pg_depend_objsubid,
17149  BTEqualStrategyNumber, F_INT4EQ,
17150  Int32GetDatum(0));
17151 
17152  scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
17153  NULL, 3, key);
17154 
17155  while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
17156  {
17157  Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
17158 
17159  if (dep->refclassid == refclassid &&
17160  dep->refobjid == refobjid &&
17161  dep->refobjsubid == 0 &&
17162  dep->deptype == deptype)
17163  CatalogTupleDelete(catalogRelation, &depTuple->t_self);
17164  }
17165 
17166  systable_endscan(scan);
17167  table_close(catalogRelation, RowExclusiveLock);
17168 }

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

20352 {
20353  ScanKeyData skey;
20354  SysScanDesc scan;
20355  HeapTuple trigtup;
20356  Relation tgrel;
20357  ObjectAddresses *objects;
20358 
20359  objects = new_object_addresses();
20360 
20361  /*
20362  * Scan pg_trigger to search for all triggers on this rel.
20363  */
20364  ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20365  F_OIDEQ, ObjectIdGetDatum(partitionId));
20366  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20367  scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20368  true, NULL, 1, &skey);
20369  while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20370  {
20371  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20372  ObjectAddress trig;
20373 
20374  /* Ignore triggers that weren't cloned */
20375  if (!OidIsValid(pg_trigger->tgparentid))
20376  continue;
20377 
20378  /*
20379  * Ignore internal triggers that are implementation objects of foreign
20380  * keys, because these will be detached when the foreign keys
20381  * themselves are.
20382  */
20383  if (OidIsValid(pg_trigger->tgconstrrelid))
20384  continue;
20385 
20386  /*
20387  * This is ugly, but necessary: remove the dependency markings on the
20388  * trigger so that it can be removed.
20389  */
20390  deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20391  TriggerRelationId,
20393  deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20394  RelationRelationId,
20396 
20397  /* remember this trigger to remove it below */
20398  ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20399  add_exact_object_address(&trig, objects);
20400  }
20401 
20402  /* make the dependency removal visible to the deletion below */
20405 
20406  /* done */
20407  free_object_addresses(objects);
20408  systable_endscan(scan);
20409  table_close(tgrel, RowExclusiveLock);
20410 }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

References i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 12496 of file tablecmds.c.

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

20890 {
20891  char cmethod;
20892 
20893  if (compression == NULL || strcmp(compression, "default") == 0)
20894  return InvalidCompressionMethod;
20895 
20896  /*
20897  * To specify a nondefault method, the column data type must be toastable.
20898  * Note this says nothing about whether the column's attstorage setting
20899  * permits compression; we intentionally allow attstorage and
20900  * attcompression to be independent. But with a non-toastable type,
20901  * attstorage could not be set to a value that would permit compression.
20902  *
20903  * We don't actually need to enforce this, since nothing bad would happen
20904  * if attcompression were non-default; it would never be consulted. But
20905  * it seems more user-friendly to complain about a certainly-useless
20906  * attempt to set the property.
20907  */
20908  if (!TypeIsToastable(atttypid))
20909  ereport(ERROR,
20910  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20911  errmsg("column data type %s does not support compression",
20912  format_type_be(atttypid))));
20913 
20914  cmethod = CompressionNameToMethod(compression);
20915  if (!CompressionMethodIsValid(cmethod))
20916  ereport(ERROR,
20917  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20918  errmsg("invalid compression method \"%s\"", compression)));
20919 
20920  return cmethod;
20921 }
#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 20927 of file tablecmds.c.

20928 {
20929  char cstorage = 0;
20930 
20931  if (pg_strcasecmp(storagemode, "plain") == 0)
20932  cstorage = TYPSTORAGE_PLAIN;
20933  else if (pg_strcasecmp(storagemode, "external") == 0)
20934  cstorage = TYPSTORAGE_EXTERNAL;
20935  else if (pg_strcasecmp(storagemode, "extended") == 0)
20936  cstorage = TYPSTORAGE_EXTENDED;
20937  else if (pg_strcasecmp(storagemode, "main") == 0)
20938  cstorage = TYPSTORAGE_MAIN;
20939  else if (pg_strcasecmp(storagemode, "default") == 0)
20940  cstorage = get_typstorage(atttypid);
20941  else
20942  ereport(ERROR,
20943  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20944  errmsg("invalid storage type \"%s\"",
20945  storagemode)));
20946 
20947  /*
20948  * safety check: do not allow toasted storage modes unless column datatype
20949  * is TOAST-aware.
20950  */
20951  if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20952  ereport(ERROR,
20953  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20954  errmsg("column data type %s can only have storage PLAIN",
20955  format_type_be(atttypid))));
20956 
20957  return cstorage;
20958 }
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 11586 of file tablecmds.c.

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

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

20788 {
20789  Relation pg_constraint;
20790  HeapTuple tuple;
20791  SysScanDesc scan;
20792  ScanKeyData key[2];
20793  List *constraints = NIL;
20794 
20795  /*
20796  * If no indexes, or no columns are referenceable by FKs, we can avoid the
20797  * scan.
20798  */
20799  if (RelationGetIndexList(partition) == NIL ||
20802  return NIL;
20803 
20804  /* Search for constraints referencing this table */
20805  pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
20806  ScanKeyInit(&key[0],
20807  Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
20808  F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
20809  ScanKeyInit(&key[1],
20810  Anum_pg_constraint_contype, BTEqualStrategyNumber,
20811  F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
20812 
20813  /* XXX This is a seqscan, as we don't have a usable index */
20814  scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
20815  while ((tuple = systable_getnext(scan)) != NULL)
20816  {
20817  Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20818 
20819  /*
20820  * We only need to process constraints that are part of larger ones.
20821  */
20822  if (!OidIsValid(constrForm->conparentid))
20823  continue;
20824 
20825  constraints = lappend_oid(constraints, constrForm->oid);
20826  }
20827 
20828  systable_endscan(scan);
20829  table_close(pg_constraint, AccessShareLock);
20830 
20831  return constraints;
20832 }
#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 16078 of file tablecmds.c.

16079 {
16080  SMgrRelation dstrel;
16081 
16082  /*
16083  * Since we copy the file directly without looking at the shared buffers,
16084  * we'd better first flush out any pages of the source relation that are
16085  * in shared buffers. We assume no new changes will be made while we are
16086  * holding exclusive lock on the rel.
16087  */
16088  FlushRelationBuffers(rel);
16089 
16090  /*
16091  * Create and copy all forks of the relation, and schedule unlinking of
16092  * old physical files.
16093  *
16094  * NOTE: any conflict in relfilenumber value will be caught in
16095  * RelationCreateStorage().
16096  */
16097  dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
16098 
16099  /* copy main fork */
16101  rel->rd_rel->relpersistence);
16102 
16103  /* copy those extra forks that exist */
16104  for (ForkNumber forkNum = MAIN_FORKNUM + 1;
16105  forkNum <= MAX_FORKNUM; forkNum++)
16106  {
16107  if (smgrexists(RelationGetSmgr(rel), forkNum))
16108  {
16109  smgrcreate(dstrel, forkNum, false);
16110 
16111  /*
16112  * WAL log creation if the relation is persistent, or this is the
16113  * init fork of an unlogged relation.
16114  */
16115  if (RelationIsPermanent(rel) ||
16116  (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
16117  forkNum == INIT_FORKNUM))
16118  log_smgrcreate(&newrlocator, forkNum);
16119  RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
16120  rel->rd_rel->relpersistence);
16121  }
16122  }
16123 
16124  /* drop old relation, and close new one */
16125  RelationDropStorage(rel);
16126  smgrclose(dstrel);
16127 }
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 16804 of file tablecmds.c.

16805 {
16806  Relation catalogRelation;
16807  SysScanDesc scan;
16808  ScanKeyData key;
16809  HeapTuple inheritsTuple;
16810  bool found = false;
16811 
16812  Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16813 
16814  /*
16815  * Find pg_inherits entries by inhparent. (We need to scan them all in
16816  * order to verify that no other partition is pending detach.)
16817  */
16818  catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16819  ScanKeyInit(&key,
16820  Anum_pg_inherits_inhparent,
16821  BTEqualStrategyNumber, F_OIDEQ,
16822  ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16823  scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16824  true, NULL, 1, &key);
16825 
16826  while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16827  {
16828  Form_pg_inherits inhForm;
16829 
16830  inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16831  if (inhForm->inhdetachpending)
16832  ereport(ERROR,
16833  errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16834  errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16835  get_rel_name(inhForm->inhrelid),
16836  get_namespace_name(parent_rel->rd_rel->relnamespace),
16837  RelationGetRelationName(parent_rel)),
16838  errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16839 
16840  if (inhForm->inhrelid == RelationGetRelid(child_rel))
16841  {
16842  HeapTuple newtup;
16843 
16844  newtup = heap_copytuple(inheritsTuple);
16845  ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16846 
16847  CatalogTupleUpdate(catalogRelation,
16848  &inheritsTuple->t_self,
16849  newtup);
16850  found = true;
16851  heap_freetuple(newtup);
16852  /* keep looking, to ensure we catch others pending detach */
16853  }
16854  }
16855 
16856  /* Done */
16857  systable_endscan(scan);
16858  table_close(catalogRelation, RowExclusiveLock);
16859 
16860  if (!found)
16861  ereport(ERROR,
16863  errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16864  RelationGetRelationName(child_rel),
16865  RelationGetRelationName(parent_rel))));
16866 }
#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 2492 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

21404 {
21405  CommandId mycid;
21406 
21407  /* The FSM is empty, so don't bother using it. */
21408  int ti_options = TABLE_INSERT_SKIP_FSM;
21409  ListCell *listptr;
21410  BulkInsertState bistate; /* state of bulk inserts for partition */
21411  TupleTableSlot *dstslot;
21412 
21413  mycid = GetCurrentCommandId(true);
21414 
21415  /* Prepare a BulkInsertState for table_tuple_insert. */
21416  bistate = GetBulkInsertState();
21417 
21418  /* Create necessary tuple slot. */
21419  dstslot = MakeSingleTupleTableSlot(RelationGetDescr(newPartRel),
21420  table_slot_callbacks(newPartRel));
21421  ExecStoreAllNullTuple(dstslot);
21422 
21423  foreach(listptr, mergingPartitionsList)
21424  {
21425  Relation mergingPartition = (Relation) lfirst(listptr);
21426  TupleTableSlot *srcslot;
21427  TupleConversionMap *tuple_map;
21428  TableScanDesc scan;
21429  Snapshot snapshot;
21430 
21431  /* Create tuple slot for new partition. */
21432  srcslot = MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition),
21433  table_slot_callbacks(mergingPartition));
21434 
21435  /*
21436  * Map computing for moving attributes of merged partition to new
21437  * partition.
21438  */
21439  tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
21440  RelationGetDescr(newPartRel));
21441 
21442  /* Scan through the rows. */
21443  snapshot = RegisterSnapshot(GetLatestSnapshot());
21444  scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
21445 
21446  while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
21447  {
21448  TupleTableSlot *insertslot;
21449 
21450  /* Extract data from old tuple. */
21451  slot_getallattrs(srcslot);
21452 
21453  if (tuple_map)
21454  {
21455  /* Need to use map to copy attributes. */
21456  insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
21457  }
21458  else
21459  {
21460  /* Copy attributes directly. */
21461  insertslot = dstslot;
21462 
21463  ExecClearTuple(insertslot);
21464 
21465  memcpy(insertslot->tts_values, srcslot->tts_values,
21466  sizeof(Datum) * srcslot->tts_nvalid);
21467  memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
21468  sizeof(bool) * srcslot->tts_nvalid);
21469 
21470  ExecStoreVirtualTuple(insertslot);
21471  }
21472 
21473  /* Write the tuple out to the new relation. */
21474  table_tuple_insert(newPartRel, insertslot, mycid,
21475  ti_options, bistate);
21476 
21478  }
21479 
21480  table_endscan(scan);
21481  UnregisterSnapshot(snapshot);
21482 
21483  if (tuple_map)
21484  free_conversion_map(tuple_map);
21485 
21487  }
21488 
21490  FreeBulkInsertState(bistate);
21491 
21492  table_finish_bulk_insert(newPartRel, ti_options);
21493 }
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 21022 of file tablecmds.c.

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

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

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

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

18991 {
18992  List *existConstraint = NIL;
18993  TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18994  int i;
18995 
18996  if (constr && constr->has_not_null)
18997  {
18998  int natts = scanrel->rd_att->natts;
18999 
19000  for (i = 1; i <= natts; i++)
19001  {
19002  Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
19003 
19004  if (att->attnotnull && !att->attisdropped)
19005  {
19006  NullTest *ntest = makeNode(NullTest);
19007 
19008  ntest->arg = (Expr *) makeVar(1,
19009  i,
19010  att->atttypid,
19011  att->atttypmod,
19012  att->attcollation,
19013  0);
19014  ntest->nulltesttype = IS_NOT_NULL;
19015 
19016  /*
19017  * argisrow=false is correct even for a composite column,
19018  * because attnotnull does not represent a SQL-spec IS NOT
19019  * NULL test in such a case, just IS DISTINCT FROM NULL.
19020  */
19021  ntest->argisrow = false;
19022  ntest->location = -1;
19023  existConstraint = lappend(existConstraint, ntest);
19024  }
19025  }
19026  }
19027 
19028  return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
19029 }

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

18267 {
18268  ListCell *l;
18269  List *oids_to_truncate = NIL;
18270  List *oids_to_drop = NIL;
18271 
18272  foreach(l, on_commits)
18273  {
18274  OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18275 
18276  /* Ignore entry if already dropped in this xact */
18278  continue;
18279 
18280  switch (oc->oncommit)
18281  {
18282  case ONCOMMIT_NOOP:
18284  /* Do nothing (there shouldn't be such entries, actually) */
18285  break;
18286  case ONCOMMIT_DELETE_ROWS:
18287 
18288  /*
18289  * If this transaction hasn't accessed any temporary
18290  * relations, we can skip truncating ON COMMIT DELETE ROWS
18291  * tables, as they must still be empty.
18292  */
18294  oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18295  break;
18296  case ONCOMMIT_DROP:
18297  oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18298  break;
18299  }
18300  }
18301 
18302  /*
18303  * Truncate relations before dropping so that all dependencies between
18304  * relations are removed after they are worked on. Doing it like this
18305  * might be a waste as it is possible that a relation being truncated will
18306  * be dropped anyway due to its parent being dropped, but this makes the
18307  * code more robust because of not having to re-check that the relation
18308  * exists at truncation time.
18309  */
18310  if (oids_to_truncate != NIL)
18311  heap_truncate(oids_to_truncate);
18312 
18313  if (oids_to_drop != NIL)
18314  {
18315  ObjectAddresses *targetObjects = new_object_addresses();
18316 
18317  foreach(l, oids_to_drop)
18318  {
18319  ObjectAddress object;
18320 
18321  object.classId = RelationRelationId;
18322  object.objectId = lfirst_oid(l);
18323  object.objectSubId = 0;
18324 
18325  Assert(!object_address_present(&object, targetObjects));
18326 
18327  add_exact_object_address(&object, targetObjects);
18328  }
18329 
18330  /*
18331  * Object deletion might involve toast table access (to clean up
18332  * toasted catalog entries), so ensure we have a valid snapshot.
18333  */
18335 
18336  /*
18337  * Since this is an automatic drop, rather than one directly initiated
18338  * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18339  */
18340  performMultipleDeletions(targetObjects, DROP_CASCADE,
18342 
18344 
18345 #ifdef USE_ASSERT_CHECKING
18346 
18347  /*
18348  * Note that table deletion will call remove_on_commit_action, so the
18349  * entry should get marked as deleted.
18350  */
18351  foreach(l, on_commits)
18352  {
18353  OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18354 
18355  if (oc->oncommit != ONCOMMIT_DROP)
18356  continue;
18357 
18359  }
18360 #endif
18361  }
18362 }
#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:116
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 19099 of file tablecmds.c.

19102 {
19103  /*
19104  * Based on the table's existing constraints, determine whether or not we
19105  * may skip scanning the table.
19106  */
19107  if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
19108  {
19109  if (!validate_default)
19110  ereport(DEBUG1,
19111  (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19112  RelationGetRelationName(scanrel))));
19113  else
19114  ereport(DEBUG1,
19115  (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19116  RelationGetRelationName(scanrel))));
19117  return;
19118  }
19119 
19120  /*
19121  * Constraints proved insufficient. For plain relations, queue a
19122  * validation item now; for partitioned tables, recurse to process each
19123  * partition.
19124  */
19125  if (scanrel->rd_rel->relkind == RELKIND_RELATION)
19126  {
19127  AlteredTableInfo *tab;
19128 
19129  /* Grab a work queue entry. */
19130  tab = ATGetQueueEntry(wqueue, scanrel);
19131  Assert(tab->partition_constraint == NULL);
19132  tab->partition_constraint = (Expr *) linitial(partConstraint);
19133  tab->validate_default = validate_default;
19134  }
19135  else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19136  {
19137  PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
19138  int i;
19139 
19140  for (i = 0; i < partdesc->nparts; i++)
19141  {
19142  Relation part_rel;
19143  List *thisPartConstraint;
19144 
19145  /*
19146  * This is the minimum lock we need to prevent deadlocks.
19147  */
19148  part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
19149 
19150  /*
19151  * Adjust the constraint for scanrel so that it matches this
19152  * partition's attribute numbers.
19153  */
19154  thisPartConstraint =
19155  map_partition_varattnos(partConstraint, 1,
19156  part_rel, scanrel);
19157 
19158  QueuePartitionConstraintValidation(wqueue, part_rel,
19159  thisPartConstraint,
19160  validate_default);
19161  table_close(part_rel, NoLock); /* keep lock till commit */
19162  }
19163  }
19164 }

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

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

20426 {
20428  Form_pg_class classform;
20429  HeapTuple tuple;
20430 
20431  state = (struct AttachIndexCallbackState *) arg;
20432 
20433  if (!state->lockedParentTbl)
20434  {
20435  LockRelationOid(state->parentTblOid, AccessShareLock);
20436  state->lockedParentTbl = true;
20437  }
20438 
20439  /*
20440  * If we previously locked some other heap, and the name we're looking up
20441  * no longer refers to an index on that relation, release the now-useless
20442  * lock. XXX maybe we should do *after* we verify whether the index does
20443  * not actually belong to the same relation ...
20444  */
20445  if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20446  {
20447  UnlockRelationOid(state->partitionOid, AccessShareLock);
20448  state->partitionOid = InvalidOid;
20449  }
20450 
20451  /* Didn't find a relation, so no need for locking or permission checks. */
20452  if (!OidIsValid(relOid))
20453  return;
20454 
20455  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20456  if (!HeapTupleIsValid(tuple))
20457  return; /* concurrently dropped, so nothing to do */
20458  classform = (Form_pg_class) GETSTRUCT(tuple);
20459  if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20460  classform->relkind != RELKIND_INDEX)
20461  ereport(ERROR,
20462  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20463  errmsg("\"%s\" is not an index", rv->relname)));
20464  ReleaseSysCache(tuple);
20465 
20466  /*
20467  * Since we need only examine the heap's tupledesc, an access share lock
20468  * on it (preventing any DDL) is sufficient.
20469  */
20470  state->partitionOid = IndexGetRelation(relOid, false);
20471  LockRelationOid(state->partitionOid, AccessShareLock);
20472 }
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 1649 of file tablecmds.c.

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

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

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

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

18478 {
18479  HeapTuple tuple;
18480 
18481  /* Nothing to do if the relation was not found. */
18482  if (!OidIsValid(relId))
18483  return;
18484 
18485  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18486  if (!HeapTupleIsValid(tuple)) /* should not happen */
18487  elog(ERROR, "cache lookup failed for relation %u", relId);
18488 
18489  truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
18490  truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
18491 
18492  ReleaseSysCache(tuple);
18493 }

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

18442 {
18443  char relkind;
18444  AclResult aclresult;
18445 
18446  /* Nothing to do if the relation was not found. */
18447  if (!OidIsValid(relId))
18448  return;
18449 
18450  /*
18451  * If the relation does exist, check whether it's an index. But note that
18452  * the relation might have been dropped between the time we did the name
18453  * lookup and now. In that case, there's nothing to do.
18454  */
18455  relkind = get_rel_relkind(relId);
18456  if (!relkind)
18457  return;
18458  if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18459  relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18460  ereport(ERROR,
18461  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18462  errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18463 
18464  /* Check permissions */
18465  aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18466  if (aclresult != ACLCHECK_OK)
18467  aclcheck_error(aclresult,
18469  relation->relname);
18470 }
#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 18500 of file tablecmds.c.

18502 {
18503  HeapTuple tuple;
18504 
18505  /* Nothing to do if the relation was not found. */
18506  if (!OidIsValid(relId))
18507  return;
18508 
18509  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18510  if (!HeapTupleIsValid(tuple)) /* should not happen */
18511  elog(ERROR, "cache lookup failed for relation %u", relId);
18512 
18513  if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18515  relation->relname);
18516 
18517  if (!allowSystemTableMods &&
18518  IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18519  ereport(ERROR,
18520  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18521  errmsg("permission denied: \"%s\" is a system catalog",
18522  relation->relname)));
18523 
18524  ReleaseSysCache(tuple);
18525 }

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

14781 {
14782  CommentStmt *cmd;
14783  char *comment_str;
14784  AlterTableCmd *newcmd;
14785 
14786  /* Look for comment for object wanted, and leave if none */
14787  comment_str = GetComment(objid, ConstraintRelationId, 0);
14788  if (comment_str == NULL)
14789  return;
14790 
14791  /* Build CommentStmt node, copying all input data for safety */
14792  cmd = makeNode(CommentStmt);
14793  if (rel)
14794  {
14796  cmd->object = (Node *)
14799  makeString(pstrdup(conname)));
14800  }
14801  else
14802  {
14804  cmd->object = (Node *)
14806  makeString(pstrdup(conname)));
14807  }
14808  cmd->comment = comment_str;
14809 
14810  /* Append it to list of commands */
14811  newcmd = makeNode(AlterTableCmd);
14812  newcmd->subtype = AT_ReAddComment;
14813  newcmd->def = (Node *) cmd;
14814  tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14815 }
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 20640 of file tablecmds.c.

20641 {
20642  Oid existingIdx;
20643 
20644  existingIdx = index_get_partition(partitionTbl,
20645  RelationGetRelid(parentIdx));
20646  if (OidIsValid(existingIdx))
20647  ereport(ERROR,
20648  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20649  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20650  RelationGetRelationName(partIdx),
20651  RelationGetRelationName(parentIdx)),
20652  errdetail("Another index is already attached for partition \"%s\".",
20653  RelationGetRelationName(partitionTbl))));
20654 }

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

18208 {
18209  OnCommitItem *oc;
18210  MemoryContext oldcxt;
18211 
18212  /*
18213  * We needn't bother registering the relation unless there is an ON COMMIT
18214  * action we need to take.
18215  */
18217  return;
18218 
18220 
18221  oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18222  oc->relid = relid;
18223  oc->oncommit = action;
18226 
18227  /*
18228  * We use lcons() here so that ON COMMIT actions are processed in reverse
18229  * order of registration. That might not be essential but it seems
18230  * reasonable.
18231  */
18232  on_commits = lcons(oc, on_commits);
18233 
18234  MemoryContextSwitchTo(oldcxt);
18235 }
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 17367 of file tablecmds.c.

17369 {
17370  Relation pg_index;
17371  Relation pg_class;
17372  HeapTuple pg_class_tuple;
17373  HeapTuple pg_index_tuple;
17374  Form_pg_class pg_class_form;
17375  Form_pg_index pg_index_form;
17376  ListCell *index;
17377 
17378  /*
17379  * Check whether relreplident has changed, and update it if so.
17380  */
17381  pg_class = table_open(RelationRelationId, RowExclusiveLock);
17382  pg_class_tuple = SearchSysCacheCopy1(RELOID,
17384  if (!HeapTupleIsValid(pg_class_tuple))
17385  elog(ERROR, "cache lookup failed for relation \"%s\"",
17387  pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17388  if (pg_class_form->relreplident != ri_type)
17389  {
17390  pg_class_form->relreplident = ri_type;
17391  CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17392  }
17393  table_close(pg_class, RowExclusiveLock);
17394  heap_freetuple(pg_class_tuple);
17395 
17396  /*
17397  * Update the per-index indisreplident flags correctly.
17398  */
17399  pg_index = table_open(IndexRelationId, RowExclusiveLock);
17400  foreach(index, RelationGetIndexList(rel))
17401  {
17402  Oid thisIndexOid = lfirst_oid(index);
17403  bool dirty = false;
17404 
17405  pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17406  ObjectIdGetDatum(thisIndexOid));
17407  if (!HeapTupleIsValid(pg_index_tuple))
17408  elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17409  pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17410 
17411  if (thisIndexOid == indexOid)
17412  {
17413  /* Set the bit if not already set. */
17414  if (!pg_index_form->indisreplident)
17415  {
17416  dirty = true;
17417  pg_index_form->indisreplident = true;
17418  }
17419  }
17420  else
17421  {
17422  /* Unset the bit if set. */
17423  if (pg_index_form->indisreplident)
17424  {
17425  dirty = true;
17426  pg_index_form->indisreplident = false;
17427  }
17428  }
17429 
17430  if (dirty)
17431  {
17432  CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17433  InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17434  InvalidOid, is_internal);
17435 
17436  /*
17437  * Invalidate the relcache for the table, so that after we commit
17438  * all sessions will refresh the table's replica identity index
17439  * before attempting any UPDATE or DELETE on the table. (If we
17440  * changed the table's pg_class row above, then a relcache inval
17441  * is already queued due to that; but we might not have.)
17442  */
17444  }
17445  heap_freetuple(pg_index_tuple);
17446  }
17447 
17448  table_close(pg_index, RowExclusiveLock);
17449 }
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 14027 of file tablecmds.c.

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

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

14254 {
14255  if (!get_index_isclustered(indoid))
14256  return;
14257 
14258  if (tab->clusterOnIndex)
14259  elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14260 
14261  tab->clusterOnIndex = get_rel_name(indoid);
14262 }
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 14269 of file tablecmds.c.

14270 {
14271  /*
14272  * This de-duplication check is critical for two independent reasons: we
14273  * mustn't try to recreate the same constraint twice, and if a constraint
14274  * depends on more than one column whose type is to be altered, we must
14275  * capture its definition string before applying any of the column type
14276  * changes. ruleutils.c will get confused if we ask again later.
14277  */
14278  if (!list_member_oid(tab->changedConstraintOids, conoid))
14279  {
14280  /* OK, capture the constraint's existing definition string */
14281  char *defstring = pg_get_constraintdef_command(conoid);
14282  Oid indoid;
14283 
14285  conoid);
14287  defstring);
14288 
14289  /*
14290  * For the index of a constraint, if any, remember if it is used for
14291  * the table's replica identity or if it is a clustered index, so that
14292  * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14293  * those properties.
14294  */
14295  indoid = get_constraint_index(conoid);
14296  if (OidIsValid(indoid))
14297  {
14299  RememberClusterOnForRebuilding(indoid, tab);
14300  }
14301  }
14302 }
char * pg_get_constraintdef_command(Oid constraintId)
Definition: ruleutils.c:2168
static void RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:14238
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:14253

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

14310 {
14311  /*
14312  * This de-duplication check is critical for two independent reasons: we
14313  * mustn't try to recreate the same index twice, and if an index depends
14314  * on more than one column whose type is to be altered, we must capture
14315  * its definition string before applying any of the column type changes.
14316  * ruleutils.c will get confused if we ask again later.
14317  */
14318  if (!list_member_oid(tab->changedIndexOids, indoid))
14319  {
14320  /*
14321  * Before adding it as an index-to-rebuild, we'd better see if it
14322  * belongs to a constraint, and if so rebuild the constraint instead.
14323  * Typically this check fails, because constraint indexes normally
14324  * have only dependencies on their constraint. But it's possible for
14325  * such an index to also have direct dependencies on table columns,
14326  * for example with a partial exclusion constraint.
14327  */
14328  Oid conoid = get_index_constraint(indoid);
14329 
14330  if (OidIsValid(conoid))
14331  {
14332  RememberConstraintForRebuilding(conoid, tab);
14333  }
14334  else
14335  {
14336  /* OK, capture the index's existing definition string */
14337  char *defstring = pg_get_indexdef_string(indoid);
14338 
14340  indoid);
14342  defstring);
14343 
14344  /*
14345  * Remember if this index is used for the table's replica identity
14346  * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14347  * can queue up commands necessary to restore those properties.
14348  */
14350  RememberClusterOnForRebuilding(indoid, tab);
14351  }
14352  }
14353 }
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 14238 of file tablecmds.c.

14239 {
14240  if (!get_index_isreplident(indoid))
14241  return;
14242 
14243  if (tab->replicaIdentityIndex)
14244  elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14245 
14246  tab->replicaIdentityIndex = get_rel_name(indoid);
14247 }
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 14360 of file tablecmds.c.

14361 {
14362  /*
14363  * This de-duplication check is critical for two independent reasons: we
14364  * mustn't try to recreate the same statistics object twice, and if the
14365  * statistics object depends on more than one column whose type is to be
14366  * altered, we must capture its definition string before applying any of
14367  * the type changes. ruleutils.c will get confused if we ask again later.
14368  */
14369  if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14370  {
14371  /* OK, capture the statistics object's existing definition string */
14372  char *defstring = pg_get_statisticsobjdef_string(stxoid);
14373 
14375  stxoid);
14377  defstring);
14378  }
14379 }
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 18243 of file tablecmds.c.

18244 {
18245  ListCell *l;
18246 
18247  foreach(l, on_commits)
18248  {
18249  OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18250 
18251  if (oc->relid == relid)
18252  {
18254  break;
18255  }
18256  }
18257 }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Referenced by MergeChildAttribute(), and MergeInheritedAttribute().

◆ StoreCatalogInheritance()

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

Definition at line 3470 of file tablecmds.c.

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

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

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

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

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

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

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

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

18674 {
18675  PartitionSpec *newspec;
18676  ParseState *pstate;
18677  ParseNamespaceItem *nsitem;
18678  ListCell *l;
18679 
18680  newspec = makeNode(PartitionSpec);
18681 
18682  newspec->strategy = partspec->strategy;
18683  newspec->partParams = NIL;
18684  newspec->location = partspec->location;
18685 
18686  /* Check valid number of columns for strategy */
18687  if (partspec->strategy == PARTITION_STRATEGY_LIST &&
18688  list_length(partspec->partParams) != 1)
18689  ereport(ERROR,
18690  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18691  errmsg("cannot use \"list\" partition strategy with more than one column")));
18692 
18693  /*
18694  * Create a dummy ParseState and insert the target relation as its sole
18695  * rangetable entry. We need a ParseState for transformExpr.
18696  */
18697  pstate = make_parsestate(NULL);
18698  nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
18699  NULL, false, true);
18700  addNSItemToQuery(pstate, nsitem, true, true, true);
18701 
18702  /* take care of any partition expressions */
18703  foreach(l, partspec->partParams)
18704  {
18706 
18707  if (pelem->expr)
18708  {
18709  /* Copy, to avoid scribbling on the input */
18710  pelem = copyObject(pelem);
18711 
18712  /* Now do parse transformation of the expression */
18713  pelem->expr = transformExpr(pstate, pelem->expr,
18715 
18716  /* we have to fix its collations too */
18717  assign_expr_collations(pstate, pelem->expr);
18718  }
18719 
18720  newspec->partParams = lappend(newspec->partParams, pelem);
18721  }
18722 
18723  return newspec;
18724 }
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 2385 of file tablecmds.c.

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

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

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

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

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

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

14852 {
14853  HeapTuple tup;
14854  Datum adatum;
14855  ArrayType *arr;
14856  Oid *rawarr;
14857  int numkeys;
14858  int i;
14859 
14860  Assert(con->contype == CONSTR_FOREIGN);
14861  Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14862 
14863  tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14864  if (!HeapTupleIsValid(tup)) /* should not happen */
14865  elog(ERROR, "cache lookup failed for constraint %u", oldId);
14866 
14867  adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14868  Anum_pg_constraint_conpfeqop);
14869  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14870  numkeys = ARR_DIMS(arr)[0];
14871  /* test follows the one in ri_FetchConstraintInfo() */
14872  if (ARR_NDIM(arr) != 1 ||
14873  ARR_HASNULL(arr) ||
14874  ARR_ELEMTYPE(arr) != OIDOID)
14875  elog(ERROR, "conpfeqop is not a 1-D Oid array");
14876  rawarr = (Oid *) ARR_DATA_PTR(arr);
14877 
14878  /* stash a List of the operator Oids in our Constraint node */
14879  for (i = 0; i < numkeys; i++)
14880  con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14881 
14882  ReleaseSysCache(tup);
14883 }

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

14823 {
14824  if (CheckIndexCompatible(oldId,
14825  stmt->accessMethod,
14826  stmt->indexParams,
14827  stmt->excludeOpNames,
14828  stmt->iswithoutoverlaps))
14829  {
14830  Relation irel = index_open(oldId, NoLock);
14831 
14832  /* If it's a partitioned index, there is no storage to share. */
14833  if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14834  {
14835  stmt->oldNumber = irel->rd_locator.relNumber;
14836  stmt->oldCreateSubid = irel->rd_createSubid;
14837  stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14838  }
14839  index_close(irel, NoLock);
14840  }
14841 }
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 10421 of file tablecmds.c.

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

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

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

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

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

20766 {
20767  for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
20768  {
20770  iinfo->ii_IndexAttrNumbers[i] - 1);
20771 
20772  if (!att->attnotnull)
20773  ereport(ERROR,
20774  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
20775  errmsg("invalid primary key definition"),
20776  errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20777  NameStr(att->attname),
20778  RelationGetRelationName(partition)));
20779  }
20780 }

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

Referenced by DropErrorMsgNonExistent(), and DropErrorMsgWrongType().

◆ on_commits